diff --git a/bin/add_config_header.py b/bin/add_config_header.py index 122da47364de41840e02a13f809f70e4538abfc6..7bbb596521f1a8f1406d9393b4c959c9225ef14e 100644 --- a/bin/add_config_header.py +++ b/bin/add_config_header.py @@ -14,7 +14,7 @@ def add_config_header(module, dir): contains_config_header = False with open(file,'r') as f: - file_content = [line.strip() for line in f.readlines()] + file_content = [line.rstrip() for line in f.readlines()] for l,line in enumerate(file_content): if config_header in line: contains_config_header = True diff --git a/dune/common/alignedallocator.hh b/dune/common/alignedallocator.hh index c5972404f0fb3de269d7bab54e6d0b2f116a66f8..64d517ea0c3f5b6ce234a07e865e17126168d11e 100644 --- a/dune/common/alignedallocator.hh +++ b/dune/common/alignedallocator.hh @@ -11,52 +11,52 @@ namespace Dune { -/** -@ingroup Allocators -@brief Allocators which guarantee alignment of the memory - -@tparam T type of the object one wants to allocate -@tparam Alignment explicitly specify the alignment, by default it is std::alignment_of<T>::value -*/ -template<class T, int Alignment = -1> -class AlignedAllocator : public MallocAllocator<T> { - -/* -* Check whether an explicit alignment was -* restricted or fall back to the default alignment of T. -*/ -static constexpr int fixAlignment(int align) -{ -return (Alignment==-1) ? std::alignment_of<T>::value : Alignment; -} - -public: -using pointer = typename MallocAllocator<T>::pointer; -using size_type = typename MallocAllocator<T>::size_type; -template <class U> struct rebind { -typedef AlignedAllocator<U,Alignment> other; -}; - -static constexpr int alignment = fixAlignment(sizeof(void*)); - -//! allocate n objects of type T -pointer allocate(size_type n, const void* hint = 0) -{ - -DUNE_UNUSED_PARAMETER(hint); -if (n > this->max_size()) -throw std::bad_alloc(); - -/* -* Everybody else gets the standard treatment. -*/ -pointer ret = static_cast<pointer>(std::aligned_alloc(alignment, n * sizeof(T))); -if (!ret) -throw std::bad_alloc(); - -return ret; -} -}; + /** + @ingroup Allocators + @brief Allocators which guarantee alignment of the memory + + @tparam T type of the object one wants to allocate + @tparam Alignment explicitly specify the alignment, by default it is std::alignment_of<T>::value + */ + template<class T, int Alignment = -1> + class AlignedAllocator : public MallocAllocator<T> { + + /* + * Check whether an explicit alignment was + * restricted or fall back to the default alignment of T. + */ + static constexpr int fixAlignment(int align) + { + return (Alignment==-1) ? std::alignment_of<T>::value : Alignment; + } + + public: + using pointer = typename MallocAllocator<T>::pointer; + using size_type = typename MallocAllocator<T>::size_type; + template <class U> struct rebind { + typedef AlignedAllocator<U,Alignment> other; + }; + + static constexpr int alignment = fixAlignment(sizeof(void*)); + + //! allocate n objects of type T + pointer allocate(size_type n, const void* hint = 0) + { + + DUNE_UNUSED_PARAMETER(hint); + if (n > this->max_size()) + throw std::bad_alloc(); + + /* + * Everybody else gets the standard treatment. + */ + pointer ret = static_cast<pointer>(std::aligned_alloc(alignment, n * sizeof(T))); + if (!ret) + throw std::bad_alloc(); + + return ret; + } + }; } diff --git a/dune/common/arraylist.hh b/dune/common/arraylist.hh index 308b9433c03972ad0e1542de98bd72a84313194c..526d3fbb30ccbb78d19c8680cd371aa5958fea67 100644 --- a/dune/common/arraylist.hh +++ b/dune/common/arraylist.hh @@ -13,721 +13,721 @@ namespace Dune { -// forward declaration -template<class T, int N, class A> -class ArrayListIterator; - -template<class T, int N, class A> -class ConstArrayListIterator; - -/** -* @file -* \brief Implements a random-access container that can efficiently change size (similar to std::deque) -* -* This file implements the class ArrayList which behaves like -* dynamically growing array together with -* the class ArrayListIterator which is random access iterator as needed -* by the stl for sorting and other algorithms. -* @author Markus Blatt -*/ -/** -* @addtogroup Common -* -* @{ -*/ - -/** -* @brief A dynamically growing random access list. -* -* Internally the data is organised in a list of arrays of fixed size. -* Whenever the capacity of the array list is not sufficient a new -* std::array is allocated. In contrast to -* std::vector this approach prevents data copying. On the outside -* we provide the same interface as the stl random access containers. -* -* While the concept sounds quite similar to std::deque there are slight -* but crucial differences: -* - In contrast to std:deque the actual implementation (a list of arrays) -* is known. While -* for std::deque there are at least two possible implementations -* (dynamic array or using a double linked list. -* - In contrast to std:deque there is not insert which invalidates iterators -* but our push_back method leaves all iterators valid. -* - Additional functionality lets one delete entries before and at an -* iterator while moving the iterator to the next valid position. -*/ -template<class T, int N=100, class A=std::allocator<T> > -class ArrayList -{ -public: -/** -* @brief The member type that is stored. -* -* Has to be assignable and has to have an empty constructor. -*/ -typedef T MemberType; - -/** -* @brief Value type for stl compliance. -*/ -typedef T value_type; - -/** -* @brief The type of a reference to the type we store. -*/ -typedef T& reference; - -/** -* @brief The type of a const reference to the type we store. -*/ -typedef const T& const_reference; - -/** -* @brief The type of a pointer to the type we store. -*/ -typedef T* pointer; - -/** -* @brief The type of a const pointer to the type we store. -*/ -typedef const T* const_pointer; - -enum -{ -/** -* @brief The number of elements in one chunk of the list. -* This has to be at least one. The default is 100. -*/ -chunkSize_ = (N > 0) ? N : 1 -}; - -/** -* @brief A random access iterator. -*/ -typedef ArrayListIterator<MemberType,N,A> iterator; - -/** -* @brief A constant random access iterator. -*/ -typedef ConstArrayListIterator<MemberType,N,A> const_iterator; - -/** -* @brief The size type. -*/ -typedef std::size_t size_type; - -/** -* @brief The difference type. -*/ -typedef std::ptrdiff_t difference_type; - -/** -* @brief Get an iterator that is positioned at the first element. -* @return The iterator. -*/ -iterator begin(); - -/** -* @brief Get a random access iterator that is positioned at the -* first element. -* @return The iterator. -*/ -const_iterator begin() const; - -/** -* @brief Get a random access iterator positioned after the last -* element -*/ -iterator end(); - -/** -* @brief Get a random access iterator positioned after the last -* element -*/ -const_iterator end() const; - -/** -* @brief Append an entry to the list. -* @param entry The new entry. -*/ -inline void push_back(const_reference entry); - -/** -* @brief Get the element at specific position. -* @param i The index of the position. -* @return The element at that position. -*/ -inline reference operator[](size_type i); - -/** -* @brief Get the element at specific position. -* @param i The index of the position. -* @return The element at that position. -*/ -inline const_reference operator[](size_type i) const; - -/** -* @brief Get the number of elements in the list. -* @return The number of elements. -*/ -inline size_type size() const; - -/** -* @brief Purge the list. -* -* If there are empty chunks at the front all nonempty -* chunks will be moved towards the front and the capacity -* increases. -*/ -inline void purge(); - -/** -* @brief Delete all entries from the list. -*/ -inline void clear(); -/** -* @brief Constructs an Array list with one chunk. -*/ -ArrayList(); - -private: - -/** -* @brief The allocators for the smart pointer. -*/ -using SmartPointerAllocator = typename std::allocator_traits<A>::template rebind_alloc< std::shared_ptr< std::array<MemberType,chunkSize_> > >; - -/** -* @brief The allocator for the fixed array. -*/ -using ArrayAllocator = typename std::allocator_traits<A>::template rebind_alloc< std::array<MemberType,chunkSize_> >; - -/** -* @brief The iterator needs access to the private variables. -*/ -friend class ArrayListIterator<T,N,A>; -friend class ConstArrayListIterator<T,N,A>; - -/** @brief the data chunks of our list. */ -std::vector<std::shared_ptr<std::array<MemberType,chunkSize_> >, -SmartPointerAllocator> chunks_; -/** @brief The current data capacity. -* This is the capacity that the list could have theoretically -* with this number of chunks. That is chunks * chunkSize. -* In practice some of the chunks at the beginning might be empty -* (i.e. null pointers in the first start_/chunkSize chunks) -* because of previous calls to eraseToHere. -* start_+size_<=capacity_ holds. -*/ -size_type capacity_; -/** @brief The current number of elements in our data structure. */ -size_type size_; -/** @brief The index of the first entry. */ -size_type start_; -/** -* @brief Get the element at specific position. -* -* Index 0 always refers to the first entry in the list -* whether it is erased or not! -* @param i The index of the position. -* @return The element at that position. -*/ -inline reference elementAt(size_type i); - -/** -* @brief Get the element at specific position. -* -* Index 0 always refers to the first entry in the list -* whether it is erased or not! -* @param i The index of the position. -* @return The element at that position. -*/ -inline const_reference elementAt(size_type i) const; -}; - - -/** -* @brief A random access iterator for the Dune::ArrayList class. -*/ -template<class T, int N, class A> -class ArrayListIterator : public RandomAccessIteratorFacade<ArrayListIterator<T,N,A>, -typename A::value_type, -typename A::value_type &, -typename A::difference_type> -{ - -friend class ArrayList<T,N,A>; -friend class ConstArrayListIterator<T,N,A>; -public: -/** -* @brief The member type. -*/ -typedef typename A::value_type MemberType; - -typedef typename A::difference_type difference_type; - -typedef typename A::size_type size_type; - -using reference = typename A::value_type &; - -using const_reference = typename A::value_type const&; - -enum -{ -/** -* @brief The number of elements in one chunk of the list. -* -* This has to be at least one. The default is 100. -*/ -chunkSize_ = (N > 0) ? N : 1 -}; - - -/** -* @brief Comares two iterators. -* @return True if the iterators are for the same list and -* at the position. -*/ -inline bool equals(const ArrayListIterator<MemberType,N,A>& other) const; - -/** -* @brief Comares two iterators. -* @return True if the iterators are for the same list and -* at the position. -*/ -inline bool equals(const ConstArrayListIterator<MemberType,N,A>& other) const; - -/** -* @brief Increment the iterator. -*/ -inline void increment(); - -/** -* @brief decrement the iterator. -*/ -inline void decrement(); - -/** -* @brief Get the value of the list at an arbitrary position. -* @return The value at that position. -*/ -inline reference elementAt(size_type i) const; - -/** -* @brief Access the element at the current position. -* @return The element at the current position. -*/ -inline reference dereference() const; - -/** -* @brief Erase all entries before the current position -* and the one at the current position. -* -* Afterwards the iterator will be positioned at the next -* unerased entry or the end if the list is empty. -* This does not invalidate any iterators positioned after -* the current position but those positioned at previous ones. -* @return An iterator to the first position after the deleted -* ones or to the end if the list is empty. -*/ -inline void eraseToHere(); - -/** \todo Please doc me! */ -inline size_type position(){return position_;} - -/** \todo Please doc me! */ -inline void advance(difference_type n); - -/** \todo Please doc me! */ -inline difference_type distanceTo(const ArrayListIterator<T,N,A>& other) const; - -/** \todo Please doc me! */ -inline ArrayListIterator<T,N,A>& operator=(const ArrayListIterator<T,N,A>& other); - -//! Standard constructor -inline ArrayListIterator() : position_(0), list_(nullptr) -{} - -private: -/** -* @brief Constructor. -* @param list The list we are an iterator for. -* @param position The initial position of the iterator. -*/ -inline ArrayListIterator(ArrayList<T,N,A>& arrayList, size_type position); - -/** -* @brief The current position. -*/ -size_type position_; -/** -* @brief The list we are an iterator for. -*/ -ArrayList<T,N,A>* list_; -}; - -/** -* @brief A constant random access iterator for the Dune::ArrayList class. -*/ -template<class T, int N, class A> -class ConstArrayListIterator -: public RandomAccessIteratorFacade<ConstArrayListIterator<T,N,A>, -const typename A::value_type, -typename A::value_type const&, -typename A::difference_type> -{ - -friend class ArrayList<T,N,A>; -friend class ArrayListIterator<T,N,A>; - -public: -/** -* @brief The member type. -*/ -typedef typename A::value_type MemberType; - -typedef typename A::difference_type difference_type; - -typedef typename A::size_type size_type; - -using reference = typename A::value_type &; - -using const_reference = typename A::value_type const&; - -enum -{ -/** -* @brief The number of elements in one chunk of the list. -* -* This has to be at least one. The default is 100. -*/ -chunkSize_ = (N > 0) ? N : 1 -}; - -/** -* @brief Comares to iterators. -* @return true if the iterators are for the same list and -* at the position. -*/ -inline bool equals(const ConstArrayListIterator<MemberType,N,A>& other) const; - -/** -* @brief Increment the iterator. -*/ -inline void increment(); - -/** -* @brief decrement the iterator. -*/ -inline void decrement(); - -/** \todo Please doc me! */ -inline void advance(difference_type n); - -/** \todo Please doc me! */ -inline difference_type distanceTo(const ConstArrayListIterator<T,N,A>& other) const; - -/** -* @brief Get the value of the list at an arbitrary position. -* @return The value at that position. -*/ -inline const_reference elementAt(size_type i) const; - -/** -* @brief Access the element at the current position. -* @return The element at the current position. -*/ -inline const_reference dereference() const; - -inline const ConstArrayListIterator<T,N,A>& operator=(const ConstArrayListIterator<T,N,A>& other); - -inline ConstArrayListIterator() : position_(0), list_(nullptr) -{} - -inline ConstArrayListIterator(const ArrayListIterator<T,N,A>& other); - -private: - -/** -* @brief Constructor. -* @param list The list we are an iterator for. -* @param position The initial position of the iterator. -*/ -inline ConstArrayListIterator(const ArrayList<T,N,A>& arrayList, size_type position); - -/** -* @brief The current position. -*/ -size_type position_; -/** -* @brief The list we are an iterator for. -*/ -const ArrayList<T,N,A>* list_; -}; - - -template<class T, int N, class A> -ArrayList<T,N,A>::ArrayList() -: capacity_(0), size_(0), start_(0) -{ -chunks_.reserve(100); -} - -template<class T, int N, class A> -void ArrayList<T,N,A>::clear(){ -capacity_=0; -size_=0; -start_=0; -chunks_.clear(); -} - -template<class T, int N, class A> -size_t ArrayList<T,N,A>::size() const -{ -return size_; -} - -template<class T, int N, class A> -void ArrayList<T,N,A>::push_back(const_reference entry) -{ -size_t index=start_+size_; -if(index==capacity_) -{ -chunks_.push_back(std::make_shared<std::array<MemberType,chunkSize_> >()); -capacity_ += chunkSize_; -} -elementAt(index)=entry; -++size_; -} - -template<class T, int N, class A> -typename ArrayList<T,N,A>::reference ArrayList<T,N,A>::operator[](size_type i) -{ -return elementAt(start_+i); -} - - -template<class T, int N, class A> -typename ArrayList<T,N,A>::const_reference ArrayList<T,N,A>::operator[](size_type i) const -{ -return elementAt(start_+i); -} - -template<class T, int N, class A> -typename ArrayList<T,N,A>::reference ArrayList<T,N,A>::elementAt(size_type i) -{ -return chunks_[i/chunkSize_]->operator[](i%chunkSize_); -} - - -template<class T, int N, class A> -typename ArrayList<T,N,A>::const_reference ArrayList<T,N,A>::elementAt(size_type i) const -{ -return chunks_[i/chunkSize_]->operator[](i%chunkSize_); -} - -template<class T, int N, class A> -ArrayListIterator<T,N,A> ArrayList<T,N,A>::begin() -{ -return ArrayListIterator<T,N,A>(*this, start_); -} - -template<class T, int N, class A> -ConstArrayListIterator<T,N,A> ArrayList<T,N,A>::begin() const -{ -return ConstArrayListIterator<T,N,A>(*this, start_); -} - -template<class T, int N, class A> -ArrayListIterator<T,N,A> ArrayList<T,N,A>::end() -{ -return ArrayListIterator<T,N,A>(*this, start_+size_); -} - -template<class T, int N, class A> -ConstArrayListIterator<T,N,A> ArrayList<T,N,A>::end() const -{ -return ConstArrayListIterator<T,N,A>(*this, start_+size_); -} - -template<class T, int N, class A> -void ArrayList<T,N,A>::purge() -{ -// Distance to copy to the left. -size_t distance = start_/chunkSize_; -if(distance>0) { -// Number of chunks with entries in it; -size_t chunks = ((start_%chunkSize_ + size_)/chunkSize_ ); - -// Copy chunks to the left. -std::copy(chunks_.begin()+distance, -chunks_.begin()+(distance+chunks), chunks_.begin()); - -// Calculate new parameters -start_ = start_ % chunkSize_; -//capacity += distance * chunkSize_; -} -} - -template<class T, int N, class A> -void ArrayListIterator<T,N,A>::advance(difference_type i) -{ -position_+=i; -} - -template<class T, int N, class A> -void ConstArrayListIterator<T,N,A>::advance(difference_type i) -{ -position_+=i; -} - - -template<class T, int N, class A> -bool ArrayListIterator<T,N,A>::equals(const ArrayListIterator<MemberType,N,A>& other) const -{ -// Makes only sense if we reference a common list -assert(list_==(other.list_)); -return position_==other.position_ ; -} - - -template<class T, int N, class A> -bool ArrayListIterator<T,N,A>::equals(const ConstArrayListIterator<MemberType,N,A>& other) const -{ -// Makes only sense if we reference a common list -assert(list_==(other.list_)); -return position_==other.position_ ; -} - - -template<class T, int N, class A> -bool ConstArrayListIterator<T,N,A>::equals(const ConstArrayListIterator<MemberType,N,A>& other) const -{ -// Makes only sense if we reference a common list -assert(list_==(other.list_)); -return position_==other.position_ ; -} - -template<class T, int N, class A> -void ArrayListIterator<T,N,A>::increment() -{ -++position_; -} - -template<class T, int N, class A> -void ConstArrayListIterator<T,N,A>::increment() -{ -++position_; -} - -template<class T, int N, class A> -void ArrayListIterator<T,N,A>::decrement() -{ ---position_; -} - -template<class T, int N, class A> -void ConstArrayListIterator<T,N,A>::decrement() -{ ---position_; -} - -template<class T, int N, class A> -typename ArrayListIterator<T,N,A>::reference ArrayListIterator<T,N,A>::elementAt(size_type i) const -{ -return list_->elementAt(i+position_); -} - -template<class T, int N, class A> -typename ConstArrayListIterator<T,N,A>::const_reference ConstArrayListIterator<T,N,A>::elementAt(size_type i) const -{ -return list_->elementAt(i+position_); -} - -template<class T, int N, class A> -typename ArrayListIterator<T,N,A>::reference ArrayListIterator<T,N,A>::dereference() const -{ -return list_->elementAt(position_); -} - -template<class T, int N, class A> -typename ConstArrayListIterator<T,N,A>::const_reference ConstArrayListIterator<T,N,A>::dereference() const -{ -return list_->elementAt(position_); -} - -template<class T, int N, class A> -typename ArrayListIterator<T,N,A>::difference_type ArrayListIterator<T,N,A>::distanceTo(const ArrayListIterator<T,N,A>& other) const -{ -// Makes only sense if we reference a common list -assert(list_==(other.list_)); -return other.position_ - position_; -} - -template<class T, int N, class A> -typename ConstArrayListIterator<T,N,A>::difference_type ConstArrayListIterator<T,N,A>::distanceTo(const ConstArrayListIterator<T,N,A>& other) const -{ -// Makes only sense if we reference a common list -assert(list_==(other.list_)); -return other.position_ - position_; -} - -template<class T, int N, class A> -ArrayListIterator<T,N,A>& ArrayListIterator<T,N,A>::operator=(const ArrayListIterator<T,N,A>& other) -{ -position_=other.position_; -list_=other.list_; -return *this; -} - -template<class T, int N, class A> -const ConstArrayListIterator<T,N,A>& ConstArrayListIterator<T,N,A>::operator=(const ConstArrayListIterator<T,N,A>& other) -{ -position_=other.position_; -list_=other.list_; -return *this; -} - -template<class T, int N, class A> -void ArrayListIterator<T,N,A>::eraseToHere() -{ -list_->size_ -= ++position_ - list_->start_; -// chunk number of the new position. -size_t posChunkStart = position_ / chunkSize_; -// number of chunks to deallocate -size_t chunks = (position_ - list_->start_ + list_->start_ % chunkSize_) -/ chunkSize_; -list_->start_ = position_; - -// Deallocate memory not needed any more. -for(size_t chunk=0; chunk<chunks; chunk++) { ---posChunkStart; -list_->chunks_[posChunkStart].reset(); -} - -// Capacity stays the same as the chunks before us -// are still there. They null pointers. -assert(list_->start_+list_->size_<=list_->capacity_); -} - -template<class T, int N, class A> -ArrayListIterator<T,N,A>::ArrayListIterator(ArrayList<T,N,A>& arrayList, size_type position) -: position_(position), list_(&arrayList) -{} - - -template<class T, int N, class A> -ConstArrayListIterator<T,N,A>::ConstArrayListIterator(const ArrayList<T,N,A>& arrayList, -size_type position) -: position_(position), list_(&arrayList) -{} - -template<class T, int N, class A> -ConstArrayListIterator<T,N,A>::ConstArrayListIterator(const ArrayListIterator<T,N,A>& other) -: position_(other.position_), list_(other.list_) -{} - - -/** @} */ + // forward declaration + template<class T, int N, class A> + class ArrayListIterator; + + template<class T, int N, class A> + class ConstArrayListIterator; + + /** + * @file + * \brief Implements a random-access container that can efficiently change size (similar to std::deque) + * + * This file implements the class ArrayList which behaves like + * dynamically growing array together with + * the class ArrayListIterator which is random access iterator as needed + * by the stl for sorting and other algorithms. + * @author Markus Blatt + */ + /** + * @addtogroup Common + * + * @{ + */ + + /** + * @brief A dynamically growing random access list. + * + * Internally the data is organised in a list of arrays of fixed size. + * Whenever the capacity of the array list is not sufficient a new + * std::array is allocated. In contrast to + * std::vector this approach prevents data copying. On the outside + * we provide the same interface as the stl random access containers. + * + * While the concept sounds quite similar to std::deque there are slight + * but crucial differences: + * - In contrast to std:deque the actual implementation (a list of arrays) + * is known. While + * for std::deque there are at least two possible implementations + * (dynamic array or using a double linked list. + * - In contrast to std:deque there is not insert which invalidates iterators + * but our push_back method leaves all iterators valid. + * - Additional functionality lets one delete entries before and at an + * iterator while moving the iterator to the next valid position. + */ + template<class T, int N=100, class A=std::allocator<T> > + class ArrayList + { + public: + /** + * @brief The member type that is stored. + * + * Has to be assignable and has to have an empty constructor. + */ + typedef T MemberType; + + /** + * @brief Value type for stl compliance. + */ + typedef T value_type; + + /** + * @brief The type of a reference to the type we store. + */ + typedef T& reference; + + /** + * @brief The type of a const reference to the type we store. + */ + typedef const T& const_reference; + + /** + * @brief The type of a pointer to the type we store. + */ + typedef T* pointer; + + /** + * @brief The type of a const pointer to the type we store. + */ + typedef const T* const_pointer; + + enum + { + /** + * @brief The number of elements in one chunk of the list. + * This has to be at least one. The default is 100. + */ + chunkSize_ = (N > 0) ? N : 1 + }; + + /** + * @brief A random access iterator. + */ + typedef ArrayListIterator<MemberType,N,A> iterator; + + /** + * @brief A constant random access iterator. + */ + typedef ConstArrayListIterator<MemberType,N,A> const_iterator; + + /** + * @brief The size type. + */ + typedef std::size_t size_type; + + /** + * @brief The difference type. + */ + typedef std::ptrdiff_t difference_type; + + /** + * @brief Get an iterator that is positioned at the first element. + * @return The iterator. + */ + iterator begin(); + + /** + * @brief Get a random access iterator that is positioned at the + * first element. + * @return The iterator. + */ + const_iterator begin() const; + + /** + * @brief Get a random access iterator positioned after the last + * element + */ + iterator end(); + + /** + * @brief Get a random access iterator positioned after the last + * element + */ + const_iterator end() const; + + /** + * @brief Append an entry to the list. + * @param entry The new entry. + */ + inline void push_back(const_reference entry); + + /** + * @brief Get the element at specific position. + * @param i The index of the position. + * @return The element at that position. + */ + inline reference operator[](size_type i); + + /** + * @brief Get the element at specific position. + * @param i The index of the position. + * @return The element at that position. + */ + inline const_reference operator[](size_type i) const; + + /** + * @brief Get the number of elements in the list. + * @return The number of elements. + */ + inline size_type size() const; + + /** + * @brief Purge the list. + * + * If there are empty chunks at the front all nonempty + * chunks will be moved towards the front and the capacity + * increases. + */ + inline void purge(); + + /** + * @brief Delete all entries from the list. + */ + inline void clear(); + /** + * @brief Constructs an Array list with one chunk. + */ + ArrayList(); + + private: + + /** + * @brief The allocators for the smart pointer. + */ + using SmartPointerAllocator = typename std::allocator_traits<A>::template rebind_alloc< std::shared_ptr< std::array<MemberType,chunkSize_> > >; + + /** + * @brief The allocator for the fixed array. + */ + using ArrayAllocator = typename std::allocator_traits<A>::template rebind_alloc< std::array<MemberType,chunkSize_> >; + + /** + * @brief The iterator needs access to the private variables. + */ + friend class ArrayListIterator<T,N,A>; + friend class ConstArrayListIterator<T,N,A>; + + /** @brief the data chunks of our list. */ + std::vector<std::shared_ptr<std::array<MemberType,chunkSize_> >, + SmartPointerAllocator> chunks_; + /** @brief The current data capacity. + * This is the capacity that the list could have theoretically + * with this number of chunks. That is chunks * chunkSize. + * In practice some of the chunks at the beginning might be empty + * (i.e. null pointers in the first start_/chunkSize chunks) + * because of previous calls to eraseToHere. + * start_+size_<=capacity_ holds. + */ + size_type capacity_; + /** @brief The current number of elements in our data structure. */ + size_type size_; + /** @brief The index of the first entry. */ + size_type start_; + /** + * @brief Get the element at specific position. + * + * Index 0 always refers to the first entry in the list + * whether it is erased or not! + * @param i The index of the position. + * @return The element at that position. + */ + inline reference elementAt(size_type i); + + /** + * @brief Get the element at specific position. + * + * Index 0 always refers to the first entry in the list + * whether it is erased or not! + * @param i The index of the position. + * @return The element at that position. + */ + inline const_reference elementAt(size_type i) const; + }; + + + /** + * @brief A random access iterator for the Dune::ArrayList class. + */ + template<class T, int N, class A> + class ArrayListIterator : public RandomAccessIteratorFacade<ArrayListIterator<T,N,A>, + typename A::value_type, + typename A::value_type &, + typename A::difference_type> + { + + friend class ArrayList<T,N,A>; + friend class ConstArrayListIterator<T,N,A>; + public: + /** + * @brief The member type. + */ + typedef typename A::value_type MemberType; + + typedef typename A::difference_type difference_type; + + typedef typename A::size_type size_type; + + using reference = typename A::value_type &; + + using const_reference = typename A::value_type const&; + + enum + { + /** + * @brief The number of elements in one chunk of the list. + * + * This has to be at least one. The default is 100. + */ + chunkSize_ = (N > 0) ? N : 1 + }; + + + /** + * @brief Comares two iterators. + * @return True if the iterators are for the same list and + * at the position. + */ + inline bool equals(const ArrayListIterator<MemberType,N,A>& other) const; + + /** + * @brief Comares two iterators. + * @return True if the iterators are for the same list and + * at the position. + */ + inline bool equals(const ConstArrayListIterator<MemberType,N,A>& other) const; + + /** + * @brief Increment the iterator. + */ + inline void increment(); + + /** + * @brief decrement the iterator. + */ + inline void decrement(); + + /** + * @brief Get the value of the list at an arbitrary position. + * @return The value at that position. + */ + inline reference elementAt(size_type i) const; + + /** + * @brief Access the element at the current position. + * @return The element at the current position. + */ + inline reference dereference() const; + + /** + * @brief Erase all entries before the current position + * and the one at the current position. + * + * Afterwards the iterator will be positioned at the next + * unerased entry or the end if the list is empty. + * This does not invalidate any iterators positioned after + * the current position but those positioned at previous ones. + * @return An iterator to the first position after the deleted + * ones or to the end if the list is empty. + */ + inline void eraseToHere(); + + /** \todo Please doc me! */ + inline size_type position(){return position_;} + + /** \todo Please doc me! */ + inline void advance(difference_type n); + + /** \todo Please doc me! */ + inline difference_type distanceTo(const ArrayListIterator<T,N,A>& other) const; + + /** \todo Please doc me! */ + inline ArrayListIterator<T,N,A>& operator=(const ArrayListIterator<T,N,A>& other); + + //! Standard constructor + inline ArrayListIterator() : position_(0), list_(nullptr) + {} + + private: + /** + * @brief Constructor. + * @param list The list we are an iterator for. + * @param position The initial position of the iterator. + */ + inline ArrayListIterator(ArrayList<T,N,A>& arrayList, size_type position); + + /** + * @brief The current position. + */ + size_type position_; + /** + * @brief The list we are an iterator for. + */ + ArrayList<T,N,A>* list_; + }; + + /** + * @brief A constant random access iterator for the Dune::ArrayList class. + */ + template<class T, int N, class A> + class ConstArrayListIterator + : public RandomAccessIteratorFacade<ConstArrayListIterator<T,N,A>, + const typename A::value_type, + typename A::value_type const&, + typename A::difference_type> + { + + friend class ArrayList<T,N,A>; + friend class ArrayListIterator<T,N,A>; + + public: + /** + * @brief The member type. + */ + typedef typename A::value_type MemberType; + + typedef typename A::difference_type difference_type; + + typedef typename A::size_type size_type; + + using reference = typename A::value_type &; + + using const_reference = typename A::value_type const&; + + enum + { + /** + * @brief The number of elements in one chunk of the list. + * + * This has to be at least one. The default is 100. + */ + chunkSize_ = (N > 0) ? N : 1 + }; + + /** + * @brief Comares to iterators. + * @return true if the iterators are for the same list and + * at the position. + */ + inline bool equals(const ConstArrayListIterator<MemberType,N,A>& other) const; + + /** + * @brief Increment the iterator. + */ + inline void increment(); + + /** + * @brief decrement the iterator. + */ + inline void decrement(); + + /** \todo Please doc me! */ + inline void advance(difference_type n); + + /** \todo Please doc me! */ + inline difference_type distanceTo(const ConstArrayListIterator<T,N,A>& other) const; + + /** + * @brief Get the value of the list at an arbitrary position. + * @return The value at that position. + */ + inline const_reference elementAt(size_type i) const; + + /** + * @brief Access the element at the current position. + * @return The element at the current position. + */ + inline const_reference dereference() const; + + inline const ConstArrayListIterator<T,N,A>& operator=(const ConstArrayListIterator<T,N,A>& other); + + inline ConstArrayListIterator() : position_(0), list_(nullptr) + {} + + inline ConstArrayListIterator(const ArrayListIterator<T,N,A>& other); + + private: + + /** + * @brief Constructor. + * @param list The list we are an iterator for. + * @param position The initial position of the iterator. + */ + inline ConstArrayListIterator(const ArrayList<T,N,A>& arrayList, size_type position); + + /** + * @brief The current position. + */ + size_type position_; + /** + * @brief The list we are an iterator for. + */ + const ArrayList<T,N,A>* list_; + }; + + + template<class T, int N, class A> + ArrayList<T,N,A>::ArrayList() + : capacity_(0), size_(0), start_(0) + { + chunks_.reserve(100); + } + + template<class T, int N, class A> + void ArrayList<T,N,A>::clear(){ + capacity_=0; + size_=0; + start_=0; + chunks_.clear(); + } + + template<class T, int N, class A> + size_t ArrayList<T,N,A>::size() const + { + return size_; + } + + template<class T, int N, class A> + void ArrayList<T,N,A>::push_back(const_reference entry) + { + size_t index=start_+size_; + if(index==capacity_) + { + chunks_.push_back(std::make_shared<std::array<MemberType,chunkSize_> >()); + capacity_ += chunkSize_; + } + elementAt(index)=entry; + ++size_; + } + + template<class T, int N, class A> + typename ArrayList<T,N,A>::reference ArrayList<T,N,A>::operator[](size_type i) + { + return elementAt(start_+i); + } + + + template<class T, int N, class A> + typename ArrayList<T,N,A>::const_reference ArrayList<T,N,A>::operator[](size_type i) const + { + return elementAt(start_+i); + } + + template<class T, int N, class A> + typename ArrayList<T,N,A>::reference ArrayList<T,N,A>::elementAt(size_type i) + { + return chunks_[i/chunkSize_]->operator[](i%chunkSize_); + } + + + template<class T, int N, class A> + typename ArrayList<T,N,A>::const_reference ArrayList<T,N,A>::elementAt(size_type i) const + { + return chunks_[i/chunkSize_]->operator[](i%chunkSize_); + } + + template<class T, int N, class A> + ArrayListIterator<T,N,A> ArrayList<T,N,A>::begin() + { + return ArrayListIterator<T,N,A>(*this, start_); + } + + template<class T, int N, class A> + ConstArrayListIterator<T,N,A> ArrayList<T,N,A>::begin() const + { + return ConstArrayListIterator<T,N,A>(*this, start_); + } + + template<class T, int N, class A> + ArrayListIterator<T,N,A> ArrayList<T,N,A>::end() + { + return ArrayListIterator<T,N,A>(*this, start_+size_); + } + + template<class T, int N, class A> + ConstArrayListIterator<T,N,A> ArrayList<T,N,A>::end() const + { + return ConstArrayListIterator<T,N,A>(*this, start_+size_); + } + + template<class T, int N, class A> + void ArrayList<T,N,A>::purge() + { + // Distance to copy to the left. + size_t distance = start_/chunkSize_; + if(distance>0) { + // Number of chunks with entries in it; + size_t chunks = ((start_%chunkSize_ + size_)/chunkSize_ ); + + // Copy chunks to the left. + std::copy(chunks_.begin()+distance, + chunks_.begin()+(distance+chunks), chunks_.begin()); + + // Calculate new parameters + start_ = start_ % chunkSize_; + //capacity += distance * chunkSize_; + } + } + + template<class T, int N, class A> + void ArrayListIterator<T,N,A>::advance(difference_type i) + { + position_+=i; + } + + template<class T, int N, class A> + void ConstArrayListIterator<T,N,A>::advance(difference_type i) + { + position_+=i; + } + + + template<class T, int N, class A> + bool ArrayListIterator<T,N,A>::equals(const ArrayListIterator<MemberType,N,A>& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return position_==other.position_ ; + } + + + template<class T, int N, class A> + bool ArrayListIterator<T,N,A>::equals(const ConstArrayListIterator<MemberType,N,A>& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return position_==other.position_ ; + } + + + template<class T, int N, class A> + bool ConstArrayListIterator<T,N,A>::equals(const ConstArrayListIterator<MemberType,N,A>& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return position_==other.position_ ; + } + + template<class T, int N, class A> + void ArrayListIterator<T,N,A>::increment() + { + ++position_; + } + + template<class T, int N, class A> + void ConstArrayListIterator<T,N,A>::increment() + { + ++position_; + } + + template<class T, int N, class A> + void ArrayListIterator<T,N,A>::decrement() + { + --position_; + } + + template<class T, int N, class A> + void ConstArrayListIterator<T,N,A>::decrement() + { + --position_; + } + + template<class T, int N, class A> + typename ArrayListIterator<T,N,A>::reference ArrayListIterator<T,N,A>::elementAt(size_type i) const + { + return list_->elementAt(i+position_); + } + + template<class T, int N, class A> + typename ConstArrayListIterator<T,N,A>::const_reference ConstArrayListIterator<T,N,A>::elementAt(size_type i) const + { + return list_->elementAt(i+position_); + } + + template<class T, int N, class A> + typename ArrayListIterator<T,N,A>::reference ArrayListIterator<T,N,A>::dereference() const + { + return list_->elementAt(position_); + } + + template<class T, int N, class A> + typename ConstArrayListIterator<T,N,A>::const_reference ConstArrayListIterator<T,N,A>::dereference() const + { + return list_->elementAt(position_); + } + + template<class T, int N, class A> + typename ArrayListIterator<T,N,A>::difference_type ArrayListIterator<T,N,A>::distanceTo(const ArrayListIterator<T,N,A>& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return other.position_ - position_; + } + + template<class T, int N, class A> + typename ConstArrayListIterator<T,N,A>::difference_type ConstArrayListIterator<T,N,A>::distanceTo(const ConstArrayListIterator<T,N,A>& other) const + { + // Makes only sense if we reference a common list + assert(list_==(other.list_)); + return other.position_ - position_; + } + + template<class T, int N, class A> + ArrayListIterator<T,N,A>& ArrayListIterator<T,N,A>::operator=(const ArrayListIterator<T,N,A>& other) + { + position_=other.position_; + list_=other.list_; + return *this; + } + + template<class T, int N, class A> + const ConstArrayListIterator<T,N,A>& ConstArrayListIterator<T,N,A>::operator=(const ConstArrayListIterator<T,N,A>& other) + { + position_=other.position_; + list_=other.list_; + return *this; + } + + template<class T, int N, class A> + void ArrayListIterator<T,N,A>::eraseToHere() + { + list_->size_ -= ++position_ - list_->start_; + // chunk number of the new position. + size_t posChunkStart = position_ / chunkSize_; + // number of chunks to deallocate + size_t chunks = (position_ - list_->start_ + list_->start_ % chunkSize_) + / chunkSize_; + list_->start_ = position_; + + // Deallocate memory not needed any more. + for(size_t chunk=0; chunk<chunks; chunk++) { + --posChunkStart; + list_->chunks_[posChunkStart].reset(); + } + + // Capacity stays the same as the chunks before us + // are still there. They null pointers. + assert(list_->start_+list_->size_<=list_->capacity_); + } + + template<class T, int N, class A> + ArrayListIterator<T,N,A>::ArrayListIterator(ArrayList<T,N,A>& arrayList, size_type position) + : position_(position), list_(&arrayList) + {} + + + template<class T, int N, class A> + ConstArrayListIterator<T,N,A>::ConstArrayListIterator(const ArrayList<T,N,A>& arrayList, + size_type position) + : position_(position), list_(&arrayList) + {} + + template<class T, int N, class A> + ConstArrayListIterator<T,N,A>::ConstArrayListIterator(const ArrayListIterator<T,N,A>& other) + : position_(other.position_), list_(other.list_) + {} + + + /** @} */ } #endif diff --git a/dune/common/assertandreturn.hh b/dune/common/assertandreturn.hh index 397c3301933891536e928ad52460268347d3186b..478d8ad4c35c53f43f44dd0aa70baca7aa9d4feb 100644 --- a/dune/common/assertandreturn.hh +++ b/dune/common/assertandreturn.hh @@ -8,17 +8,17 @@ //! Asserts a condition and return on success in constexpr context. /** -* The macro DUNE_ASSERT_AND_RETURN can be used as expression in the return -* statement of a constexpr function to have assert() and constexpr at the -* same time. It first uses assert for the condition given by the first argument -* and then returns the value of the second argument. -* -* \ingroup CxxUtilities -*/ + * The macro DUNE_ASSERT_AND_RETURN can be used as expression in the return + * statement of a constexpr function to have assert() and constexpr at the + * same time. It first uses assert for the condition given by the first argument + * and then returns the value of the second argument. + * + * \ingroup CxxUtilities + */ #ifdef NDEBUG -#define DUNE_ASSERT_AND_RETURN(C,X) X + #define DUNE_ASSERT_AND_RETURN(C,X) X #else -#define DUNE_ASSERT_AND_RETURN(C,X) (!(C) ? throw [&](){assert(!#C);return 0;}() : 0), X + #define DUNE_ASSERT_AND_RETURN(C,X) (!(C) ? throw [&](){assert(!#C);return 0;}() : 0), X #endif diff --git a/dune/common/bartonnackmanifcheck.hh b/dune/common/bartonnackmanifcheck.hh index 3c835680992dc0c9193220ce6158f2d5cf996f29..3a60d3b5de528a9f31730eac32096e481444f0b0 100644 --- a/dune/common/bartonnackmanifcheck.hh +++ b/dune/common/bartonnackmanifcheck.hh @@ -1,23 +1,23 @@ // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: /** @file -@author Robert Kloefkorn -@brief Provides check for implementation of interface methods when using -static polymorphism, i.e. the Barton-Nackman trick. This is purely for -debugging purposes. To check the correct implementation of interface methods -(and pick up possible infinite loops) NDEBUG must be undefined and -DUNE_INTERFACECHECK has to be defined. + @author Robert Kloefkorn + @brief Provides check for implementation of interface methods when using + static polymorphism, i.e. the Barton-Nackman trick. This is purely for + debugging purposes. To check the correct implementation of interface methods + (and pick up possible infinite loops) NDEBUG must be undefined and + DUNE_INTERFACECHECK has to be defined. -Use by invoking CHECK_INTERFACE_IMPLEMENTATION(asImp().methodToCheck()) -and for -template methods double (CHECK_INTERFACE_IMPLEMENTATION((asImp().template methodToCheck<param> ())). -If either NDEBUG is defined or -DUNE_INTERFACECHECK is undefined the CHECK_INTERFACE_IMPLEMENTATION macro is empty. + Use by invoking CHECK_INTERFACE_IMPLEMENTATION(asImp().methodToCheck()) + and for + template methods double (CHECK_INTERFACE_IMPLEMENTATION((asImp().template methodToCheck<param> ())). + If either NDEBUG is defined or + DUNE_INTERFACECHECK is undefined the CHECK_INTERFACE_IMPLEMENTATION macro is empty. -Note: adding the interface check to a method will cause the implementation of the -method to be called twice, so before use make sure -that this will not cause problems e.g. if internal counters are updated. -**/ + Note: adding the interface check to a method will cause the implementation of the + method to be called twice, so before use make sure + that this will not cause problems e.g. if internal counters are updated. + **/ //- Dune includes #include <dune/common/exceptions.hh> @@ -33,32 +33,32 @@ that this will not cause problems e.g. if internal counters are updated. #define CHECK_INTERFACE_IMPLEMENTATION(dummy) #else #define CHECK_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) \ -{\ -static bool call = false; \ -if( call == true ) \ -DUNE_THROW(NotImplemented,"Interface method not implemented!");\ -call = true; \ -try { \ -(__interface_method_to_call__); \ -call = false; \ -} \ -catch ( ... ) \ -{ \ -call = false; \ -throw; \ -} \ -} + {\ + static bool call = false; \ + if( call == true ) \ + DUNE_THROW(NotImplemented,"Interface method not implemented!");\ + call = true; \ + try { \ + (__interface_method_to_call__); \ + call = false; \ + } \ + catch ( ... ) \ + { \ + call = false; \ + throw; \ + } \ + } #endif /** The macro CHECK_AND_CALL_INTERFACE_IMPLEMENTATION throws an exception, -if the interface method ist not implemented and just calls the method -otherwise. If NDEBUG is defined no -checking is done and the method is just called. -*/ + if the interface method ist not implemented and just calls the method + otherwise. If NDEBUG is defined no + checking is done and the method is just called. + */ #if defined NDEBUG || !defined DUNE_INTERFACECHECK #define CHECK_AND_CALL_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) \ -(__interface_method_to_call__) + (__interface_method_to_call__) #else #define CHECK_AND_CALL_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) \ -CHECK_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) + CHECK_INTERFACE_IMPLEMENTATION(__interface_method_to_call__) #endif diff --git a/dune/common/bigunsignedint.hh b/dune/common/bigunsignedint.hh index 910775b45fe47208edca1da452cea14d9c72c68d..c8371453caf3dae7ca5361153c0ce03647dfa941 100644 --- a/dune/common/bigunsignedint.hh +++ b/dune/common/bigunsignedint.hh @@ -15,675 +15,675 @@ #include <dune/common/hash.hh> /** -* @file -* @brief Portable very large unsigned integers -* @author Peter Bastian -*/ + * @file + * @brief Portable very large unsigned integers + * @author Peter Bastian + */ namespace Dune { #if HAVE_MPI -template<class K> -struct MPITraits; + template<class K> + struct MPITraits; #endif -/** @addtogroup Numbers -* -* @{ -*/ - -namespace Impl { - -// numeric_limits_helper provides std::numeric_limits access to the internals -// of bigunsignedint. Previously, the correct specialization of std::numeric_limits -// was a friend of bigunsignedint, but that creates problems on recent versions -// of clang with the alternative libc++ library, because that library declares the -// base template of std::numeric_limits as a class and clang subsequently complains -// if the friend declaration uses 'struct'. Unfortunately, libstdc++ uses a struct, -// making it impossible to keep clang happy for both standard libraries. -// So we move the access helper functionality into a custom struct and simply let -// the numeric_limits specialization inherit from the helper. - -template<typename T> -struct numeric_limits_helper -{ - -protected: + /** @addtogroup Numbers + * + * @{ + */ -static std::uint16_t& digit(T& big_unsigned_int, std::size_t i) -{ -return big_unsigned_int.digit[i]; -} + namespace Impl { -}; + // numeric_limits_helper provides std::numeric_limits access to the internals + // of bigunsignedint. Previously, the correct specialization of std::numeric_limits + // was a friend of bigunsignedint, but that creates problems on recent versions + // of clang with the alternative libc++ library, because that library declares the + // base template of std::numeric_limits as a class and clang subsequently complains + // if the friend declaration uses 'struct'. Unfortunately, libstdc++ uses a struct, + // making it impossible to keep clang happy for both standard libraries. + // So we move the access helper functionality into a custom struct and simply let + // the numeric_limits specialization inherit from the helper. -} - -/** -* @brief Portable very large unsigned integers -* -* Implements (arbitrarily) large unsigned integers to be used as global -* ids in some grid managers. Size is a template parameter. -* -* \tparam k Number of bits of the integer type -*/ + template<typename T> + struct numeric_limits_helper + { -template<int k> -class bigunsignedint { -public: + protected: -// unsigned short is 16 bits wide, n is the number of digits needed -enum { bits=std::numeric_limits<std::uint16_t>::digits, n=k/bits+(k%bits!=0), -hexdigits=4, bitmask=0xFFFF, compbitmask=0xFFFF0000, -overflowmask=0x1 }; + static std::uint16_t& digit(T& big_unsigned_int, std::size_t i) + { + return big_unsigned_int.digit[i]; + } -//! Construct uninitialized -bigunsignedint (); + }; -//! Construct from signed int -template<typename Signed> -bigunsignedint (Signed x, typename std::enable_if<std::is_integral<Signed>::value && std::is_signed<Signed>::value>::type* = 0); + } -//! Construct from unsigned int -bigunsignedint (std::uintmax_t x); + /** + * @brief Portable very large unsigned integers + * + * Implements (arbitrarily) large unsigned integers to be used as global + * ids in some grid managers. Size is a template parameter. + * + * \tparam k Number of bits of the integer type + */ -//! Print number in hex notation -void print (std::ostream& s) const ; + template<int k> + class bigunsignedint { + public: -//! add -bigunsignedint<k> operator+ (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator+= (const bigunsignedint<k>& x); + // unsigned short is 16 bits wide, n is the number of digits needed + enum { bits=std::numeric_limits<std::uint16_t>::digits, n=k/bits+(k%bits!=0), + hexdigits=4, bitmask=0xFFFF, compbitmask=0xFFFF0000, + overflowmask=0x1 }; -//! subtract -bigunsignedint<k> operator- (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator-= (const bigunsignedint<k>& x); + //! Construct uninitialized + bigunsignedint (); -//! multiply -bigunsignedint<k> operator* (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator*= (const bigunsignedint<k>& x); + //! Construct from signed int + template<typename Signed> + bigunsignedint (Signed x, typename std::enable_if<std::is_integral<Signed>::value && std::is_signed<Signed>::value>::type* = 0); -//! prefix increment -bigunsignedint<k>& operator++ (); + //! Construct from unsigned int + bigunsignedint (std::uintmax_t x); -//! divide -//! \warning This function is very slow and its usage should be -//! prevented if possible -bigunsignedint<k> operator/ (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator/= (const bigunsignedint<k>& x); + //! Print number in hex notation + void print (std::ostream& s) const ; -//! modulo -//! \warning This function is very slow and its usage should be -//! prevented if possible -bigunsignedint<k> operator% (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator%= (const bigunsignedint<k>& x); + //! add + bigunsignedint<k> operator+ (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator+= (const bigunsignedint<k>& x); -//! bitwise and -bigunsignedint<k> operator& (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator&= (const bigunsignedint<k>& x); + //! subtract + bigunsignedint<k> operator- (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator-= (const bigunsignedint<k>& x); -//! bitwise exor -bigunsignedint<k> operator^ (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator^= (const bigunsignedint<k>& x); + //! multiply + bigunsignedint<k> operator* (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator*= (const bigunsignedint<k>& x); -//! bitwise or -bigunsignedint<k> operator| (const bigunsignedint<k>& x) const; -bigunsignedint<k>& operator|= (const bigunsignedint<k>& x); + //! prefix increment + bigunsignedint<k>& operator++ (); -//! bitwise complement -bigunsignedint<k> operator~ () const; + //! divide + //! \warning This function is very slow and its usage should be + //! prevented if possible + bigunsignedint<k> operator/ (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator/= (const bigunsignedint<k>& x); + //! modulo + //! \warning This function is very slow and its usage should be + //! prevented if possible + bigunsignedint<k> operator% (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator%= (const bigunsignedint<k>& x); -//! left shift -bigunsignedint<k> operator<< (int i) const; + //! bitwise and + bigunsignedint<k> operator& (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator&= (const bigunsignedint<k>& x); -//! right shift -bigunsignedint<k> operator>> (int i) const; + //! bitwise exor + bigunsignedint<k> operator^ (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator^= (const bigunsignedint<k>& x); + //! bitwise or + bigunsignedint<k> operator| (const bigunsignedint<k>& x) const; + bigunsignedint<k>& operator|= (const bigunsignedint<k>& x); -//! less than -bool operator< (const bigunsignedint<k>& x) const; + //! bitwise complement + bigunsignedint<k> operator~ () const; -//! less than or equal -bool operator<= (const bigunsignedint<k>& x) const; -//! greater than -bool operator> (const bigunsignedint<k>& x) const; + //! left shift + bigunsignedint<k> operator<< (int i) const; -//! greater or equal -bool operator>= (const bigunsignedint<k>& x) const; + //! right shift + bigunsignedint<k> operator>> (int i) const; -//! equal -bool operator== (const bigunsignedint<k>& x) const; -//! not equal -bool operator!= (const bigunsignedint<k>& x) const; + //! less than + bool operator< (const bigunsignedint<k>& x) const; + //! less than or equal + bool operator<= (const bigunsignedint<k>& x) const; -//! export to other types -// operator unsigned int () const; -std::uint_least32_t touint() const; -/** -* @brief Convert to a double. -* -* @warning Subject to rounding errors! -*/ -double todouble() const; - -friend class bigunsignedint<k/2>; -friend struct Impl::numeric_limits_helper< bigunsignedint<k> >; - -inline friend std::size_t hash_value(const bigunsignedint& arg) -{ -return hash_range(arg.digit,arg.digit + arg.n); -} - -private: -std::uint16_t digit[n]; -#if HAVE_MPI -friend struct MPITraits<bigunsignedint<k> >; -#endif -inline void assign(std::uintmax_t x); + //! greater than + bool operator> (const bigunsignedint<k>& x) const; + //! greater or equal + bool operator>= (const bigunsignedint<k>& x) const; -} ; + //! equal + bool operator== (const bigunsignedint<k>& x) const; -// Constructors -template<int k> -bigunsignedint<k>::bigunsignedint () -{ -assign(0u); -} + //! not equal + bool operator!= (const bigunsignedint<k>& x) const; -template<int k> -template<typename Signed> -bigunsignedint<k>::bigunsignedint (Signed y, typename std::enable_if<std::is_integral<Signed>::value && std::is_signed<Signed>::value>::type*) -{ -if (y < 0) -DUNE_THROW(Dune::Exception, "Trying to construct a Dune::bigunsignedint from a negative integer: " << y); -assign(y); -} -template<int k> -bigunsignedint<k>::bigunsignedint (std::uintmax_t x) -{ -assign(x); -} -template<int k> -void bigunsignedint<k>::assign(std::uintmax_t x) -{ -static const int no=std::min(static_cast<int>(n), -static_cast<int>(std::numeric_limits<std::uintmax_t>::digits/bits)); + //! export to other types + // operator unsigned int () const; + std::uint_least32_t touint() const; + /** + * @brief Convert to a double. + * + * @warning Subject to rounding errors! + */ + double todouble() const; -for(int i=0; i<no; ++i) { -digit[i] = (x&bitmask); -x=x>>bits; -} -for (unsigned int i=no; i<n; i++) digit[i]=0; -} + friend class bigunsignedint<k/2>; + friend struct Impl::numeric_limits_helper< bigunsignedint<k> >; -// export -template<int k> -inline std::uint_least32_t bigunsignedint<k>::touint () const -{ -return (digit[1]<<bits)+digit[0]; -} - -template<int k> -inline double bigunsignedint<k>::todouble() const -{ -int firstInZeroRange=n; -for(int i=n-1; i>=0; --i) -if(digit[i]!=0) -break; -else ---firstInZeroRange; -int representableDigits=std::numeric_limits<double>::digits/bits; -int lastInRepresentableRange=0; -if(representableDigits<firstInZeroRange) -lastInRepresentableRange=firstInZeroRange-representableDigits; -double val=0; -for(int i=firstInZeroRange-1; i>=lastInRepresentableRange; --i) -val =val*(1<<bits)+digit[i]; -return val*(1<<(bits*lastInRepresentableRange)); -} -// print -template<int k> -inline void bigunsignedint<k>::print (std::ostream& s) const -{ -bool leading=false; - -// print from left to right -for (int i=n-1; i>=0; i--) -for (int d=hexdigits-1; d>=0; d--) -{ -// extract one hex digit -int current = (digit[i]>>(d*4))&0xF; -if (current!=0) -{ -// s.setf(std::ios::noshowbase); -s << std::hex << current; -leading = false; -} -else if (!leading) s << std::hex << current; -} -if (leading) s << "0"; -s << std::dec; -} - -template <int k> -inline std::ostream& operator<< (std::ostream& s, const bigunsignedint<k>& x) -{ -x.print(s); -return s; -} - -#define DUNE_BINOP(OP) \ -template <int k> \ -inline bigunsignedint<k> bigunsignedint<k>::operator OP (const bigunsignedint<k> &x) const \ -{ \ -auto temp = *this; \ -temp OP##= x; \ -return temp; \ -} - -DUNE_BINOP(+) -DUNE_BINOP(-) -DUNE_BINOP(*) -DUNE_BINOP(/) -DUNE_BINOP(%) -DUNE_BINOP(&) -DUNE_BINOP(^) -DUNE_BINOP(|) - -#undef DUNE_BINOP - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator+= (const bigunsignedint<k>& x) -{ -std::uint_fast32_t overflow=0; - -for (unsigned int i=0; i<n; i++) -{ -std::uint_fast32_t sum = static_cast<std::uint_fast32_t>(digit[i]) + static_cast<std::uint_fast32_t>(x.digit[i]) + overflow; -digit[i] = sum&bitmask; -overflow = (sum>>bits)&overflowmask; -} -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator-= (const bigunsignedint<k>& x) -{ -std::int_fast32_t overflow=0; + inline friend std::size_t hash_value(const bigunsignedint& arg) + { + return hash_range(arg.digit,arg.digit + arg.n); + } -for (unsigned int i=0; i<n; i++) -{ -std::int_fast32_t diff = static_cast<std::int_fast32_t>(digit[i]) - static_cast<std::int_fast32_t>(x.digit[i]) - overflow; -if (diff>=0) -{ -digit[i] = static_cast<std::uint16_t>(diff); -overflow = 0; -} -else -{ -digit[i] = static_cast<std::uint16_t>(diff+bitmask+1); -overflow = 1; -} -} -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator*= (const bigunsignedint<k>& x) -{ -bigunsignedint<2*k> finalproduct(0); - -for (unsigned int m=0; m<n; m++) // digit in right factor -{ -bigunsignedint<2*k> singleproduct(0); -std::uint_fast32_t overflow(0); -for (unsigned int i=0; i<n; i++) -{ -std::uint_fast32_t digitproduct = static_cast<std::uint_fast32_t>(digit[i])*static_cast<std::uint_fast32_t>(x.digit[m])+overflow; -singleproduct.digit[i+m] = static_cast<std::uint16_t>(digitproduct&bitmask); -overflow = (digitproduct>>bits)&bitmask; -} -finalproduct = finalproduct+singleproduct; -} - -for (unsigned int i=0; i<n; i++) digit[i] = finalproduct.digit[i]; -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator++ () -{ -std::uint_fast32_t overflow=1; - -for (unsigned int i=0; i<n; i++) -{ -std::uint_fast32_t sum = static_cast<std::uint_fast32_t>(digit[i]) + overflow; -digit[i] = sum&bitmask; -overflow = (sum>>bits)&overflowmask; -} -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator/= (const bigunsignedint<k>& x) -{ -if(x==0) -DUNE_THROW(Dune::MathError, "division by zero!"); - -// better slow than nothing -bigunsignedint<k> result(0); - -while (*this>=x) -{ -++result; -*this -= x; -} - -*this = result; -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator%= (const bigunsignedint<k>& x) -{ -// better slow than nothing -while (*this>=x) -{ -*this -= x; -} - -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator&= (const bigunsignedint<k>& x) -{ -for (unsigned int i=0; i<n; i++) -digit[i] = digit[i]&x.digit[i]; -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator^= (const bigunsignedint<k>& x) -{ -for (unsigned int i=0; i<n; i++) -digit[i] = digit[i]^x.digit[i]; -return *this; -} - -template <int k> -inline bigunsignedint<k>& bigunsignedint<k>::operator|= (const bigunsignedint<k>& x) -{ -for (unsigned int i=0; i<n; i++) -digit[i] = digit[i]|x.digit[i]; -return *this; -} - -template <int k> -inline bigunsignedint<k> bigunsignedint<k>::operator~ () const -{ -bigunsignedint<k> result; -for (unsigned int i=0; i<n; i++) -result.digit[i] = ~digit[i]; -return result; -} - -template <int k> -inline bigunsignedint<k> bigunsignedint<k>::operator<< (int shift) const -{ -bigunsignedint<k> result(0); - -// multiples of bits -int j=shift/bits; -for (int i=n-1-j; i>=0; i--) -result.digit[i+j] = digit[i]; - -// remainder -j=shift%bits; -for (int i=n-1; i>=0; i--) -{ -unsigned int temp = result.digit[i]; -temp = temp<<j; -result.digit[i] = static_cast<std::uint16_t>(temp&bitmask); -temp = temp>>bits; -if (i+1<(int)n) -result.digit[i+1] = result.digit[i+1]|temp; -} - -return result; -} - -template <int k> -inline bigunsignedint<k> bigunsignedint<k>::operator>> (int shift) const -{ -bigunsignedint<k> result(0); - -// multiples of bits -int j=shift/bits; -for (unsigned int i=0; i<n-j; i++) -result.digit[i] = digit[i+j]; - -// remainder -j=shift%bits; -for (unsigned int i=0; i<n; i++) -{ -std::uint_fast32_t temp = result.digit[i]; -temp = temp<<(bits-j); -result.digit[i] = static_cast<std::uint16_t>((temp&compbitmask)>>bits); -if (i>0) -result.digit[i-1] = result.digit[i-1] | (temp&bitmask); -} - -return result; -} - -template <int k> -inline bool bigunsignedint<k>::operator!= (const bigunsignedint<k>& x) const -{ -for (unsigned int i=0; i<n; i++) -if (digit[i]!=x.digit[i]) return true; -return false; -} - -template <int k> -inline bool bigunsignedint<k>::operator== (const bigunsignedint<k>& x) const -{ -return !((*this)!=x); -} - -template <int k> -inline bool bigunsignedint<k>::operator< (const bigunsignedint<k>& x) const -{ -for (int i=n-1; i>=0; i--) -if (digit[i]<x.digit[i]) return true; -else if (digit[i]>x.digit[i]) return false; -return false; -} - -template <int k> -inline bool bigunsignedint<k>::operator<= (const bigunsignedint<k>& x) const -{ -for (int i=n-1; i>=0; i--) -if (digit[i]<x.digit[i]) return true; -else if (digit[i]>x.digit[i]) return false; -return true; -} - -template <int k> -inline bool bigunsignedint<k>::operator> (const bigunsignedint<k>& x) const -{ -return !((*this)<=x); -} - -template <int k> -inline bool bigunsignedint<k>::operator>= (const bigunsignedint<k>& x) const -{ -return !((*this)<x); -} - - -template <int k> -inline bigunsignedint<k> operator+ (const bigunsignedint<k>& x, std::uintmax_t y) -{ -bigunsignedint<k> temp(y); -return x+temp; -} - -template <int k> -inline bigunsignedint<k> operator- (const bigunsignedint<k>& x, std::uintmax_t y) -{ -bigunsignedint<k> temp(y); -return x-temp; -} - -template <int k> -inline bigunsignedint<k> operator* (const bigunsignedint<k>& x, std::uintmax_t y) -{ -bigunsignedint<k> temp(y); -return x*temp; -} - -template <int k> -inline bigunsignedint<k> operator/ (const bigunsignedint<k>& x, std::uintmax_t y) -{ -bigunsignedint<k> temp(y); -return x/temp; -} - -template <int k> -inline bigunsignedint<k> operator% (const bigunsignedint<k>& x, std::uintmax_t y) -{ -bigunsignedint<k> temp(y); -return x%temp; -} - -template <int k> -inline bigunsignedint<k> operator+ (std::uintmax_t x, const bigunsignedint<k>& y) -{ -bigunsignedint<k> temp(x); -return temp+y; -} - -template <int k> -inline bigunsignedint<k> operator- (std::uintmax_t x, const bigunsignedint<k>& y) -{ -bigunsignedint<k> temp(x); -return temp-y; -} - -template <int k> -inline bigunsignedint<k> operator* (std::uintmax_t x, const bigunsignedint<k>& y) -{ -bigunsignedint<k> temp(x); -return temp*y; -} - -template <int k> -inline bigunsignedint<k> operator/ (std::uintmax_t x, const bigunsignedint<k>& y) -{ -bigunsignedint<k> temp(x); -return temp/y; -} - -template <int k> -inline bigunsignedint<k> operator% (std::uintmax_t x, const bigunsignedint<k>& y) -{ -bigunsignedint<k> temp(x); -return temp%y; -} - - -/** @} */ + private: + std::uint16_t digit[n]; +#if HAVE_MPI + friend struct MPITraits<bigunsignedint<k> >; +#endif + inline void assign(std::uintmax_t x); + + + } ; + + // Constructors + template<int k> + bigunsignedint<k>::bigunsignedint () + { + assign(0u); + } + + template<int k> + template<typename Signed> + bigunsignedint<k>::bigunsignedint (Signed y, typename std::enable_if<std::is_integral<Signed>::value && std::is_signed<Signed>::value>::type*) + { + if (y < 0) + DUNE_THROW(Dune::Exception, "Trying to construct a Dune::bigunsignedint from a negative integer: " << y); + assign(y); + } + + template<int k> + bigunsignedint<k>::bigunsignedint (std::uintmax_t x) + { + assign(x); + } + template<int k> + void bigunsignedint<k>::assign(std::uintmax_t x) + { + static const int no=std::min(static_cast<int>(n), + static_cast<int>(std::numeric_limits<std::uintmax_t>::digits/bits)); + + for(int i=0; i<no; ++i) { + digit[i] = (x&bitmask); + x=x>>bits; + } + for (unsigned int i=no; i<n; i++) digit[i]=0; + } + + // export + template<int k> + inline std::uint_least32_t bigunsignedint<k>::touint () const + { + return (digit[1]<<bits)+digit[0]; + } + + template<int k> + inline double bigunsignedint<k>::todouble() const + { + int firstInZeroRange=n; + for(int i=n-1; i>=0; --i) + if(digit[i]!=0) + break; + else + --firstInZeroRange; + int representableDigits=std::numeric_limits<double>::digits/bits; + int lastInRepresentableRange=0; + if(representableDigits<firstInZeroRange) + lastInRepresentableRange=firstInZeroRange-representableDigits; + double val=0; + for(int i=firstInZeroRange-1; i>=lastInRepresentableRange; --i) + val =val*(1<<bits)+digit[i]; + return val*(1<<(bits*lastInRepresentableRange)); + } + // print + template<int k> + inline void bigunsignedint<k>::print (std::ostream& s) const + { + bool leading=false; + + // print from left to right + for (int i=n-1; i>=0; i--) + for (int d=hexdigits-1; d>=0; d--) + { + // extract one hex digit + int current = (digit[i]>>(d*4))&0xF; + if (current!=0) + { + // s.setf(std::ios::noshowbase); + s << std::hex << current; + leading = false; + } + else if (!leading) s << std::hex << current; + } + if (leading) s << "0"; + s << std::dec; + } + + template <int k> + inline std::ostream& operator<< (std::ostream& s, const bigunsignedint<k>& x) + { + x.print(s); + return s; + } + + #define DUNE_BINOP(OP) \ + template <int k> \ + inline bigunsignedint<k> bigunsignedint<k>::operator OP (const bigunsignedint<k> &x) const \ + { \ + auto temp = *this; \ + temp OP##= x; \ + return temp; \ + } + + DUNE_BINOP(+) + DUNE_BINOP(-) + DUNE_BINOP(*) + DUNE_BINOP(/) + DUNE_BINOP(%) + DUNE_BINOP(&) + DUNE_BINOP(^) + DUNE_BINOP(|) + + #undef DUNE_BINOP + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator+= (const bigunsignedint<k>& x) + { + std::uint_fast32_t overflow=0; + + for (unsigned int i=0; i<n; i++) + { + std::uint_fast32_t sum = static_cast<std::uint_fast32_t>(digit[i]) + static_cast<std::uint_fast32_t>(x.digit[i]) + overflow; + digit[i] = sum&bitmask; + overflow = (sum>>bits)&overflowmask; + } + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator-= (const bigunsignedint<k>& x) + { + std::int_fast32_t overflow=0; + + for (unsigned int i=0; i<n; i++) + { + std::int_fast32_t diff = static_cast<std::int_fast32_t>(digit[i]) - static_cast<std::int_fast32_t>(x.digit[i]) - overflow; + if (diff>=0) + { + digit[i] = static_cast<std::uint16_t>(diff); + overflow = 0; + } + else + { + digit[i] = static_cast<std::uint16_t>(diff+bitmask+1); + overflow = 1; + } + } + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator*= (const bigunsignedint<k>& x) + { + bigunsignedint<2*k> finalproduct(0); + + for (unsigned int m=0; m<n; m++) // digit in right factor + { + bigunsignedint<2*k> singleproduct(0); + std::uint_fast32_t overflow(0); + for (unsigned int i=0; i<n; i++) + { + std::uint_fast32_t digitproduct = static_cast<std::uint_fast32_t>(digit[i])*static_cast<std::uint_fast32_t>(x.digit[m])+overflow; + singleproduct.digit[i+m] = static_cast<std::uint16_t>(digitproduct&bitmask); + overflow = (digitproduct>>bits)&bitmask; + } + finalproduct = finalproduct+singleproduct; + } + + for (unsigned int i=0; i<n; i++) digit[i] = finalproduct.digit[i]; + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator++ () + { + std::uint_fast32_t overflow=1; + + for (unsigned int i=0; i<n; i++) + { + std::uint_fast32_t sum = static_cast<std::uint_fast32_t>(digit[i]) + overflow; + digit[i] = sum&bitmask; + overflow = (sum>>bits)&overflowmask; + } + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator/= (const bigunsignedint<k>& x) + { + if(x==0) + DUNE_THROW(Dune::MathError, "division by zero!"); + + // better slow than nothing + bigunsignedint<k> result(0); + + while (*this>=x) + { + ++result; + *this -= x; + } + + *this = result; + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator%= (const bigunsignedint<k>& x) + { + // better slow than nothing + while (*this>=x) + { + *this -= x; + } + + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator&= (const bigunsignedint<k>& x) + { + for (unsigned int i=0; i<n; i++) + digit[i] = digit[i]&x.digit[i]; + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator^= (const bigunsignedint<k>& x) + { + for (unsigned int i=0; i<n; i++) + digit[i] = digit[i]^x.digit[i]; + return *this; + } + + template <int k> + inline bigunsignedint<k>& bigunsignedint<k>::operator|= (const bigunsignedint<k>& x) + { + for (unsigned int i=0; i<n; i++) + digit[i] = digit[i]|x.digit[i]; + return *this; + } + + template <int k> + inline bigunsignedint<k> bigunsignedint<k>::operator~ () const + { + bigunsignedint<k> result; + for (unsigned int i=0; i<n; i++) + result.digit[i] = ~digit[i]; + return result; + } + + template <int k> + inline bigunsignedint<k> bigunsignedint<k>::operator<< (int shift) const + { + bigunsignedint<k> result(0); + + // multiples of bits + int j=shift/bits; + for (int i=n-1-j; i>=0; i--) + result.digit[i+j] = digit[i]; + + // remainder + j=shift%bits; + for (int i=n-1; i>=0; i--) + { + unsigned int temp = result.digit[i]; + temp = temp<<j; + result.digit[i] = static_cast<std::uint16_t>(temp&bitmask); + temp = temp>>bits; + if (i+1<(int)n) + result.digit[i+1] = result.digit[i+1]|temp; + } + + return result; + } + + template <int k> + inline bigunsignedint<k> bigunsignedint<k>::operator>> (int shift) const + { + bigunsignedint<k> result(0); + + // multiples of bits + int j=shift/bits; + for (unsigned int i=0; i<n-j; i++) + result.digit[i] = digit[i+j]; + + // remainder + j=shift%bits; + for (unsigned int i=0; i<n; i++) + { + std::uint_fast32_t temp = result.digit[i]; + temp = temp<<(bits-j); + result.digit[i] = static_cast<std::uint16_t>((temp&compbitmask)>>bits); + if (i>0) + result.digit[i-1] = result.digit[i-1] | (temp&bitmask); + } + + return result; + } + + template <int k> + inline bool bigunsignedint<k>::operator!= (const bigunsignedint<k>& x) const + { + for (unsigned int i=0; i<n; i++) + if (digit[i]!=x.digit[i]) return true; + return false; + } + + template <int k> + inline bool bigunsignedint<k>::operator== (const bigunsignedint<k>& x) const + { + return !((*this)!=x); + } + + template <int k> + inline bool bigunsignedint<k>::operator< (const bigunsignedint<k>& x) const + { + for (int i=n-1; i>=0; i--) + if (digit[i]<x.digit[i]) return true; + else if (digit[i]>x.digit[i]) return false; + return false; + } + + template <int k> + inline bool bigunsignedint<k>::operator<= (const bigunsignedint<k>& x) const + { + for (int i=n-1; i>=0; i--) + if (digit[i]<x.digit[i]) return true; + else if (digit[i]>x.digit[i]) return false; + return true; + } + + template <int k> + inline bool bigunsignedint<k>::operator> (const bigunsignedint<k>& x) const + { + return !((*this)<=x); + } + + template <int k> + inline bool bigunsignedint<k>::operator>= (const bigunsignedint<k>& x) const + { + return !((*this)<x); + } + + + template <int k> + inline bigunsignedint<k> operator+ (const bigunsignedint<k>& x, std::uintmax_t y) + { + bigunsignedint<k> temp(y); + return x+temp; + } + + template <int k> + inline bigunsignedint<k> operator- (const bigunsignedint<k>& x, std::uintmax_t y) + { + bigunsignedint<k> temp(y); + return x-temp; + } + + template <int k> + inline bigunsignedint<k> operator* (const bigunsignedint<k>& x, std::uintmax_t y) + { + bigunsignedint<k> temp(y); + return x*temp; + } + + template <int k> + inline bigunsignedint<k> operator/ (const bigunsignedint<k>& x, std::uintmax_t y) + { + bigunsignedint<k> temp(y); + return x/temp; + } + + template <int k> + inline bigunsignedint<k> operator% (const bigunsignedint<k>& x, std::uintmax_t y) + { + bigunsignedint<k> temp(y); + return x%temp; + } + + template <int k> + inline bigunsignedint<k> operator+ (std::uintmax_t x, const bigunsignedint<k>& y) + { + bigunsignedint<k> temp(x); + return temp+y; + } + + template <int k> + inline bigunsignedint<k> operator- (std::uintmax_t x, const bigunsignedint<k>& y) + { + bigunsignedint<k> temp(x); + return temp-y; + } + + template <int k> + inline bigunsignedint<k> operator* (std::uintmax_t x, const bigunsignedint<k>& y) + { + bigunsignedint<k> temp(x); + return temp*y; + } + + template <int k> + inline bigunsignedint<k> operator/ (std::uintmax_t x, const bigunsignedint<k>& y) + { + bigunsignedint<k> temp(x); + return temp/y; + } + + template <int k> + inline bigunsignedint<k> operator% (std::uintmax_t x, const bigunsignedint<k>& y) + { + bigunsignedint<k> temp(x); + return temp%y; + } + + + /** @} */ } namespace std { -template<int k> -struct numeric_limits<Dune::bigunsignedint<k> > -: private Dune::Impl::numeric_limits_helper<Dune::bigunsignedint<k> > // for access to internal state of bigunsignedint -{ -public: -static const bool is_specialized = true; - -static Dune::bigunsignedint<k> min() -{ -return static_cast<Dune::bigunsignedint<k> >(0); -} - -static Dune::bigunsignedint<k> max() -{ -Dune::bigunsignedint<k> max_; -for(std::size_t i=0; i < Dune::bigunsignedint<k>::n; ++i) -// access internal state via the helper base class -Dune::Impl::numeric_limits_helper<Dune::bigunsignedint<k> >:: -digit(max_,i)=std::numeric_limits<std::uint16_t>::max(); -return max_; -} - - -static const int digits = Dune::bigunsignedint<k>::bits * -Dune::bigunsignedint<k>::n; -static const bool is_signed = false; -static const bool is_integer = true; -static const bool is_exact = true; -static const int radix = 2; - -static Dune::bigunsignedint<k> epsilon() -{ -return static_cast<Dune::bigunsignedint<k> >(0); -} - -static Dune::bigunsignedint<k> round_error() -{ -return static_cast<Dune::bigunsignedint<k> >(0); -} - -static const int min_exponent = 0; -static const int min_exponent10 = 0; -static const int max_exponent = 0; -static const int max_exponent10 = 0; - -static const bool has_infinity = false; -static const bool has_quiet_NaN = false; -static const bool has_signaling_NaN = false; - -static const float_denorm_style has_denorm = denorm_absent; -static const bool has_denorm_loss = false; - -static Dune::bigunsignedint<k> infinity() noexcept -{ -return static_cast<Dune::bigunsignedint<k> >(0); -} - -static Dune::bigunsignedint<k> quiet_NaN() noexcept -{ -return static_cast<Dune::bigunsignedint<k> >(0); -} - -static Dune::bigunsignedint<k> signaling_NaN() noexcept -{ -return static_cast<Dune::bigunsignedint<k> >(0); -} - -static Dune::bigunsignedint<k> denorm_min() noexcept -{ -return static_cast<Dune::bigunsignedint<k> >(0); -} - -static const bool is_iec559 = false; -static const bool is_bounded = true; -static const bool is_modulo = true; - -static const bool traps = false; -static const bool tinyness_before = false; -static const float_round_style round_style = round_toward_zero; - -}; + template<int k> + struct numeric_limits<Dune::bigunsignedint<k> > + : private Dune::Impl::numeric_limits_helper<Dune::bigunsignedint<k> > // for access to internal state of bigunsignedint + { + public: + static const bool is_specialized = true; + + static Dune::bigunsignedint<k> min() + { + return static_cast<Dune::bigunsignedint<k> >(0); + } + + static Dune::bigunsignedint<k> max() + { + Dune::bigunsignedint<k> max_; + for(std::size_t i=0; i < Dune::bigunsignedint<k>::n; ++i) + // access internal state via the helper base class + Dune::Impl::numeric_limits_helper<Dune::bigunsignedint<k> >:: + digit(max_,i)=std::numeric_limits<std::uint16_t>::max(); + return max_; + } + + + static const int digits = Dune::bigunsignedint<k>::bits * + Dune::bigunsignedint<k>::n; + static const bool is_signed = false; + static const bool is_integer = true; + static const bool is_exact = true; + static const int radix = 2; + + static Dune::bigunsignedint<k> epsilon() + { + return static_cast<Dune::bigunsignedint<k> >(0); + } + + static Dune::bigunsignedint<k> round_error() + { + return static_cast<Dune::bigunsignedint<k> >(0); + } + + static const int min_exponent = 0; + static const int min_exponent10 = 0; + static const int max_exponent = 0; + static const int max_exponent10 = 0; + + static const bool has_infinity = false; + static const bool has_quiet_NaN = false; + static const bool has_signaling_NaN = false; + + static const float_denorm_style has_denorm = denorm_absent; + static const bool has_denorm_loss = false; + + static Dune::bigunsignedint<k> infinity() noexcept + { + return static_cast<Dune::bigunsignedint<k> >(0); + } + + static Dune::bigunsignedint<k> quiet_NaN() noexcept + { + return static_cast<Dune::bigunsignedint<k> >(0); + } + + static Dune::bigunsignedint<k> signaling_NaN() noexcept + { + return static_cast<Dune::bigunsignedint<k> >(0); + } + + static Dune::bigunsignedint<k> denorm_min() noexcept + { + return static_cast<Dune::bigunsignedint<k> >(0); + } + + static const bool is_iec559 = false; + static const bool is_bounded = true; + static const bool is_modulo = true; + + static const bool traps = false; + static const bool tinyness_before = false; + static const float_round_style round_style = round_toward_zero; + + }; } diff --git a/dune/common/binaryfunctions.hh b/dune/common/binaryfunctions.hh index 1a5921f0c1a7bdbc0e9468ae47b149f3f14a88e3..8b92fa58953bb0503341d8286a8cf0fdb96bd7ef 100644 --- a/dune/common/binaryfunctions.hh +++ b/dune/common/binaryfunctions.hh @@ -5,44 +5,44 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief helper classes to provide unique types for standard functions -*/ + * \brief helper classes to provide unique types for standard functions + */ #include <algorithm> namespace Dune { -template<typename Type> -struct Min -{ -using first_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + template<typename Type> + struct Min + { + using first_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; -using second_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + using second_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; -using result_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + using result_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; -Type operator()(const Type& t1, const Type& t2) const -{ -using std::min; -return min(t1,t2); -} -}; + Type operator()(const Type& t1, const Type& t2) const + { + using std::min; + return min(t1,t2); + } + }; -template<typename Type> -struct Max -{ -using first_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + template<typename Type> + struct Max + { + using first_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; -using second_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + using second_argument_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; -using result_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; + using result_type [[deprecated("This type alias is deprecated following similar deprecations in C++17")]] = Type; -Type operator()(const Type& t1, const Type& t2) const -{ -using std::max; -return max(t1,t2); -} -}; + Type operator()(const Type& t1, const Type& t2) const + { + using std::max; + return max(t1,t2); + } + }; } #endif diff --git a/dune/common/bitsetvector.hh b/dune/common/bitsetvector.hh index c71f9c246081c58b82b40be568ccb93033159961..ad6b2bbfddd220ab752ac8af6da39dcb11897459 100644 --- a/dune/common/bitsetvector.hh +++ b/dune/common/bitsetvector.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -\brief Efficient implementation of a dynamic array of static arrays of booleans -*/ + \brief Efficient implementation of a dynamic array of static arrays of booleans + */ #include <vector> #include <bitset> @@ -19,635 +19,635 @@ namespace Dune { -template <int block_size, class Alloc> class BitSetVector; -template <int block_size, class Alloc> class BitSetVectorReference; - -/** -\brief A proxy class that acts as a const reference to a single -bitset in a BitSetVector. - -It contains a conversion to std::bitset and most of the -interface of const std::bitset. - -\warning As this is only a proxy class, you can not get the -address of the bitset. -*/ -template <int block_size, class Alloc> -class BitSetVectorConstReference -{ -protected: - -typedef Dune::BitSetVector<block_size, Alloc> BitSetVector; -friend class Dune::BitSetVector<block_size, Alloc>; - -BitSetVectorConstReference(const BitSetVector& blockBitField_, int block_number_) : -blockBitField(blockBitField_), -block_number(block_number_) -{ -DUNE_ASSERT_BOUNDS(blockBitField_.size() > static_cast<size_t>(block_number_)); -} - -//! hide assignment operator -BitSetVectorConstReference& operator=(const BitSetVectorConstReference & b); - -public: - -typedef std::bitset<block_size> bitset; - -// bitset interface typedefs -typedef typename std::vector<bool, Alloc>::const_reference reference; -typedef typename std::vector<bool, Alloc>::const_reference const_reference; -typedef size_t size_type; - -//! Returns a copy of *this shifted left by n bits. -bitset operator<<(size_type n) const -{ -bitset b = *this; -b <<= n; -return b; -} - -//! Returns a copy of *this shifted right by n bits. -bitset operator>>(size_type n) const -{ -bitset b = *this; -b >>= n; -return b; -} - -//! Returns a copy of *this with all of its bits flipped. -bitset operator~() const -{ -bitset b = *this; -b.flip(); -return b; -} - -//! Returns block_size. -size_type size() const -{ -return block_size; -} - -//! Returns the number of bits that are set. -size_type count() const -{ -size_type n = 0; -for(size_type i=0; i<block_size; ++i) -n += getBit(i); -return n; -} - -//! Returns true if any bits are set. -bool any() const -{ -return count(); -} - -//! Returns true if no bits are set. -bool none() const -{ -return ! any(); -} - -//! Returns true if all bits are set -bool all() const -{ -for(size_type i=0; i<block_size; ++i) -if(not test(i)) -return false; -return true; -} - -//! Returns true if bit n is set. -bool test(size_type n) const -{ -return getBit(n); -} - -//! Return reference to the `i`-th bit -const_reference operator[](size_type i) const -{ -return getBit(i); -} - -//! cast to bitset -operator bitset() const -{ -return blockBitField.getRepr(block_number); -} - -//! Equality of reference and std::bitset -bool operator== (const bitset& bs) const -{ -return equals(bs); -} - -//! Equality of reference and other reference -bool operator== (const BitSetVectorConstReference& bs) const -{ -return equals(bs); -} - -//! Inequality of reference and std::bitset -bool operator!= (const bitset& bs) const -{ -return ! equals(bs); -} - -//! Inequality of reference and other reference -bool operator!= (const BitSetVectorConstReference& bs) const -{ -return ! equals(bs); -} - -/*! -missing operators: - -- unsigned long to_ulong() const -*/ - -friend std::ostream& operator<< (std::ostream& s, const BitSetVectorConstReference& v) -{ -s << "("; -for(int i=0; i<block_size; ++i) -s << v[i]; -s << ")"; -return s; -} - -protected: -const BitSetVector& blockBitField; -int block_number; - -const_reference getBit(size_type i) const -{ -return blockBitField.getBit(block_number,i); -} - -template<class BS> -bool equals(const BS & bs) const -{ -bool eq = true; -for(int i=0; i<block_size; ++i) -eq &= (getBit(i) == bs[i]); -return eq; -} - -private: -/** -This is only a Proxy class, you can't get the address of the -object it references -*/ -void operator & () = delete; - -friend class BitSetVectorReference<block_size, Alloc>; -}; - -/** -\brief A proxy class that acts as a mutable reference to a -single bitset in a BitSetVector. - -It contains an assignment operator from std::bitset. It -inherits the const std::bitset interface provided by -BitSetVectorConstReference and adds most of the non-const -methods of std::bitset. - -\warning As this is only a proxy class, you can not get the -address of the bitset. -*/ -template <int block_size, class Alloc> -class BitSetVectorReference : public BitSetVectorConstReference<block_size,Alloc> -{ -protected: - -typedef Dune::BitSetVector<block_size, Alloc> BitSetVector; -friend class Dune::BitSetVector<block_size, Alloc>; - -typedef Dune::BitSetVectorConstReference<block_size,Alloc> BitSetVectorConstReference; - -BitSetVectorReference(BitSetVector& blockBitField_, int block_number_) : -BitSetVectorConstReference(blockBitField_, block_number_), -blockBitField(blockBitField_) -{} - -public: -typedef std::bitset<block_size> bitset; - -//! bitset interface typedefs -//! \{ -//! A proxy class that acts as a reference to a single bit. -typedef typename std::vector<bool, Alloc>::reference reference; -//! A proxy class that acts as a const reference to a single bit. -typedef typename std::vector<bool, Alloc>::const_reference const_reference; -//! \} - -//! size_type typedef (an unsigned integral type) -typedef size_t size_type; - -//! Assignment from bool, sets each bit in the bitset to b -BitSetVectorReference& operator=(bool b) -{ -for(int i=0; i<block_size; ++i) -getBit(i) = b; -return (*this); -} - -//! Assignment from bitset -BitSetVectorReference& operator=(const bitset & b) -{ -for(int i=0; i<block_size; ++i) -getBit(i) = b.test(i); -return (*this); -} - -//! Assignment from BitSetVectorConstReference -BitSetVectorReference& operator=(const BitSetVectorConstReference & b) -{ -for(int i=0; i<block_size; ++i) -getBit(i) = b.test(i); -return (*this); -} - -//! Assignment from BitSetVectorReference -BitSetVectorReference& operator=(const BitSetVectorReference & b) -{ -for(int i=0; i<block_size; ++i) -getBit(i) = b.test(i); -return (*this); -} - -//! Bitwise and (for bitset). -BitSetVectorReference& operator&=(const bitset& x) -{ -for (size_type i=0; i<block_size; i++) -getBit(i) = (test(i) & x.test(i)); -return *this; -} - -//! Bitwise and (for BitSetVectorConstReference and BitSetVectorReference) -BitSetVectorReference& operator&=(const BitSetVectorConstReference& x) -{ -for (size_type i=0; i<block_size; i++) -getBit(i) = (test(i) & x.test(i)); -return *this; -} - -//! Bitwise inclusive or (for bitset) -BitSetVectorReference& operator|=(const bitset& x) -{ -for (size_type i=0; i<block_size; i++) -getBit(i) = (test(i) | x.test(i)); -return *this; -} - -//! Bitwise inclusive or (for BitSetVectorConstReference and BitSetVectorReference) -BitSetVectorReference& operator|=(const BitSetVectorConstReference& x) -{ -for (size_type i=0; i<block_size; i++) -getBit(i) = (test(i) | x.test(i)); -return *this; -} - -//! Bitwise exclusive or (for bitset). -BitSetVectorReference& operator^=(const bitset& x) -{ -for (size_type i=0; i<block_size; i++) -getBit(i) = (test(i) ^ x.test(i)); -return *this; -} - -private: - -// For some reason, the following variant of operator^= triggers an ICE or a hanging -// compiler on Debian 9 with GCC 6.3 and full optimizations enabled (-O3). -// The only way to reliably avoid the issue is by making sure that the compiler does not -// see the XOR in the context of the function, so here is a little helper that will normally -// be inlined, but not on the broken compiler. This incurs a substantial overhead (a function -// call), but until someone else has a better idea, it's the only way to make it work reliably. - -static bool xor_helper(bool a, bool b) + template <int block_size, class Alloc> class BitSetVector; + template <int block_size, class Alloc> class BitSetVectorReference; + + /** + \brief A proxy class that acts as a const reference to a single + bitset in a BitSetVector. + + It contains a conversion to std::bitset and most of the + interface of const std::bitset. + + \warning As this is only a proxy class, you can not get the + address of the bitset. + */ + template <int block_size, class Alloc> + class BitSetVectorConstReference + { + protected: + + typedef Dune::BitSetVector<block_size, Alloc> BitSetVector; + friend class Dune::BitSetVector<block_size, Alloc>; + + BitSetVectorConstReference(const BitSetVector& blockBitField_, int block_number_) : + blockBitField(blockBitField_), + block_number(block_number_) + { + DUNE_ASSERT_BOUNDS(blockBitField_.size() > static_cast<size_t>(block_number_)); + } + + //! hide assignment operator + BitSetVectorConstReference& operator=(const BitSetVectorConstReference & b); + + public: + + typedef std::bitset<block_size> bitset; + + // bitset interface typedefs + typedef typename std::vector<bool, Alloc>::const_reference reference; + typedef typename std::vector<bool, Alloc>::const_reference const_reference; + typedef size_t size_type; + + //! Returns a copy of *this shifted left by n bits. + bitset operator<<(size_type n) const + { + bitset b = *this; + b <<= n; + return b; + } + + //! Returns a copy of *this shifted right by n bits. + bitset operator>>(size_type n) const + { + bitset b = *this; + b >>= n; + return b; + } + + //! Returns a copy of *this with all of its bits flipped. + bitset operator~() const + { + bitset b = *this; + b.flip(); + return b; + } + + //! Returns block_size. + size_type size() const + { + return block_size; + } + + //! Returns the number of bits that are set. + size_type count() const + { + size_type n = 0; + for(size_type i=0; i<block_size; ++i) + n += getBit(i); + return n; + } + + //! Returns true if any bits are set. + bool any() const + { + return count(); + } + + //! Returns true if no bits are set. + bool none() const + { + return ! any(); + } + + //! Returns true if all bits are set + bool all() const + { + for(size_type i=0; i<block_size; ++i) + if(not test(i)) + return false; + return true; + } + + //! Returns true if bit n is set. + bool test(size_type n) const + { + return getBit(n); + } + + //! Return reference to the `i`-th bit + const_reference operator[](size_type i) const + { + return getBit(i); + } + + //! cast to bitset + operator bitset() const + { + return blockBitField.getRepr(block_number); + } + + //! Equality of reference and std::bitset + bool operator== (const bitset& bs) const + { + return equals(bs); + } + + //! Equality of reference and other reference + bool operator== (const BitSetVectorConstReference& bs) const + { + return equals(bs); + } + + //! Inequality of reference and std::bitset + bool operator!= (const bitset& bs) const + { + return ! equals(bs); + } + + //! Inequality of reference and other reference + bool operator!= (const BitSetVectorConstReference& bs) const + { + return ! equals(bs); + } + + /*! + missing operators: + + - unsigned long to_ulong() const + */ + + friend std::ostream& operator<< (std::ostream& s, const BitSetVectorConstReference& v) + { + s << "("; + for(int i=0; i<block_size; ++i) + s << v[i]; + s << ")"; + return s; + } + + protected: + const BitSetVector& blockBitField; + int block_number; + + const_reference getBit(size_type i) const + { + return blockBitField.getBit(block_number,i); + } + + template<class BS> + bool equals(const BS & bs) const + { + bool eq = true; + for(int i=0; i<block_size; ++i) + eq &= (getBit(i) == bs[i]); + return eq; + } + + private: + /** + This is only a Proxy class, you can't get the address of the + object it references + */ + void operator & () = delete; + + friend class BitSetVectorReference<block_size, Alloc>; + }; + + /** + \brief A proxy class that acts as a mutable reference to a + single bitset in a BitSetVector. + + It contains an assignment operator from std::bitset. It + inherits the const std::bitset interface provided by + BitSetVectorConstReference and adds most of the non-const + methods of std::bitset. + + \warning As this is only a proxy class, you can not get the + address of the bitset. + */ + template <int block_size, class Alloc> + class BitSetVectorReference : public BitSetVectorConstReference<block_size,Alloc> + { + protected: + + typedef Dune::BitSetVector<block_size, Alloc> BitSetVector; + friend class Dune::BitSetVector<block_size, Alloc>; + + typedef Dune::BitSetVectorConstReference<block_size,Alloc> BitSetVectorConstReference; + + BitSetVectorReference(BitSetVector& blockBitField_, int block_number_) : + BitSetVectorConstReference(blockBitField_, block_number_), + blockBitField(blockBitField_) + {} + + public: + typedef std::bitset<block_size> bitset; + + //! bitset interface typedefs + //! \{ + //! A proxy class that acts as a reference to a single bit. + typedef typename std::vector<bool, Alloc>::reference reference; + //! A proxy class that acts as a const reference to a single bit. + typedef typename std::vector<bool, Alloc>::const_reference const_reference; + //! \} + + //! size_type typedef (an unsigned integral type) + typedef size_t size_type; + + //! Assignment from bool, sets each bit in the bitset to b + BitSetVectorReference& operator=(bool b) + { + for(int i=0; i<block_size; ++i) + getBit(i) = b; + return (*this); + } + + //! Assignment from bitset + BitSetVectorReference& operator=(const bitset & b) + { + for(int i=0; i<block_size; ++i) + getBit(i) = b.test(i); + return (*this); + } + + //! Assignment from BitSetVectorConstReference + BitSetVectorReference& operator=(const BitSetVectorConstReference & b) + { + for(int i=0; i<block_size; ++i) + getBit(i) = b.test(i); + return (*this); + } + + //! Assignment from BitSetVectorReference + BitSetVectorReference& operator=(const BitSetVectorReference & b) + { + for(int i=0; i<block_size; ++i) + getBit(i) = b.test(i); + return (*this); + } + + //! Bitwise and (for bitset). + BitSetVectorReference& operator&=(const bitset& x) + { + for (size_type i=0; i<block_size; i++) + getBit(i) = (test(i) & x.test(i)); + return *this; + } + + //! Bitwise and (for BitSetVectorConstReference and BitSetVectorReference) + BitSetVectorReference& operator&=(const BitSetVectorConstReference& x) + { + for (size_type i=0; i<block_size; i++) + getBit(i) = (test(i) & x.test(i)); + return *this; + } + + //! Bitwise inclusive or (for bitset) + BitSetVectorReference& operator|=(const bitset& x) + { + for (size_type i=0; i<block_size; i++) + getBit(i) = (test(i) | x.test(i)); + return *this; + } + + //! Bitwise inclusive or (for BitSetVectorConstReference and BitSetVectorReference) + BitSetVectorReference& operator|=(const BitSetVectorConstReference& x) + { + for (size_type i=0; i<block_size; i++) + getBit(i) = (test(i) | x.test(i)); + return *this; + } + + //! Bitwise exclusive or (for bitset). + BitSetVectorReference& operator^=(const bitset& x) + { + for (size_type i=0; i<block_size; i++) + getBit(i) = (test(i) ^ x.test(i)); + return *this; + } + + private: + + // For some reason, the following variant of operator^= triggers an ICE or a hanging + // compiler on Debian 9 with GCC 6.3 and full optimizations enabled (-O3). + // The only way to reliably avoid the issue is by making sure that the compiler does not + // see the XOR in the context of the function, so here is a little helper that will normally + // be inlined, but not on the broken compiler. This incurs a substantial overhead (a function + // call), but until someone else has a better idea, it's the only way to make it work reliably. + + static bool xor_helper(bool a, bool b) #if defined(__GNUC__) && ! defined(__clang__) && __GNUC__ == 6 && __GNUC_MINOR__ == 3 && __cplusplus \ -== 201402L -__attribute__((noinline)) + == 201402L + __attribute__((noinline)) #endif -; - -public: - -//! Bitwise exclusive or (for BitSetVectorConstReference and BitSetVectorReference) -BitSetVectorReference& operator^=(const BitSetVectorConstReference& x) -{ -// This uses the helper from above to hoist the actual XOR computation out of the function for -// the buggy version of GCC. -for (size_type i=0; i<block_size; i++) -getBit(i) = xor_helper(test(i),x.test(i)); -return *this; -} - -//! Left shift. -BitSetVectorReference& operator<<=(size_type n) -{ -for (size_type i=0; i<block_size-n; i++) -getBit(i) = test(i+n); -return *this; -} - -//! Right shift. -BitSetVectorReference& operator>>=(size_type n) -{ -for (size_type i=0; i<block_size-n; i++) -getBit(i+n) = test(i); -return *this; -} - -//! Sets every bit. -BitSetVectorReference& set() -{ -for (size_type i=0; i<block_size; i++) -set(i); -return *this; -} - -//! Flips the value of every bit. -BitSetVectorReference& flip() -{ -for (size_type i=0; i<block_size; i++) -flip(i); -return *this; -} - -//! Clears every bit. -BitSetVectorReference& reset() -{ -*this = false; -return *this; -} - -//! Sets bit n if val is nonzero, and clears bit n if val is zero. -BitSetVectorReference& set(size_type n, int val = 1) -{ -getBit(n) = val; -return *this; -} - -//! Clears bit n. -BitSetVectorReference& reset(size_type n) -{ -set(n, false); -return *this; -} - -//! Flips bit n. -BitSetVectorReference& flip(size_type n) -{ -getBit(n).flip(); -return *this; -} - -using BitSetVectorConstReference::test; -using BitSetVectorConstReference::operator[]; - -//! Return reference to the `i`-th bit -reference operator[](size_type i) -{ -return getBit(i); -} - -protected: -BitSetVector& blockBitField; - -using BitSetVectorConstReference::getBit; - -reference getBit(size_type i) -{ -return blockBitField.getBit(this->block_number,i); -} -}; - -// implementation of helper - I put it into the template to avoid having -// to compile it in a dedicated compilation unit -template<int block_size, class Alloc> -bool BitSetVectorReference<block_size,Alloc>::xor_helper(bool a, bool b) -{ -return a ^ b; -} - -/** -typetraits for BitSetVectorReference -*/ -template<int block_size, class Alloc> -struct const_reference< BitSetVectorReference<block_size,Alloc> > -{ -typedef BitSetVectorConstReference<block_size,Alloc> type; -}; - -template<int block_size, class Alloc> -struct const_reference< BitSetVectorConstReference<block_size,Alloc> > -{ -typedef BitSetVectorConstReference<block_size,Alloc> type; -}; - -template<int block_size, class Alloc> -struct mutable_reference< BitSetVectorReference<block_size,Alloc> > -{ -typedef BitSetVectorReference<block_size,Alloc> type; -}; - -template<int block_size, class Alloc> -struct mutable_reference< BitSetVectorConstReference<block_size,Alloc> > -{ -typedef BitSetVectorReference<block_size,Alloc> type; -}; - -/** -\brief A dynamic %array of blocks of booleans -*/ -template <int block_size, class Allocator=std::allocator<bool> > -class BitSetVector : private std::vector<bool, Allocator> -{ -/** \brief The implementation class: an unblocked bitfield */ -typedef std::vector<bool, Allocator> BlocklessBaseClass; - -public: -//! container interface typedefs -//! \{ - -/** \brief Type of the values stored by the container */ -typedef std::bitset<block_size> value_type; - -/** \brief Reference to a small block of bits */ -typedef BitSetVectorReference<block_size,Allocator> reference; - -/** \brief Const reference to a small block of bits */ -typedef BitSetVectorConstReference<block_size,Allocator> const_reference; - -/** \brief Pointer to a small block of bits */ -typedef BitSetVectorReference<block_size,Allocator>* pointer; - -/** \brief Const pointer to a small block of bits */ -typedef BitSetVectorConstReference<block_size,Allocator>* const_pointer; - -/** \brief size type */ -typedef typename std::vector<bool, Allocator>::size_type size_type; - -/** \brief The type of the allocator */ -typedef Allocator allocator_type; -//! \} - -//! iterators -//! \{ -typedef Dune::GenericIterator<BitSetVector<block_size,Allocator>, value_type, reference, std::ptrdiff_t, ForwardIteratorFacade> iterator; -typedef Dune::GenericIterator<const BitSetVector<block_size,Allocator>, const value_type, const_reference, std::ptrdiff_t, ForwardIteratorFacade> const_iterator; -//! \} - -//! Returns a iterator pointing to the beginning of the vector. -iterator begin(){ -return iterator(*this, 0); -} - -//! Returns a const_iterator pointing to the beginning of the vector. -const_iterator begin() const { -return const_iterator(*this, 0); -} - -//! Returns an iterator pointing to the end of the vector. -iterator end(){ -return iterator(*this, size()); -} - -//! Returns a const_iterator pointing to the end of the vector. -const_iterator end() const { -return const_iterator(*this, size()); -} - -//! Default constructor -BitSetVector() : -BlocklessBaseClass() -{} - -//! Construction from an unblocked bitfield -BitSetVector(const BlocklessBaseClass& blocklessBitField) : -BlocklessBaseClass(blocklessBitField) -{ -if (blocklessBitField.size()%block_size != 0) -DUNE_THROW(RangeError, "Vector size is not a multiple of the block size!"); -} - -/** Constructor with a given length -\param n Number of blocks -*/ -explicit BitSetVector(int n) : -BlocklessBaseClass(n*block_size) -{} - -//! Constructor which initializes the field with true or false -BitSetVector(int n, bool v) : -BlocklessBaseClass(n*block_size,v) -{} - -//! Erases all of the elements. -void clear() -{ -BlocklessBaseClass::clear(); -} - -//! Resize field -void resize(int n, bool v = bool()) -{ -BlocklessBaseClass::resize(n*block_size, v); -} - -/** \brief Return the number of blocks */ -size_type size() const -{ -return BlocklessBaseClass::size()/block_size; -} - -//! Sets all entries to <tt> true </tt> -void setAll() { -this->assign(BlocklessBaseClass::size(), true); -} - -//! Sets all entries to <tt> false </tt> -void unsetAll() { -this->assign(BlocklessBaseClass::size(), false); -} - -/** \brief Return reference to i-th block */ -reference operator[](int i) -{ -return reference(*this, i); -} - -/** \brief Return const reference to i-th block */ -const_reference operator[](int i) const -{ -return const_reference(*this, i); -} - -/** \brief Return reference to last block */ -reference back() -{ -return reference(*this, size()-1); -} - -/** \brief Return const reference to last block */ -const_reference back() const -{ -return const_reference(*this, size()-1); -} - -//! Returns the number of bits that are set. -size_type count() const -{ -return std::count(BlocklessBaseClass::begin(), BlocklessBaseClass::end(), true); -} - -//! Returns the number of set bits, while each block is masked with 1<<i -size_type countmasked(int j) const -{ -size_type n = 0; -size_type blocks = size(); -for(size_type i=0; i<blocks; ++i) -n += getBit(i,j); -return n; -} - -//! Send bitfield to an output stream -friend std::ostream& operator<< (std::ostream& s, const BitSetVector& v) -{ -for (size_t i=0; i<v.size(); i++) -s << v[i] << " "; -return s; -} - -private: - -//! Get a representation as value_type -value_type getRepr(int i) const -{ -value_type bits; -for(int j=0; j<block_size; ++j) -bits.set(j, getBit(i,j)); -return bits; -} - -typename std::vector<bool>::reference getBit(size_type i, size_type j) { -DUNE_ASSERT_BOUNDS(j < block_size); -DUNE_ASSERT_BOUNDS(i < size()); -return BlocklessBaseClass::operator[](i*block_size+j); -} - -typename std::vector<bool>::const_reference getBit(size_type i, size_type j) const { -DUNE_ASSERT_BOUNDS(j < block_size); -DUNE_ASSERT_BOUNDS(i < size()); -return BlocklessBaseClass::operator[](i*block_size+j); -} - -friend class BitSetVectorReference<block_size,Allocator>; -friend class BitSetVectorConstReference<block_size,Allocator>; -}; + ; + + public: + + //! Bitwise exclusive or (for BitSetVectorConstReference and BitSetVectorReference) + BitSetVectorReference& operator^=(const BitSetVectorConstReference& x) + { + // This uses the helper from above to hoist the actual XOR computation out of the function for + // the buggy version of GCC. + for (size_type i=0; i<block_size; i++) + getBit(i) = xor_helper(test(i),x.test(i)); + return *this; + } + + //! Left shift. + BitSetVectorReference& operator<<=(size_type n) + { + for (size_type i=0; i<block_size-n; i++) + getBit(i) = test(i+n); + return *this; + } + + //! Right shift. + BitSetVectorReference& operator>>=(size_type n) + { + for (size_type i=0; i<block_size-n; i++) + getBit(i+n) = test(i); + return *this; + } + + //! Sets every bit. + BitSetVectorReference& set() + { + for (size_type i=0; i<block_size; i++) + set(i); + return *this; + } + + //! Flips the value of every bit. + BitSetVectorReference& flip() + { + for (size_type i=0; i<block_size; i++) + flip(i); + return *this; + } + + //! Clears every bit. + BitSetVectorReference& reset() + { + *this = false; + return *this; + } + + //! Sets bit n if val is nonzero, and clears bit n if val is zero. + BitSetVectorReference& set(size_type n, int val = 1) + { + getBit(n) = val; + return *this; + } + + //! Clears bit n. + BitSetVectorReference& reset(size_type n) + { + set(n, false); + return *this; + } + + //! Flips bit n. + BitSetVectorReference& flip(size_type n) + { + getBit(n).flip(); + return *this; + } + + using BitSetVectorConstReference::test; + using BitSetVectorConstReference::operator[]; + + //! Return reference to the `i`-th bit + reference operator[](size_type i) + { + return getBit(i); + } + + protected: + BitSetVector& blockBitField; + + using BitSetVectorConstReference::getBit; + + reference getBit(size_type i) + { + return blockBitField.getBit(this->block_number,i); + } + }; + + // implementation of helper - I put it into the template to avoid having + // to compile it in a dedicated compilation unit + template<int block_size, class Alloc> + bool BitSetVectorReference<block_size,Alloc>::xor_helper(bool a, bool b) + { + return a ^ b; + } + + /** + typetraits for BitSetVectorReference + */ + template<int block_size, class Alloc> + struct const_reference< BitSetVectorReference<block_size,Alloc> > + { + typedef BitSetVectorConstReference<block_size,Alloc> type; + }; + + template<int block_size, class Alloc> + struct const_reference< BitSetVectorConstReference<block_size,Alloc> > + { + typedef BitSetVectorConstReference<block_size,Alloc> type; + }; + + template<int block_size, class Alloc> + struct mutable_reference< BitSetVectorReference<block_size,Alloc> > + { + typedef BitSetVectorReference<block_size,Alloc> type; + }; + + template<int block_size, class Alloc> + struct mutable_reference< BitSetVectorConstReference<block_size,Alloc> > + { + typedef BitSetVectorReference<block_size,Alloc> type; + }; + + /** + \brief A dynamic %array of blocks of booleans + */ + template <int block_size, class Allocator=std::allocator<bool> > + class BitSetVector : private std::vector<bool, Allocator> + { + /** \brief The implementation class: an unblocked bitfield */ + typedef std::vector<bool, Allocator> BlocklessBaseClass; + + public: + //! container interface typedefs + //! \{ + + /** \brief Type of the values stored by the container */ + typedef std::bitset<block_size> value_type; + + /** \brief Reference to a small block of bits */ + typedef BitSetVectorReference<block_size,Allocator> reference; + + /** \brief Const reference to a small block of bits */ + typedef BitSetVectorConstReference<block_size,Allocator> const_reference; + + /** \brief Pointer to a small block of bits */ + typedef BitSetVectorReference<block_size,Allocator>* pointer; + + /** \brief Const pointer to a small block of bits */ + typedef BitSetVectorConstReference<block_size,Allocator>* const_pointer; + + /** \brief size type */ + typedef typename std::vector<bool, Allocator>::size_type size_type; + + /** \brief The type of the allocator */ + typedef Allocator allocator_type; + //! \} + + //! iterators + //! \{ + typedef Dune::GenericIterator<BitSetVector<block_size,Allocator>, value_type, reference, std::ptrdiff_t, ForwardIteratorFacade> iterator; + typedef Dune::GenericIterator<const BitSetVector<block_size,Allocator>, const value_type, const_reference, std::ptrdiff_t, ForwardIteratorFacade> const_iterator; + //! \} + + //! Returns a iterator pointing to the beginning of the vector. + iterator begin(){ + return iterator(*this, 0); + } + + //! Returns a const_iterator pointing to the beginning of the vector. + const_iterator begin() const { + return const_iterator(*this, 0); + } + + //! Returns an iterator pointing to the end of the vector. + iterator end(){ + return iterator(*this, size()); + } + + //! Returns a const_iterator pointing to the end of the vector. + const_iterator end() const { + return const_iterator(*this, size()); + } + + //! Default constructor + BitSetVector() : + BlocklessBaseClass() + {} + + //! Construction from an unblocked bitfield + BitSetVector(const BlocklessBaseClass& blocklessBitField) : + BlocklessBaseClass(blocklessBitField) + { + if (blocklessBitField.size()%block_size != 0) + DUNE_THROW(RangeError, "Vector size is not a multiple of the block size!"); + } + + /** Constructor with a given length + \param n Number of blocks + */ + explicit BitSetVector(int n) : + BlocklessBaseClass(n*block_size) + {} + + //! Constructor which initializes the field with true or false + BitSetVector(int n, bool v) : + BlocklessBaseClass(n*block_size,v) + {} + + //! Erases all of the elements. + void clear() + { + BlocklessBaseClass::clear(); + } + + //! Resize field + void resize(int n, bool v = bool()) + { + BlocklessBaseClass::resize(n*block_size, v); + } + + /** \brief Return the number of blocks */ + size_type size() const + { + return BlocklessBaseClass::size()/block_size; + } + + //! Sets all entries to <tt> true </tt> + void setAll() { + this->assign(BlocklessBaseClass::size(), true); + } + + //! Sets all entries to <tt> false </tt> + void unsetAll() { + this->assign(BlocklessBaseClass::size(), false); + } + + /** \brief Return reference to i-th block */ + reference operator[](int i) + { + return reference(*this, i); + } + + /** \brief Return const reference to i-th block */ + const_reference operator[](int i) const + { + return const_reference(*this, i); + } + + /** \brief Return reference to last block */ + reference back() + { + return reference(*this, size()-1); + } + + /** \brief Return const reference to last block */ + const_reference back() const + { + return const_reference(*this, size()-1); + } + + //! Returns the number of bits that are set. + size_type count() const + { + return std::count(BlocklessBaseClass::begin(), BlocklessBaseClass::end(), true); + } + + //! Returns the number of set bits, while each block is masked with 1<<i + size_type countmasked(int j) const + { + size_type n = 0; + size_type blocks = size(); + for(size_type i=0; i<blocks; ++i) + n += getBit(i,j); + return n; + } + + //! Send bitfield to an output stream + friend std::ostream& operator<< (std::ostream& s, const BitSetVector& v) + { + for (size_t i=0; i<v.size(); i++) + s << v[i] << " "; + return s; + } + + private: + + //! Get a representation as value_type + value_type getRepr(int i) const + { + value_type bits; + for(int j=0; j<block_size; ++j) + bits.set(j, getBit(i,j)); + return bits; + } + + typename std::vector<bool>::reference getBit(size_type i, size_type j) { + DUNE_ASSERT_BOUNDS(j < block_size); + DUNE_ASSERT_BOUNDS(i < size()); + return BlocklessBaseClass::operator[](i*block_size+j); + } + + typename std::vector<bool>::const_reference getBit(size_type i, size_type j) const { + DUNE_ASSERT_BOUNDS(j < block_size); + DUNE_ASSERT_BOUNDS(i < size()); + return BlocklessBaseClass::operator[](i*block_size+j); + } + + friend class BitSetVectorReference<block_size,Allocator>; + friend class BitSetVectorConstReference<block_size,Allocator>; + }; } // namespace Dune diff --git a/dune/common/boundschecking.hh b/dune/common/boundschecking.hh index f957500f159892d13afae0bf87dac0dc3133585a..de7006a81c9d0906f8aeccdbffe35f7f21e44406 100644 --- a/dune/common/boundschecking.hh +++ b/dune/common/boundschecking.hh @@ -5,32 +5,32 @@ #include <dune/common/exceptions.hh> /** -* \file -* \brief Macro for wrapping boundary checks -*/ + * \file + * \brief Macro for wrapping boundary checks + */ /** -* @addtogroup Common -* -* @{ -*/ + * @addtogroup Common + * + * @{ + */ #ifndef DUNE_ASSERT_BOUNDS #if defined(DUNE_CHECK_BOUNDS) || defined(DOXYGEN) /** -* \brief If `DUNE_CHECK_BOUNDS` is defined: check if condition -* \a cond holds; otherwise, do nothing. -* -* Meant to be used for conditions that assure writes and reads -* do not occur outside of memory limits or pre-defined patterns -* and related conditions. -*/ + * \brief If `DUNE_CHECK_BOUNDS` is defined: check if condition + * \a cond holds; otherwise, do nothing. + * + * Meant to be used for conditions that assure writes and reads + * do not occur outside of memory limits or pre-defined patterns + * and related conditions. + */ #define DUNE_ASSERT_BOUNDS(cond) \ -do { \ -if (!(cond)) \ -DUNE_THROW(Dune::RangeError, "Index out of bounds."); \ -} while (false) + do { \ + if (!(cond)) \ + DUNE_THROW(Dune::RangeError, "Index out of bounds."); \ + } while (false) #else #define DUNE_ASSERT_BOUNDS(cond) diff --git a/dune/common/classname.hh b/dune/common/classname.hh index 1122182a78b8850e35ff515deec5d291c9a20319..4fa62a39161eb5501a1d9aa990ad06f1529a13ef 100644 --- a/dune/common/classname.hh +++ b/dune/common/classname.hh @@ -5,9 +5,9 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief A free function to provide the demangled class name -* of a given object or type as a string -*/ + * \brief A free function to provide the demangled class name + * of a given object or type as a string + */ #include <cstdlib> #include <memory> @@ -21,57 +21,57 @@ namespace Dune { -namespace Impl { + namespace Impl { -inline std::string demangle(std::string name) -{ + inline std::string demangle(std::string name) + { #if HAVE_CXA_DEMANGLE -int status; -std::unique_ptr<char, void(*)(void*)> -demangled(abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), -std::free); -if( demangled ) -name = demangled.get(); + int status; + std::unique_ptr<char, void(*)(void*)> + demangled(abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), + std::free); + if( demangled ) + name = demangled.get(); #endif // #if HAVE_CXA_DEMANGLE -return name; -} -} + return name; + } + } -/** \brief Provide the demangled class name of a type T as a string */ -/* -* \ingroup CxxUtilities -*/ -template <class T> -std::string className () -{ -typedef typename std::remove_reference<T>::type TR; -std::string className = Impl::demangle( typeid( TR ).name() ); -if (std::is_const<TR>::value) -className += " const"; -if (std::is_volatile<TR>::value) -className += " volatile"; -if (std::is_lvalue_reference<T>::value) -className += "&"; -else if (std::is_rvalue_reference<T>::value) -className += "&&"; -return className; -} + /** \brief Provide the demangled class name of a type T as a string */ + /* + * \ingroup CxxUtilities + */ + template <class T> + std::string className () + { + typedef typename std::remove_reference<T>::type TR; + std::string className = Impl::demangle( typeid( TR ).name() ); + if (std::is_const<TR>::value) + className += " const"; + if (std::is_volatile<TR>::value) + className += " volatile"; + if (std::is_lvalue_reference<T>::value) + className += "&"; + else if (std::is_rvalue_reference<T>::value) + className += "&&"; + return className; + } -/** \brief Provide the demangled class name of a given object as a string */ -/* -* \ingroup CxxUtilities -*/ -template <class T> -std::string className ( T&& v) -{ -typedef typename std::remove_reference<T>::type TR; -std::string className = Impl::demangle( typeid(v).name() ); -if (std::is_const<TR>::value) -className += " const"; -if (std::is_volatile<TR>::value) -className += " volatile"; -return className; -} + /** \brief Provide the demangled class name of a given object as a string */ + /* + * \ingroup CxxUtilities + */ + template <class T> + std::string className ( T&& v) + { + typedef typename std::remove_reference<T>::type TR; + std::string className = Impl::demangle( typeid(v).name() ); + if (std::is_const<TR>::value) + className += " const"; + if (std::is_volatile<TR>::value) + className += " volatile"; + return className; + } } // namespace Dune #endif // DUNE_CLASSNAME_HH diff --git a/dune/common/concept.hh b/dune/common/concept.hh index 2a43759497d75de40de6b3d0b2cbdca45e3f0b3b..4f1c7af3b56d5379059942bf0f533b0327bc270f 100644 --- a/dune/common/concept.hh +++ b/dune/common/concept.hh @@ -14,44 +14,44 @@ #include <dune/common/std/type_traits.hh> /** -* \file -* -* \brief Infrastructure for concepts. -*/ + * \file + * + * \brief Infrastructure for concepts. + */ namespace Dune { /** -* \brief Namespace for concepts -* -* This namespace contains helper functions for -* concept definitions and the concept definitions -* themselves. -* -* \ingroup CxxConcepts -*/ + * \brief Namespace for concepts + * + * This namespace contains helper functions for + * concept definitions and the concept definitions + * themselves. + * + * \ingroup CxxConcepts + */ namespace Concept { /** -* \brief Base class for refined concepts. -* -* If a new concept should refine one or more existing concepts, -* this can be achieved by deriving the new concept from -* Refines<C1,...,CN> where C1, ..., CN are the concepts -* to be refined. If you want to refine several concepts -* they should all be put in a single Refines<...> base -* class. -* -* \tparam BaseConcepts The list of concepts to be refined. -* -* \ingroup CxxConcepts -*/ + * \brief Base class for refined concepts. + * + * If a new concept should refine one or more existing concepts, + * this can be achieved by deriving the new concept from + * Refines<C1,...,CN> where C1, ..., CN are the concepts + * to be refined. If you want to refine several concepts + * they should all be put in a single Refines<...> base + * class. + * + * \tparam BaseConcepts The list of concepts to be refined. + * + * \ingroup CxxConcepts + */ template<class... BaseConcepts> struct Refines { -typedef TypeList<BaseConcepts...> BaseConceptList; + typedef TypeList<BaseConcepts...> BaseConceptList; }; @@ -59,88 +59,88 @@ typedef TypeList<BaseConcepts...> BaseConceptList; namespace Impl { -// ############################################################################# -// # All functions following here are implementation details -// # for the models() function below. -// ############################################################################# - -// Forward declaration -template<class C, class... T> -constexpr bool models(); - - - -// Here is the implementation of the concept checking. -// The first two overloads do the magic for checking -// if the requirements of a concept are satisfied. -// The rest is just for checking base concepts in case -// of refinement. - -// This overload is present if type substitution for -// C::require(T...) is successful, i.e., if the T... -// matches the requirement of C. In this case this -// overload is selected because PriorityTag<1> -// is a better match for PrioriryTag<42> than -// PriorityTag<0> in the default overload. -template<class C, class... T, -decltype(std::declval<C>().require(std::declval<T>()...), 0) =0> -constexpr std::true_type matchesRequirement(PriorityTag<1>) -{ return {}; } - -// If the above overload is ruled out by SFINAE because -// the T... does not match the requirements of C, then -// this default overload drops in. -template<class C, class... T> -constexpr std::false_type matchesRequirement(PriorityTag<0>) -{ return {}; } - - - -// An empty list C of concepts is always matched by T... -template<class...T> -constexpr bool modelsConceptList(TypeList<>) -{ return true; } - -// A nonempty list C0,..,CN of concepts is modeled -// by T... if it models the concept C0 -// and all concepts in the list C1,..,CN. -template<class...T, class C0, class... CC> -constexpr bool modelsConceptList(TypeList<C0, CC...>) -{ return models<C0, T...>() and modelsConceptList<T...>(TypeList<CC...>()); } - - - -// If C is an unrefined concept, then T... models C -// if it matches the requirement of C. -template<class C, class... T> -constexpr bool modelsConcept(PriorityTag<0>) -{ return matchesRequirement<C, T...>(PriorityTag<42>()); } - -// If C is a refined concept, then T... models C -// if it matches the requirement of C and of -// all base concepts. -// -// This overload is used if C::BaseConceptList exists -// due to its higher priority. -template<class C, class... T, -decltype(typename C::BaseConceptList(), 0) = 0> -constexpr bool modelsConcept(PriorityTag<1>) -{ return matchesRequirement<C, T...>(PriorityTag<42>()) and modelsConceptList<T...>(typename C::BaseConceptList()); } - -// This is the full concept check. It's defined here in the -// implementation namespace with 'constexpr bool' return type -// because we need a forward declaration in order to use it -// internally above. -// -// The actual interface function can then call this one and -// return the result as std::integral_constant<bool,*> which -// does not allow for a forward declaration because the return -// type is deduced. -template<class C, class... T> -constexpr bool models() -{ -return modelsConcept<C, T...>(PriorityTag<42>()); -} + // ############################################################################# + // # All functions following here are implementation details + // # for the models() function below. + // ############################################################################# + + // Forward declaration + template<class C, class... T> + constexpr bool models(); + + + + // Here is the implementation of the concept checking. + // The first two overloads do the magic for checking + // if the requirements of a concept are satisfied. + // The rest is just for checking base concepts in case + // of refinement. + + // This overload is present if type substitution for + // C::require(T...) is successful, i.e., if the T... + // matches the requirement of C. In this case this + // overload is selected because PriorityTag<1> + // is a better match for PrioriryTag<42> than + // PriorityTag<0> in the default overload. + template<class C, class... T, + decltype(std::declval<C>().require(std::declval<T>()...), 0) =0> + constexpr std::true_type matchesRequirement(PriorityTag<1>) + { return {}; } + + // If the above overload is ruled out by SFINAE because + // the T... does not match the requirements of C, then + // this default overload drops in. + template<class C, class... T> + constexpr std::false_type matchesRequirement(PriorityTag<0>) + { return {}; } + + + + // An empty list C of concepts is always matched by T... + template<class...T> + constexpr bool modelsConceptList(TypeList<>) + { return true; } + + // A nonempty list C0,..,CN of concepts is modeled + // by T... if it models the concept C0 + // and all concepts in the list C1,..,CN. + template<class...T, class C0, class... CC> + constexpr bool modelsConceptList(TypeList<C0, CC...>) + { return models<C0, T...>() and modelsConceptList<T...>(TypeList<CC...>()); } + + + + // If C is an unrefined concept, then T... models C + // if it matches the requirement of C. + template<class C, class... T> + constexpr bool modelsConcept(PriorityTag<0>) + { return matchesRequirement<C, T...>(PriorityTag<42>()); } + + // If C is a refined concept, then T... models C + // if it matches the requirement of C and of + // all base concepts. + // + // This overload is used if C::BaseConceptList exists + // due to its higher priority. + template<class C, class... T, + decltype(typename C::BaseConceptList(), 0) = 0> + constexpr bool modelsConcept(PriorityTag<1>) + { return matchesRequirement<C, T...>(PriorityTag<42>()) and modelsConceptList<T...>(typename C::BaseConceptList()); } + + // This is the full concept check. It's defined here in the + // implementation namespace with 'constexpr bool' return type + // because we need a forward declaration in order to use it + // internally above. + // + // The actual interface function can then call this one and + // return the result as std::integral_constant<bool,*> which + // does not allow for a forward declaration because the return + // type is deduced. + template<class C, class... T> + constexpr bool models() + { + return modelsConcept<C, T...>(PriorityTag<42>()); + } } // namespace Dune::Concept::Impl @@ -151,38 +151,38 @@ return modelsConcept<C, T...>(PriorityTag<42>()); /** -* \brief Check if concept is modeled by given types -* -* This will check if the given concept is modeled by the given -* list of types. This is true if the list of types models all -* the base concepts that are refined by the given concept -* and if it satisfies all additional requirements of the latter. -* -* Notice that a concept may be defined for a list of interacting types. -* The function will check if the given list of types matches the requirements -* on the whole list. It does not check if each individual type in the list -* satisfies the concept. -* -* This concept check mechanism is inspired by the concept checking -* facility in Eric Nieblers range-v3. For more information please -* refer to the libraries project page https://github.com/ericniebler/range-v3 -* or this blog entry: http://ericniebler.com/2013/11/23/concept-checking-in-c11. -* In fact the interface provided here is almost exactly the same as in range-v3. -* However the implementation differs, because range-v3 uses its own meta-programming -* library whereas our implementation is more straight forward. -* -* The result is returned as std::integral_constant<bool, ...> which -* allows to nicely use this method with Hybrid::ifElse. -* -* \tparam C The concept to check -* \tparam T The list of type to check against the concept -* -* \ingroup CxxConcepts -*/ + * \brief Check if concept is modeled by given types + * + * This will check if the given concept is modeled by the given + * list of types. This is true if the list of types models all + * the base concepts that are refined by the given concept + * and if it satisfies all additional requirements of the latter. + * + * Notice that a concept may be defined for a list of interacting types. + * The function will check if the given list of types matches the requirements + * on the whole list. It does not check if each individual type in the list + * satisfies the concept. + * + * This concept check mechanism is inspired by the concept checking + * facility in Eric Nieblers range-v3. For more information please + * refer to the libraries project page https://github.com/ericniebler/range-v3 + * or this blog entry: http://ericniebler.com/2013/11/23/concept-checking-in-c11. + * In fact the interface provided here is almost exactly the same as in range-v3. + * However the implementation differs, because range-v3 uses its own meta-programming + * library whereas our implementation is more straight forward. + * + * The result is returned as std::integral_constant<bool, ...> which + * allows to nicely use this method with Hybrid::ifElse. + * + * \tparam C The concept to check + * \tparam T The list of type to check against the concept + * + * \ingroup CxxConcepts + */ template<class C, class... T> constexpr auto models() { -return Std::bool_constant<Concept::Impl::models<C, T...>()>(); + return Std::bool_constant<Concept::Impl::models<C, T...>()>(); } @@ -193,21 +193,21 @@ namespace Concept { namespace Impl { -// ############################################################################# -// # All functions following here are implementation details for the -// # for the tupleEntriesModel() function below. -// ############################################################################# - -template<class C, class Tuple> -struct TupleEntriesModelHelper -{ -template<class Accumulated, class T> -struct AccumulateFunctor -{ -using type = typename std::integral_constant<bool, Accumulated::value and models<C, T>()>; -}; -using Result = typename ReduceTuple<AccumulateFunctor, Tuple, std::true_type>::type; -}; + // ############################################################################# + // # All functions following here are implementation details for the + // # for the tupleEntriesModel() function below. + // ############################################################################# + + template<class C, class Tuple> + struct TupleEntriesModelHelper + { + template<class Accumulated, class T> + struct AccumulateFunctor + { + using type = typename std::integral_constant<bool, Accumulated::value and models<C, T>()>; + }; + using Result = typename ReduceTuple<AccumulateFunctor, Tuple, std::true_type>::type; + }; } // namespace Dune::Concept::Impl @@ -221,9 +221,9 @@ using Result = typename ReduceTuple<AccumulateFunctor, Tuple, std::true_type>::t template<class C, class Tuple> constexpr auto tupleEntriesModel() --> typename Impl::TupleEntriesModelHelper<C, Tuple>::Result + -> typename Impl::TupleEntriesModelHelper<C, Tuple>::Result { -return {}; + return {}; } // ############################################################################# @@ -237,14 +237,14 @@ return {}; template<bool b, typename std::enable_if<b, int>::type = 0> constexpr bool requireTrue() { -return true; + return true; } // Helper function for use in concept definitions. template<class C, class... T, typename std::enable_if<models<C, T...>(), int>::type = 0> constexpr bool requireConcept() { -return true; + return true; } // Helper function for use in concept definitions. @@ -252,7 +252,7 @@ return true; template<class C, class... T, typename std::enable_if<models<C, T...>(), int>::type = 0> constexpr bool requireConcept(T&&... /*t*/) { -return true; + return true; } // Helper function for use in concept definitions. @@ -260,25 +260,25 @@ return true; template<class C, class Tuple, typename std::enable_if<tupleEntriesModel<C, Tuple>(), int>::type = 0> constexpr bool requireConceptForTupleEntries() { -return true; + return true; } // Helper function for use in concept definitions. // If the first passed type is not convertible to the second, the concept will not be satisfied. template<class From, class To, -typename std::enable_if< std::is_convertible<From, To>::value, int>::type = 0> + typename std::enable_if< std::is_convertible<From, To>::value, int>::type = 0> constexpr bool requireConvertible() { -return true; + return true; } // Helper function for use in concept definitions. // If passed argument is not convertible to the first passed type, the concept will not be satisfied. template<class To, class From, -typename std::enable_if< std::is_convertible<From, To>::value, int>::type = 0> + typename std::enable_if< std::is_convertible<From, To>::value, int>::type = 0> constexpr bool requireConvertible(const From&) { -return true; + return true; } // Helper function for use in concept definitions. @@ -288,41 +288,41 @@ return true; template<typename T> constexpr bool requireType() { -return true; + return true; } // Helper function for use in concept definitions. // If first passed type is not a base class of second type, the concept will not be satisfied. template<class Base, class Derived, -typename std::enable_if< std::is_base_of<Base, Derived>::value, int>::type = 0> + typename std::enable_if< std::is_base_of<Base, Derived>::value, int>::type = 0> constexpr bool requireBaseOf() { -return true; + return true; } // Helper function for use in concept definitions. // If first passed type is not a base class of first arguments type, the concept will not be satisfied. template<class Base, class Derived, -typename std::enable_if< std::is_base_of<Base, Derived>::value, int>::type = 0> + typename std::enable_if< std::is_base_of<Base, Derived>::value, int>::type = 0> constexpr bool requireBaseOf(const Derived&) { -return true; + return true; } // Helper function for use in concept definitions. // If the passed types are not the same, the concept will not be satisfied. template<class A, class B, -typename std::enable_if< std::is_same<A, B>::value, int>::type = 0> + typename std::enable_if< std::is_same<A, B>::value, int>::type = 0> constexpr bool requireSameType() { -return true; + return true; } } // namespace Dune::Concept -/** @} */ + /** @} */ } // namespace Dune diff --git a/dune/common/conditional.hh b/dune/common/conditional.hh index 1f3c1127aacb02b8233c05c4465f6af76179c48f..c7dc5bf54d0aa11c1424d5a18d01128911fee1e5 100644 --- a/dune/common/conditional.hh +++ b/dune/common/conditional.hh @@ -5,29 +5,29 @@ namespace Dune { -/** \brief conditional evaluate - -sometimes call immediate if, evaluates to - -\code -if (b) -return v1; -else -return v2; -\endcode - -In contrast to if-then-else the cond function can also be -evaluated for vector valued SIMD data types, see simd.hh. - -\param b boolean value -\param v1 value of b==true -\param v2 value of b==false -*/ -template<typename T1, typename T2> -const T1 cond(bool b, const T1 & v1, const T2 & v2) -{ -return (b ? v1 : v2); -} + /** \brief conditional evaluate + + sometimes call immediate if, evaluates to + + \code + if (b) + return v1; + else + return v2; + \endcode + + In contrast to if-then-else the cond function can also be + evaluated for vector valued SIMD data types, see simd.hh. + + \param b boolean value + \param v1 value of b==true + \param v2 value of b==false + */ + template<typename T1, typename T2> + const T1 cond(bool b, const T1 & v1, const T2 & v2) + { + return (b ? v1 : v2); + } } // end namespace Dune diff --git a/dune/common/debugalign.hh b/dune/common/debugalign.hh index 2b5ac29f7fba6abe3b3cf8b568720d28e953f5ac..7a2e7015d18ae3c1a701af3b121061294c7c30f5 100644 --- a/dune/common/debugalign.hh +++ b/dune/common/debugalign.hh @@ -25,517 +25,517 @@ namespace Dune { -//! type of the handler called by `violatedAlignment()` -using ViolatedAlignmentHandler = -std::function<void(const char*, std::size_t, const void*)>; - -//! access the handler called by `violatedAlignment()` -/** -* This may be used to obtain the handler for the purpose of calling, or for -* saving it somewhere to restore it later. It may also be used to set the -* handler simply by assigning a new handler. Setting the handler races -* with other accesses. -*/ -ViolatedAlignmentHandler &violatedAlignmentHandler(); - -//! called when an alignment violation is detected -/** -* \p className Name of the class whose alignment was violated -* \p expectedAlignment The (over-)alignment that the class expected -* \p address The address the class actually found itself at. -* -* The main purpose of the function is to serve as a convenient breakpoint -* for debugging -- which is why we put it in an external compilation unit -* so it isn't inlined. -*/ -void violatedAlignment(const char *className, std::size_t expectedAlignment, -const void *address); - -//! check whether an address conforms to the given alignment -inline bool isAligned(const void *p, std::size_t align) -{ -// a more portable way to do this would be to abuse std::align(), but that -// isn't supported by g++-4.9 yet -return std::uintptr_t(p) % align == 0; -} - -//! CRTP base mixin class to check alignment -template<std::size_t align, class Impl> -class alignas(align) AlignedBase -{ -void checkAlignment() const -{ -auto pimpl = static_cast<const Impl*>(this); -if(!isAligned(pimpl, align)) -violatedAlignment(className<Impl>().c_str(), align, pimpl); -} -public: -AlignedBase() { checkAlignment(); } -AlignedBase(const AlignedBase &) { checkAlignment(); } -AlignedBase(AlignedBase &&) { checkAlignment(); } -~AlignedBase() { checkAlignment(); } - -AlignedBase& operator=(const AlignedBase &) = default; -AlignedBase& operator=(AlignedBase &&) = default; -}; - -//! an alignment large enough to trigger alignment errors -static constexpr auto debugAlignment = 2*alignof(std::max_align_t); - -namespace AlignedNumberImpl { - -template<class T, std::size_t align = debugAlignment> -class AlignedNumber; - -} // namespace AlignedNumberImpl - -using AlignedNumberImpl::AlignedNumber; - -//! align a value to a certain alignment -template<std::size_t align = debugAlignment, class T> -AlignedNumber<T, align> aligned(T value) { return { std::move(value) }; } - -// The purpose of this namespace is to move the `<cmath>` function overloads -// out of namespace `Dune`. This avoids problems where people called -// e.g. `sqrt(1.0)` inside the `Dune` namespace, without first doing `using -// std::sqrt;`. Without any `Dune::sqrt()`, such a use will find -// `::sqrt()`, but with `Dune::sqrt()` it will find only `Dune::sqrt()`, -// which does not have an overload for `double`. -namespace AlignedNumberImpl { - -//! aligned wrappers for arithmetic types -template<class T, std::size_t align> -class AlignedNumber -: public AlignedBase<align, AlignedNumber<T, align> > -{ -T value_; - -public: -AlignedNumber() = default; -AlignedNumber(T value) : value_(std::move(value)) {} -template<class U, std::size_t uAlign, -class = std::enable_if_t<(align >= uAlign) && -std::is_convertible<U, T>::value> > -AlignedNumber(const AlignedNumber<U, uAlign> &o) : value_(U(o)) {} - -// accessors -template<class U, -class = std::enable_if_t<std::is_convertible<T, U>::value> > -explicit operator U() const { return value_; } - -const T &value() const { return value_; } -T &value() { return value_; } - -// I/O -template<class charT, class Traits> -friend std::basic_istream<charT, Traits>& -operator>>(std::basic_istream<charT, Traits>& str, AlignedNumber &u) -{ -return str >> u.value_; -} - -template<class charT, class Traits> -friend std::basic_ostream<charT, Traits>& -operator<<(std::basic_ostream<charT, Traits>& str, -const AlignedNumber &u) -{ -return str << u.value_; -} - -// The trick with `template<class U = T, class = void_t<expr(U)> >` is -// needed because at least g++-4.9 seems to evaluates a default argument -// in `template<class = void_t<expr(T))> >` as soon as possible and will -// error out if `expr(T)` is invalid. E.g. for `expr(T)` = -// `decltype(--std::declval<T&>())`, instantiating `AlignedNumber<bool>` -// will result in an unrecoverable error (`--` cannot be applied to a -// `bool`). - -// Increment, decrement -template<class U = T, class = void_t<decltype(++std::declval<U&>())> > -AlignedNumber &operator++() { ++value_; return *this; } - -template<class U = T, class = void_t<decltype(--std::declval<U&>())> > -AlignedNumber &operator--() { --value_; return *this; } - -template<class U = T, class = void_t<decltype(std::declval<U&>()++)> > -decltype(auto) operator++(int) { return aligned<align>(value_++); } - -template<class U = T, class = void_t<decltype(std::declval<U&>()--)> > -decltype(auto) operator--(int) { return aligned<align>(value_--); } - -// unary operators -template<class U = T, -class = void_t<decltype(+std::declval<const U&>())> > -decltype(auto) operator+() const { return aligned<align>(+value_); } - -template<class U = T, -class = void_t<decltype(-std::declval<const U&>())> > -decltype(auto) operator-() const { return aligned<align>(-value_); } - -/* -* silence warnings from GCC about using `~` on a bool -* (when instantiated for T=bool) -*/ + //! type of the handler called by `violatedAlignment()` + using ViolatedAlignmentHandler = + std::function<void(const char*, std::size_t, const void*)>; + + //! access the handler called by `violatedAlignment()` + /** + * This may be used to obtain the handler for the purpose of calling, or for + * saving it somewhere to restore it later. It may also be used to set the + * handler simply by assigning a new handler. Setting the handler races + * with other accesses. + */ + ViolatedAlignmentHandler &violatedAlignmentHandler(); + + //! called when an alignment violation is detected + /** + * \p className Name of the class whose alignment was violated + * \p expectedAlignment The (over-)alignment that the class expected + * \p address The address the class actually found itself at. + * + * The main purpose of the function is to serve as a convenient breakpoint + * for debugging -- which is why we put it in an external compilation unit + * so it isn't inlined. + */ + void violatedAlignment(const char *className, std::size_t expectedAlignment, + const void *address); + + //! check whether an address conforms to the given alignment + inline bool isAligned(const void *p, std::size_t align) + { + // a more portable way to do this would be to abuse std::align(), but that + // isn't supported by g++-4.9 yet + return std::uintptr_t(p) % align == 0; + } + + //! CRTP base mixin class to check alignment + template<std::size_t align, class Impl> + class alignas(align) AlignedBase + { + void checkAlignment() const + { + auto pimpl = static_cast<const Impl*>(this); + if(!isAligned(pimpl, align)) + violatedAlignment(className<Impl>().c_str(), align, pimpl); + } + public: + AlignedBase() { checkAlignment(); } + AlignedBase(const AlignedBase &) { checkAlignment(); } + AlignedBase(AlignedBase &&) { checkAlignment(); } + ~AlignedBase() { checkAlignment(); } + + AlignedBase& operator=(const AlignedBase &) = default; + AlignedBase& operator=(AlignedBase &&) = default; + }; + + //! an alignment large enough to trigger alignment errors + static constexpr auto debugAlignment = 2*alignof(std::max_align_t); + + namespace AlignedNumberImpl { + + template<class T, std::size_t align = debugAlignment> + class AlignedNumber; + + } // namespace AlignedNumberImpl + + using AlignedNumberImpl::AlignedNumber; + + //! align a value to a certain alignment + template<std::size_t align = debugAlignment, class T> + AlignedNumber<T, align> aligned(T value) { return { std::move(value) }; } + + // The purpose of this namespace is to move the `<cmath>` function overloads + // out of namespace `Dune`. This avoids problems where people called + // e.g. `sqrt(1.0)` inside the `Dune` namespace, without first doing `using + // std::sqrt;`. Without any `Dune::sqrt()`, such a use will find + // `::sqrt()`, but with `Dune::sqrt()` it will find only `Dune::sqrt()`, + // which does not have an overload for `double`. + namespace AlignedNumberImpl { + + //! aligned wrappers for arithmetic types + template<class T, std::size_t align> + class AlignedNumber + : public AlignedBase<align, AlignedNumber<T, align> > + { + T value_; + + public: + AlignedNumber() = default; + AlignedNumber(T value) : value_(std::move(value)) {} + template<class U, std::size_t uAlign, + class = std::enable_if_t<(align >= uAlign) && + std::is_convertible<U, T>::value> > + AlignedNumber(const AlignedNumber<U, uAlign> &o) : value_(U(o)) {} + + // accessors + template<class U, + class = std::enable_if_t<std::is_convertible<T, U>::value> > + explicit operator U() const { return value_; } + + const T &value() const { return value_; } + T &value() { return value_; } + + // I/O + template<class charT, class Traits> + friend std::basic_istream<charT, Traits>& + operator>>(std::basic_istream<charT, Traits>& str, AlignedNumber &u) + { + return str >> u.value_; + } + + template<class charT, class Traits> + friend std::basic_ostream<charT, Traits>& + operator<<(std::basic_ostream<charT, Traits>& str, + const AlignedNumber &u) + { + return str << u.value_; + } + + // The trick with `template<class U = T, class = void_t<expr(U)> >` is + // needed because at least g++-4.9 seems to evaluates a default argument + // in `template<class = void_t<expr(T))> >` as soon as possible and will + // error out if `expr(T)` is invalid. E.g. for `expr(T)` = + // `decltype(--std::declval<T&>())`, instantiating `AlignedNumber<bool>` + // will result in an unrecoverable error (`--` cannot be applied to a + // `bool`). + + // Increment, decrement + template<class U = T, class = void_t<decltype(++std::declval<U&>())> > + AlignedNumber &operator++() { ++value_; return *this; } + + template<class U = T, class = void_t<decltype(--std::declval<U&>())> > + AlignedNumber &operator--() { --value_; return *this; } + + template<class U = T, class = void_t<decltype(std::declval<U&>()++)> > + decltype(auto) operator++(int) { return aligned<align>(value_++); } + + template<class U = T, class = void_t<decltype(std::declval<U&>()--)> > + decltype(auto) operator--(int) { return aligned<align>(value_--); } + + // unary operators + template<class U = T, + class = void_t<decltype(+std::declval<const U&>())> > + decltype(auto) operator+() const { return aligned<align>(+value_); } + + template<class U = T, + class = void_t<decltype(-std::declval<const U&>())> > + decltype(auto) operator-() const { return aligned<align>(-value_); } + + /* + * silence warnings from GCC about using `~` on a bool + * (when instantiated for T=bool) + */ #if __GNUC__ >= 7 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wbool-operation" #endif -template<class U = T, -class = void_t<decltype(~std::declval<const U&>())> > -decltype(auto) operator~() const { return aligned<align>(~value_); } + template<class U = T, + class = void_t<decltype(~std::declval<const U&>())> > + decltype(auto) operator~() const { return aligned<align>(~value_); } #if __GNUC__ >= 7 # pragma GCC diagnostic pop #endif -template<class U = T, -class = void_t<decltype(!std::declval<const U&>())> > -decltype(auto) operator!() const { return aligned<align>(!value_); } + template<class U = T, + class = void_t<decltype(!std::declval<const U&>())> > + decltype(auto) operator!() const { return aligned<align>(!value_); } -// assignment operators + // assignment operators #define DUNE_ASSIGN_OP(OP) \ -template<class U, std::size_t uAlign, \ -class = std::enable_if_t< \ -( uAlign <= align && \ -sizeof(std::declval<T&>() OP std::declval<U>()) ) \ -> > \ -AlignedNumber &operator OP(const AlignedNumber<U, uAlign> &u) \ -{ \ -value_ OP U(u); \ -return *this; \ -} \ -\ -template<class U, \ -class = void_t<decltype(std::declval<T&>() OP \ -std::declval<U>())> > \ -AlignedNumber &operator OP(const U &u) \ -{ \ -value_ OP u; \ -return *this; \ -} \ -\ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_ASSIGN_OP(+=); -DUNE_ASSIGN_OP(-=); - -DUNE_ASSIGN_OP(*=); -DUNE_ASSIGN_OP(/=); -DUNE_ASSIGN_OP(%=); - -DUNE_ASSIGN_OP(^=); -DUNE_ASSIGN_OP(&=); -DUNE_ASSIGN_OP(|=); - -DUNE_ASSIGN_OP(<<=); -DUNE_ASSIGN_OP(>>=); + template<class U, std::size_t uAlign, \ + class = std::enable_if_t< \ + ( uAlign <= align && \ + sizeof(std::declval<T&>() OP std::declval<U>()) ) \ + > > \ + AlignedNumber &operator OP(const AlignedNumber<U, uAlign> &u) \ + { \ + value_ OP U(u); \ + return *this; \ + } \ + \ + template<class U, \ + class = void_t<decltype(std::declval<T&>() OP \ + std::declval<U>())> > \ + AlignedNumber &operator OP(const U &u) \ + { \ + value_ OP u; \ + return *this; \ + } \ + \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_ASSIGN_OP(+=); + DUNE_ASSIGN_OP(-=); + + DUNE_ASSIGN_OP(*=); + DUNE_ASSIGN_OP(/=); + DUNE_ASSIGN_OP(%=); + + DUNE_ASSIGN_OP(^=); + DUNE_ASSIGN_OP(&=); + DUNE_ASSIGN_OP(|=); + + DUNE_ASSIGN_OP(<<=); + DUNE_ASSIGN_OP(>>=); #undef DUNE_ASSIGN_OP -}; + }; -// binary operators + // binary operators #define DUNE_BINARY_OP(OP) \ -template<class T, std::size_t tAlign, class U, std::size_t uAlign, \ -class = void_t<decltype(std::declval<T>() \ -OP std::declval<U>())> > \ -decltype(auto) \ -operator OP(const AlignedNumber<T, tAlign> &t, \ -const AlignedNumber<U, uAlign> &u) \ -{ \ -/* can't use std::max(); not constexpr */ \ -return aligned<(tAlign > uAlign ? tAlign : uAlign)>(T(t) OP U(u)); \ -} \ -\ -template<class T, class U, std::size_t uAlign, \ -class = void_t<decltype(std::declval<T>() \ -OP std::declval<U>())> > \ -decltype(auto) \ -operator OP(const T &t, const AlignedNumber<U, uAlign> &u) \ -{ \ -return aligned<uAlign>(t OP U(u)); \ -} \ -\ -template<class T, std::size_t tAlign, class U, \ -class = void_t<decltype(std::declval<T>() \ -OP std::declval<U>())> > \ -decltype(auto) \ -operator OP(const AlignedNumber<T, tAlign> &t, const U &u) \ -{ \ -return aligned<tAlign>(T(t) OP u); \ -} \ -\ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_BINARY_OP(+); -DUNE_BINARY_OP(-); - -DUNE_BINARY_OP(*); -DUNE_BINARY_OP(/); -DUNE_BINARY_OP(%); - -DUNE_BINARY_OP(^); -DUNE_BINARY_OP(&); -DUNE_BINARY_OP(|); - -DUNE_BINARY_OP(<<); -DUNE_BINARY_OP(>>); - -DUNE_BINARY_OP(==); -DUNE_BINARY_OP(!=); -DUNE_BINARY_OP(<); -DUNE_BINARY_OP(>); -DUNE_BINARY_OP(<=); -DUNE_BINARY_OP(>=); - -DUNE_BINARY_OP(&&); -DUNE_BINARY_OP(||); + template<class T, std::size_t tAlign, class U, std::size_t uAlign, \ + class = void_t<decltype(std::declval<T>() \ + OP std::declval<U>())> > \ + decltype(auto) \ + operator OP(const AlignedNumber<T, tAlign> &t, \ + const AlignedNumber<U, uAlign> &u) \ + { \ + /* can't use std::max(); not constexpr */ \ + return aligned<(tAlign > uAlign ? tAlign : uAlign)>(T(t) OP U(u)); \ + } \ + \ + template<class T, class U, std::size_t uAlign, \ + class = void_t<decltype(std::declval<T>() \ + OP std::declval<U>())> > \ + decltype(auto) \ + operator OP(const T &t, const AlignedNumber<U, uAlign> &u) \ + { \ + return aligned<uAlign>(t OP U(u)); \ + } \ + \ + template<class T, std::size_t tAlign, class U, \ + class = void_t<decltype(std::declval<T>() \ + OP std::declval<U>())> > \ + decltype(auto) \ + operator OP(const AlignedNumber<T, tAlign> &t, const U &u) \ + { \ + return aligned<tAlign>(T(t) OP u); \ + } \ + \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_OP(+); + DUNE_BINARY_OP(-); + + DUNE_BINARY_OP(*); + DUNE_BINARY_OP(/); + DUNE_BINARY_OP(%); + + DUNE_BINARY_OP(^); + DUNE_BINARY_OP(&); + DUNE_BINARY_OP(|); + + DUNE_BINARY_OP(<<); + DUNE_BINARY_OP(>>); + + DUNE_BINARY_OP(==); + DUNE_BINARY_OP(!=); + DUNE_BINARY_OP(<); + DUNE_BINARY_OP(>); + DUNE_BINARY_OP(<=); + DUNE_BINARY_OP(>=); + + DUNE_BINARY_OP(&&); + DUNE_BINARY_OP(||); #undef DUNE_BINARY_OP -////////////////////////////////////////////////////////////////////// -// -// Overloads for the functions provided by the standard library -// + ////////////////////////////////////////////////////////////////////// + // + // Overloads for the functions provided by the standard library + // #define DUNE_UNARY_FUNC(name) \ -template<class T, std::size_t align> \ -decltype(auto) name(const AlignedNumber<T, align> &u) \ -{ \ -using std::name; \ -return aligned<align>(name(T(u))); \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -// -// <cmath> functions -// - -// note: only unary functions are provided at the moment. Getting all the -// overloads right for functions with more than one argument is tricky. -// All <cmath> functions appear in the list below in the order they are -// listed in in the standard, but the unimplemented ones are commented -// out. - -// note: abs is provided by both <cstdlib> (for integer) and <cmath> (for -// floating point). This overload works for both. -DUNE_UNARY_FUNC(abs); -DUNE_UNARY_FUNC(acos); -DUNE_UNARY_FUNC(acosh); -DUNE_UNARY_FUNC(asin); -DUNE_UNARY_FUNC(asinh); -DUNE_UNARY_FUNC(atan); -// atan2 -DUNE_UNARY_FUNC(atanh); -DUNE_UNARY_FUNC(cbrt); -DUNE_UNARY_FUNC(ceil); -// copysign -DUNE_UNARY_FUNC(cos); -DUNE_UNARY_FUNC(cosh); -DUNE_UNARY_FUNC(erf); -DUNE_UNARY_FUNC(erfc); -DUNE_UNARY_FUNC(exp); -DUNE_UNARY_FUNC(exp2); -DUNE_UNARY_FUNC(expm1); -DUNE_UNARY_FUNC(fabs); -// fdim -DUNE_UNARY_FUNC(floor); -// fma -// fmax -// fmin -// fmod -// frexp -// hypos -DUNE_UNARY_FUNC(ilogb); -// ldexp -DUNE_UNARY_FUNC(lgamma); -DUNE_UNARY_FUNC(llrint); -DUNE_UNARY_FUNC(llround); -DUNE_UNARY_FUNC(log); -DUNE_UNARY_FUNC(log10); -DUNE_UNARY_FUNC(log1p); -DUNE_UNARY_FUNC(log2); -DUNE_UNARY_FUNC(logb); -DUNE_UNARY_FUNC(lrint); -DUNE_UNARY_FUNC(lround); -// modf -DUNE_UNARY_FUNC(nearbyint); -// nextafter -// nexttoward -// pow -// remainder -// remquo -DUNE_UNARY_FUNC(rint); -DUNE_UNARY_FUNC(round); -// scalbln -// scalbn -DUNE_UNARY_FUNC(sin); -DUNE_UNARY_FUNC(sinh); -DUNE_UNARY_FUNC(sqrt); -DUNE_UNARY_FUNC(tan); -DUNE_UNARY_FUNC(tanh); -DUNE_UNARY_FUNC(tgamma); -DUNE_UNARY_FUNC(trunc); - -DUNE_UNARY_FUNC(isfinite); -DUNE_UNARY_FUNC(isinf); -DUNE_UNARY_FUNC(isnan); -DUNE_UNARY_FUNC(isnormal); -DUNE_UNARY_FUNC(signbit); - -// isgreater -// isgreaterequal -// isless -// islessequal -// islessgreater -// isunordered - -// -// <complex> functions -// - -// not all functions are implemented, and unlike for <cmath>, no -// comprehensive list is provided -DUNE_UNARY_FUNC(real); + template<class T, std::size_t align> \ + decltype(auto) name(const AlignedNumber<T, align> &u) \ + { \ + using std::name; \ + return aligned<align>(name(T(u))); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + // + // <cmath> functions + // + + // note: only unary functions are provided at the moment. Getting all the + // overloads right for functions with more than one argument is tricky. + // All <cmath> functions appear in the list below in the order they are + // listed in in the standard, but the unimplemented ones are commented + // out. + + // note: abs is provided by both <cstdlib> (for integer) and <cmath> (for + // floating point). This overload works for both. + DUNE_UNARY_FUNC(abs); + DUNE_UNARY_FUNC(acos); + DUNE_UNARY_FUNC(acosh); + DUNE_UNARY_FUNC(asin); + DUNE_UNARY_FUNC(asinh); + DUNE_UNARY_FUNC(atan); + // atan2 + DUNE_UNARY_FUNC(atanh); + DUNE_UNARY_FUNC(cbrt); + DUNE_UNARY_FUNC(ceil); + // copysign + DUNE_UNARY_FUNC(cos); + DUNE_UNARY_FUNC(cosh); + DUNE_UNARY_FUNC(erf); + DUNE_UNARY_FUNC(erfc); + DUNE_UNARY_FUNC(exp); + DUNE_UNARY_FUNC(exp2); + DUNE_UNARY_FUNC(expm1); + DUNE_UNARY_FUNC(fabs); + // fdim + DUNE_UNARY_FUNC(floor); + // fma + // fmax + // fmin + // fmod + // frexp + // hypos + DUNE_UNARY_FUNC(ilogb); + // ldexp + DUNE_UNARY_FUNC(lgamma); + DUNE_UNARY_FUNC(llrint); + DUNE_UNARY_FUNC(llround); + DUNE_UNARY_FUNC(log); + DUNE_UNARY_FUNC(log10); + DUNE_UNARY_FUNC(log1p); + DUNE_UNARY_FUNC(log2); + DUNE_UNARY_FUNC(logb); + DUNE_UNARY_FUNC(lrint); + DUNE_UNARY_FUNC(lround); + // modf + DUNE_UNARY_FUNC(nearbyint); + // nextafter + // nexttoward + // pow + // remainder + // remquo + DUNE_UNARY_FUNC(rint); + DUNE_UNARY_FUNC(round); + // scalbln + // scalbn + DUNE_UNARY_FUNC(sin); + DUNE_UNARY_FUNC(sinh); + DUNE_UNARY_FUNC(sqrt); + DUNE_UNARY_FUNC(tan); + DUNE_UNARY_FUNC(tanh); + DUNE_UNARY_FUNC(tgamma); + DUNE_UNARY_FUNC(trunc); + + DUNE_UNARY_FUNC(isfinite); + DUNE_UNARY_FUNC(isinf); + DUNE_UNARY_FUNC(isnan); + DUNE_UNARY_FUNC(isnormal); + DUNE_UNARY_FUNC(signbit); + + // isgreater + // isgreaterequal + // isless + // islessequal + // islessgreater + // isunordered + + // + // <complex> functions + // + + // not all functions are implemented, and unlike for <cmath>, no + // comprehensive list is provided + DUNE_UNARY_FUNC(real); #undef DUNE_UNARY_FUNC -// We need to overload min() and max() since they require types to be -// LessThanComparable, which requires `a<b` to be "convertible to bool". -// That wording seems to be a leftover from C++03, and today is probably -// equivalent to "implicitly convertible". There is also issue 2114 -// <https://cplusplus.github.io/LWG/issue2114> in the standard (still open -// as of 2018-07-06), which strives to require both "implicitly" and -// "contextually" convertible -- plus a few other things. -// -// We do not want our debug type to automatically decay to the underlying -// type, so we do not want to make the conversion non-explicit. So the -// only option left is to overload min() and max(). - -template<class T, std::size_t align> -auto max(const AlignedNumber<T, align> &a, -const AlignedNumber<T, align> &b) -{ -using std::max; -return aligned<align>(max(T(a), T(b))); -} - -template<class T, std::size_t align> -auto max(const T &a, const AlignedNumber<T, align> &b) -{ -using std::max; -return aligned<align>(max(a, T(b))); -} - -template<class T, std::size_t align> -auto max(const AlignedNumber<T, align> &a, const T &b) -{ -using std::max; -return aligned<align>(max(T(a), b)); -} - -template<class T, std::size_t align> -auto min(const AlignedNumber<T, align> &a, -const AlignedNumber<T, align> &b) -{ -using std::min; -return aligned<align>(min(T(a), T(b))); -} - -template<class T, std::size_t align> -auto min(const T &a, const AlignedNumber<T, align> &b) -{ -using std::min; -return aligned<align>(min(a, T(b))); -} - -template<class T, std::size_t align> -auto min(const AlignedNumber<T, align> &a, const T &b) -{ -using std::min; -return aligned<align>(min(T(a), b)); -} - -} // namespace AlignedNumberImpl - -// SIMD-like functions from "conditional.hh" -template<class T, std::size_t align> -AlignedNumber<T, align> -cond(const AlignedNumber<bool, align> &b, -const AlignedNumber<T, align> &v1, const AlignedNumber<T, align> &v2) -{ -return b ? v1 : v2; -} - -// SIMD-like functions from "rangeutilities.hh" -template<class T, std::size_t align> -T max_value(const AlignedNumber<T, align>& val) -{ -return T(val); -} - -template<class T, std::size_t align> -T min_value(const AlignedNumber<T, align>& val) -{ -return T(val); -} - -template<std::size_t align> -bool any_true(const AlignedNumber<bool, align>& val) -{ -return bool(val); -} - -template<std::size_t align> -bool all_true(const AlignedNumber<bool, align>& val) -{ -return bool(val); -} - -// SIMD-like functionality from "simd/interface.hh" -namespace Simd { -namespace Overloads { - -template<class T, std::size_t align> -struct ScalarType<AlignedNumber<T, align> > { using type = T; }; - -template<class U, class T, std::size_t align> -struct RebindType<U, AlignedNumber<T, align> > { -using type = AlignedNumber<U, align>; -}; - -template<class T, std::size_t align> -struct LaneCount<AlignedNumber<T, align> > : index_constant<1> {}; - -template<class T, std::size_t align> -T& lane(ADLTag<5>, std::size_t l, AlignedNumber<T, align> &v) -{ -assert(l == 0); -return v.value(); -} - -template<class T, std::size_t align> -T lane(ADLTag<5>, std::size_t l, const AlignedNumber<T, align> &v) -{ -assert(l == 0); -return v.value(); -} - -template<class T, std::size_t align> -const AlignedNumber<T, align> & -cond(ADLTag<5>, AlignedNumber<bool, align> mask, -const AlignedNumber<T, align> &ifTrue, -const AlignedNumber<T, align> &ifFalse) -{ -return mask ? ifTrue : ifFalse; -} - -template<std::size_t align> -bool anyTrue(ADLTag<5>, const AlignedNumber<bool, align> &mask) -{ -return bool(mask); -} - -} // namespace Overloads - -} // namespace Simd + // We need to overload min() and max() since they require types to be + // LessThanComparable, which requires `a<b` to be "convertible to bool". + // That wording seems to be a leftover from C++03, and today is probably + // equivalent to "implicitly convertible". There is also issue 2114 + // <https://cplusplus.github.io/LWG/issue2114> in the standard (still open + // as of 2018-07-06), which strives to require both "implicitly" and + // "contextually" convertible -- plus a few other things. + // + // We do not want our debug type to automatically decay to the underlying + // type, so we do not want to make the conversion non-explicit. So the + // only option left is to overload min() and max(). + + template<class T, std::size_t align> + auto max(const AlignedNumber<T, align> &a, + const AlignedNumber<T, align> &b) + { + using std::max; + return aligned<align>(max(T(a), T(b))); + } + + template<class T, std::size_t align> + auto max(const T &a, const AlignedNumber<T, align> &b) + { + using std::max; + return aligned<align>(max(a, T(b))); + } + + template<class T, std::size_t align> + auto max(const AlignedNumber<T, align> &a, const T &b) + { + using std::max; + return aligned<align>(max(T(a), b)); + } + + template<class T, std::size_t align> + auto min(const AlignedNumber<T, align> &a, + const AlignedNumber<T, align> &b) + { + using std::min; + return aligned<align>(min(T(a), T(b))); + } + + template<class T, std::size_t align> + auto min(const T &a, const AlignedNumber<T, align> &b) + { + using std::min; + return aligned<align>(min(a, T(b))); + } + + template<class T, std::size_t align> + auto min(const AlignedNumber<T, align> &a, const T &b) + { + using std::min; + return aligned<align>(min(T(a), b)); + } + + } // namespace AlignedNumberImpl + + // SIMD-like functions from "conditional.hh" + template<class T, std::size_t align> + AlignedNumber<T, align> + cond(const AlignedNumber<bool, align> &b, + const AlignedNumber<T, align> &v1, const AlignedNumber<T, align> &v2) + { + return b ? v1 : v2; + } + + // SIMD-like functions from "rangeutilities.hh" + template<class T, std::size_t align> + T max_value(const AlignedNumber<T, align>& val) + { + return T(val); + } + + template<class T, std::size_t align> + T min_value(const AlignedNumber<T, align>& val) + { + return T(val); + } + + template<std::size_t align> + bool any_true(const AlignedNumber<bool, align>& val) + { + return bool(val); + } + + template<std::size_t align> + bool all_true(const AlignedNumber<bool, align>& val) + { + return bool(val); + } + + // SIMD-like functionality from "simd/interface.hh" + namespace Simd { + namespace Overloads { + + template<class T, std::size_t align> + struct ScalarType<AlignedNumber<T, align> > { using type = T; }; + + template<class U, class T, std::size_t align> + struct RebindType<U, AlignedNumber<T, align> > { + using type = AlignedNumber<U, align>; + }; + + template<class T, std::size_t align> + struct LaneCount<AlignedNumber<T, align> > : index_constant<1> {}; + + template<class T, std::size_t align> + T& lane(ADLTag<5>, std::size_t l, AlignedNumber<T, align> &v) + { + assert(l == 0); + return v.value(); + } + + template<class T, std::size_t align> + T lane(ADLTag<5>, std::size_t l, const AlignedNumber<T, align> &v) + { + assert(l == 0); + return v.value(); + } + + template<class T, std::size_t align> + const AlignedNumber<T, align> & + cond(ADLTag<5>, AlignedNumber<bool, align> mask, + const AlignedNumber<T, align> &ifTrue, + const AlignedNumber<T, align> &ifFalse) + { + return mask ? ifTrue : ifFalse; + } + + template<std::size_t align> + bool anyTrue(ADLTag<5>, const AlignedNumber<bool, align> &mask) + { + return bool(mask); + } + + } // namespace Overloads + + } // namespace Simd } // namespace Dune diff --git a/dune/common/debugallocator.hh b/dune/common/debugallocator.hh index 9c631a9af0d6d16196f35fe86262bf36271c8e6d..3776762d93506879455b79031cebfc745a5a9f70 100644 --- a/dune/common/debugallocator.hh +++ b/dune/common/debugallocator.hh @@ -29,326 +29,326 @@ namespace Dune { #ifndef DOXYGEN // hide implementation details from doxygen -namespace DebugMemory -{ + namespace DebugMemory + { -extern const std::ptrdiff_t page_size; + extern const std::ptrdiff_t page_size; -struct AllocationManager -{ -typedef std::size_t size_type; -typedef std::ptrdiff_t difference_type; -typedef void* pointer; + struct AllocationManager + { + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef void* pointer; -protected: -static void allocation_error(const char* msg); + protected: + static void allocation_error(const char* msg); -struct AllocationInfo; -friend struct AllocationInfo; + struct AllocationInfo; + friend struct AllocationInfo; #define ALLOCATION_ASSERT(A) { if (!(A)) \ -{ allocation_error("Assertion " # A " failed");\ -}\ -}; - -struct AllocationInfo -{ -AllocationInfo(const std::type_info & t) : type(&t) {} -const std::type_info * type; - -pointer page_ptr; -pointer ptr; -size_type pages; -size_type capacity; -size_type size; -bool not_free; + { allocation_error("Assertion " # A " failed");\ + }\ }; -typedef MallocAllocator<AllocationInfo> Alloc; -typedef std::vector<AllocationInfo, Alloc> AllocationList; -AllocationList allocation_list; - -private: -void memprotect(void* from, difference_type len, int prot) -{ + struct AllocationInfo + { + AllocationInfo(const std::type_info & t) : type(&t) {} + const std::type_info * type; + + pointer page_ptr; + pointer ptr; + size_type pages; + size_type capacity; + size_type size; + bool not_free; + }; + + typedef MallocAllocator<AllocationInfo> Alloc; + typedef std::vector<AllocationInfo, Alloc> AllocationList; + AllocationList allocation_list; + + private: + void memprotect(void* from, difference_type len, int prot) + { #if HAVE_SYS_MMAN_H && HAVE_MPROTECT -int result = mprotect(from, len, prot); -if (result == -1) -{ - -std::cerr << "ERROR: (" << result << ": " << strerror(result) << ")" << std::endl; -std::cerr << " Failed to "; -if (prot == PROT_NONE) -std::cerr << "protect "; -else -std::cerr << "unprotect "; -std::cerr << "memory range: " -<< from << ", " -<< static_cast<void*>( -static_cast<char*>(from) + len) -<< std::endl; -abort(); -} + int result = mprotect(from, len, prot); + if (result == -1) + { + + std::cerr << "ERROR: (" << result << ": " << strerror(result) << ")" << std::endl; + std::cerr << " Failed to "; + if (prot == PROT_NONE) + std::cerr << "protect "; + else + std::cerr << "unprotect "; + std::cerr << "memory range: " + << from << ", " + << static_cast<void*>( + static_cast<char*>(from) + len) + << std::endl; + abort(); + } #else -DUNE_UNUSED_PARAMETER(from); -DUNE_UNUSED_PARAMETER(len); -DUNE_UNUSED_PARAMETER(prot); -std::cerr << "WARNING: memory protection not available" << std::endl; + DUNE_UNUSED_PARAMETER(from); + DUNE_UNUSED_PARAMETER(len); + DUNE_UNUSED_PARAMETER(prot); + std::cerr << "WARNING: memory protection not available" << std::endl; #endif -} - -public: - -~AllocationManager () -{ -AllocationList::iterator it; -bool error = false; -for (it=allocation_list.begin(); it!=allocation_list.end(); it++) -{ -if (it->not_free) -{ -std::cerr << "ERROR: found memory chunk still in use: " << -it->capacity << " bytes at " << it->ptr << std::endl; -error = true; -} -munmap(it->page_ptr, it->pages * page_size); -} -if (error) -allocation_error("lost allocations"); -} - -template<typename T> -T* allocate(size_type n) -{ -// setup chunk info -AllocationInfo ai(typeid(T)); -ai.size = n; -ai.capacity = n * sizeof(T); -ai.pages = (ai.capacity) / page_size + 2; -ai.not_free = true; -size_type overlap = ai.capacity % page_size; -ai.page_ptr = mmap(NULL, ai.pages * page_size, -PROT_READ | PROT_WRITE, + } + + public: + + ~AllocationManager () + { + AllocationList::iterator it; + bool error = false; + for (it=allocation_list.begin(); it!=allocation_list.end(); it++) + { + if (it->not_free) + { + std::cerr << "ERROR: found memory chunk still in use: " << + it->capacity << " bytes at " << it->ptr << std::endl; + error = true; + } + munmap(it->page_ptr, it->pages * page_size); + } + if (error) + allocation_error("lost allocations"); + } + + template<typename T> + T* allocate(size_type n) + { + // setup chunk info + AllocationInfo ai(typeid(T)); + ai.size = n; + ai.capacity = n * sizeof(T); + ai.pages = (ai.capacity) / page_size + 2; + ai.not_free = true; + size_type overlap = ai.capacity % page_size; + ai.page_ptr = mmap(NULL, ai.pages * page_size, + PROT_READ | PROT_WRITE, #ifdef __APPLE__ -MAP_ANON | MAP_PRIVATE, + MAP_ANON | MAP_PRIVATE, #else -MAP_ANONYMOUS | MAP_PRIVATE, + MAP_ANONYMOUS | MAP_PRIVATE, #endif --1, 0); -if (MAP_FAILED == ai.page_ptr) -{ -throw std::bad_alloc(); -} -ai.ptr = static_cast<char*>(ai.page_ptr) + page_size - overlap; -// write protect memory behind the actual data -memprotect(static_cast<char*>(ai.page_ptr) + (ai.pages-1) * page_size, -page_size, -PROT_NONE); -// remember the chunk -allocation_list.push_back(ai); -// return the ptr -return static_cast<T*>(ai.ptr); -} - -template<typename T> -void deallocate(T* ptr, size_type n = 0) noexcept -{ -// compute page address -void* page_ptr = -static_cast<void*>( -(char*)(ptr) - ((std::uintptr_t)(ptr) % page_size)); -// search list -AllocationList::iterator it; -unsigned int i = 0; -for (it=allocation_list.begin(); it!=allocation_list.end(); it++, i++) -{ -if (it->page_ptr == page_ptr) -{ -// std::cout << "found memory_block in allocation " << i << std::endl; -// sanity checks -if (n != 0) -ALLOCATION_ASSERT(n == it->size); -ALLOCATION_ASSERT(ptr == it->ptr); -ALLOCATION_ASSERT(true == it->not_free); -ALLOCATION_ASSERT(typeid(T) == *(it->type)); -// free memory -it->not_free = false; + -1, 0); + if (MAP_FAILED == ai.page_ptr) + { + throw std::bad_alloc(); + } + ai.ptr = static_cast<char*>(ai.page_ptr) + page_size - overlap; + // write protect memory behind the actual data + memprotect(static_cast<char*>(ai.page_ptr) + (ai.pages-1) * page_size, + page_size, + PROT_NONE); + // remember the chunk + allocation_list.push_back(ai); + // return the ptr + return static_cast<T*>(ai.ptr); + } + + template<typename T> + void deallocate(T* ptr, size_type n = 0) noexcept + { + // compute page address + void* page_ptr = + static_cast<void*>( + (char*)(ptr) - ((std::uintptr_t)(ptr) % page_size)); + // search list + AllocationList::iterator it; + unsigned int i = 0; + for (it=allocation_list.begin(); it!=allocation_list.end(); it++, i++) + { + if (it->page_ptr == page_ptr) + { + // std::cout << "found memory_block in allocation " << i << std::endl; + // sanity checks + if (n != 0) + ALLOCATION_ASSERT(n == it->size); + ALLOCATION_ASSERT(ptr == it->ptr); + ALLOCATION_ASSERT(true == it->not_free); + ALLOCATION_ASSERT(typeid(T) == *(it->type)); + // free memory + it->not_free = false; #if DEBUG_ALLOCATOR_KEEP -// write protect old memory -memprotect(it->page_ptr, -(it->pages) * page_size, -PROT_NONE); + // write protect old memory + memprotect(it->page_ptr, + (it->pages) * page_size, + PROT_NONE); #else -// unprotect old memory -memprotect(it->page_ptr, -(it->pages) * page_size, -PROT_READ | PROT_WRITE); -munmap(it->page_ptr, it->pages * page_size); -// remove chunk info -allocation_list.erase(it); + // unprotect old memory + memprotect(it->page_ptr, + (it->pages) * page_size, + PROT_READ | PROT_WRITE); + munmap(it->page_ptr, it->pages * page_size); + // remove chunk info + allocation_list.erase(it); #endif -return; -} -} -allocation_error("memory block not found"); -} -}; + return; + } + } + allocation_error("memory block not found"); + } + }; #undef ALLOCATION_ASSERT -extern AllocationManager alloc_man; -} // end namespace DebugMemory + extern AllocationManager alloc_man; + } // end namespace DebugMemory #endif // DOXYGEN -template<class T> -class DebugAllocator; - -// specialize for void -template <> -class DebugAllocator<void> { -public: -typedef void* pointer; -typedef const void* const_pointer; -// reference to void members are impossible. -typedef void value_type; -template <class U> struct rebind { -typedef DebugAllocator<U> other; -}; -}; - -// actual implementation -/** -@ingroup Allocators -@brief Allocators implementation which performs different kind of memory checks - -We check: -- access past the end -- only free memory which was allocated with this allocator -- list allocated memory chunks still in use upon destruction of the allocator - -When defining DEBUG_ALLOCATOR_KEEP to 1, we also check -- double free -- access after free - -When defining DEBUG_NEW_DELETE >= 1, we -- overload new/delte -- use the Debug memory management for new/delete -- DEBUG_NEW_DELETE > 2 gives extensive debug output -*/ -template <class T> -class DebugAllocator { -public: -typedef std::size_t size_type; -typedef std::ptrdiff_t difference_type; -typedef T* pointer; -typedef const T* const_pointer; -typedef T& reference; -typedef const T& const_reference; -typedef T value_type; -template <class U> struct rebind { -typedef DebugAllocator<U> other; -}; - -//! create a new DebugAllocator -DebugAllocator() noexcept {} -//! copy construct from an other DebugAllocator, possibly for a different result type -template <class U> -DebugAllocator(const DebugAllocator<U>&) noexcept {} -//! cleanup this allocator -~DebugAllocator() noexcept {} - -pointer address(reference x) const -{ -return &x; -} -const_pointer address(const_reference x) const -{ -return &x; -} - -//! allocate n objects of type T -pointer allocate(size_type n, -DebugAllocator<void>::const_pointer hint = 0) -{ -DUNE_UNUSED_PARAMETER(hint); -return DebugMemory::alloc_man.allocate<T>(n); -} - -//! deallocate n objects of type T at address p -void deallocate(pointer p, size_type n) -{ -DebugMemory::alloc_man.deallocate<T>(p,n); -} - -//! max size for allocate -size_type max_size() const noexcept -{ -return size_type(-1) / sizeof(T); -} - -//! copy-construct an object of type T (i.e. make a placement new on p) -void construct(pointer p, const T& val) -{ -::new((void*)p)T(val); -} - -//! construct an object of type T from variadic parameters -template<typename ... Args> -void construct(pointer p, Args&&... args) -{ -::new((void *)p)T(std::forward<Args>(args) ...); -} - -//! destroy an object of type T (i.e. call the destructor) -void destroy(pointer p) -{ -p->~T(); -} -}; - -//! check whether allocators are equivalent -template<class T> -constexpr bool -operator==(const DebugAllocator<T> &, const DebugAllocator<T> &) -{ -return true; -} - -//! check whether allocators are not equivalent -template<class T> -constexpr bool -operator!=(const DebugAllocator<T> &, const DebugAllocator<T> &) -{ -return false; -} + template<class T> + class DebugAllocator; + + // specialize for void + template <> + class DebugAllocator<void> { + public: + typedef void* pointer; + typedef const void* const_pointer; + // reference to void members are impossible. + typedef void value_type; + template <class U> struct rebind { + typedef DebugAllocator<U> other; + }; + }; + + // actual implementation + /** + @ingroup Allocators + @brief Allocators implementation which performs different kind of memory checks + + We check: + - access past the end + - only free memory which was allocated with this allocator + - list allocated memory chunks still in use upon destruction of the allocator + + When defining DEBUG_ALLOCATOR_KEEP to 1, we also check + - double free + - access after free + + When defining DEBUG_NEW_DELETE >= 1, we + - overload new/delte + - use the Debug memory management for new/delete + - DEBUG_NEW_DELETE > 2 gives extensive debug output + */ + template <class T> + class DebugAllocator { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template <class U> struct rebind { + typedef DebugAllocator<U> other; + }; + + //! create a new DebugAllocator + DebugAllocator() noexcept {} + //! copy construct from an other DebugAllocator, possibly for a different result type + template <class U> + DebugAllocator(const DebugAllocator<U>&) noexcept {} + //! cleanup this allocator + ~DebugAllocator() noexcept {} + + pointer address(reference x) const + { + return &x; + } + const_pointer address(const_reference x) const + { + return &x; + } + + //! allocate n objects of type T + pointer allocate(size_type n, + DebugAllocator<void>::const_pointer hint = 0) + { + DUNE_UNUSED_PARAMETER(hint); + return DebugMemory::alloc_man.allocate<T>(n); + } + + //! deallocate n objects of type T at address p + void deallocate(pointer p, size_type n) + { + DebugMemory::alloc_man.deallocate<T>(p,n); + } + + //! max size for allocate + size_type max_size() const noexcept + { + return size_type(-1) / sizeof(T); + } + + //! copy-construct an object of type T (i.e. make a placement new on p) + void construct(pointer p, const T& val) + { + ::new((void*)p)T(val); + } + + //! construct an object of type T from variadic parameters + template<typename ... Args> + void construct(pointer p, Args&&... args) + { + ::new((void *)p)T(std::forward<Args>(args) ...); + } + + //! destroy an object of type T (i.e. call the destructor) + void destroy(pointer p) + { + p->~T(); + } + }; + + //! check whether allocators are equivalent + template<class T> + constexpr bool + operator==(const DebugAllocator<T> &, const DebugAllocator<T> &) + { + return true; + } + + //! check whether allocators are not equivalent + template<class T> + constexpr bool + operator!=(const DebugAllocator<T> &, const DebugAllocator<T> &) + { + return false; + } } #ifdef DEBUG_NEW_DELETE void * operator new(size_t size) { -// try to allocate size bytes -void *p = Dune::DebugMemory::alloc_man.allocate<char>(size); + // try to allocate size bytes + void *p = Dune::DebugMemory::alloc_man.allocate<char>(size); #if DEBUG_NEW_DELETE > 2 -std::cout << "NEW " << size -<< " -> " << p -<< std::endl; + std::cout << "NEW " << size + << " -> " << p + << std::endl; #endif -return p; + return p; } void operator delete(void * p) noexcept { #if DEBUG_NEW_DELETE > 2 -std::cout << "FREE " << p << std::endl; + std::cout << "FREE " << p << std::endl; #endif -Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p)); + Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p)); } void operator delete(void * p, size_t size) noexcept { #if DEBUG_NEW_DELETE > 2 -std::cout << "FREE " << p << std::endl; + std::cout << "FREE " << p << std::endl; #endif -Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p), size); + Dune::DebugMemory::alloc_man.deallocate<char>(static_cast<char*>(p), size); } #endif // DEBUG_NEW_DELETE diff --git a/dune/common/debugstream.hh b/dune/common/debugstream.hh index 48ee2ef1d347d989f5f774167f256aade34e1911..8e2b7550f048ac858c432b1beeb9fd709aa30c80 100644 --- a/dune/common/debugstream.hh +++ b/dune/common/debugstream.hh @@ -6,8 +6,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Defines several output streams for messages of different importance -*/ + * \brief Defines several output streams for messages of different importance + */ #include <iostream> #include <stack> @@ -16,420 +16,420 @@ namespace Dune { -/*! \defgroup DebugOut Debug output -\ingroup Common + /*! \defgroup DebugOut Debug output + \ingroup Common -The debug output is implemented by instances of DebugStream which -provides the following features: + The debug output is implemented by instances of DebugStream which + provides the following features: -- output-syntax in the standard ostream-notation -- output can be totally deactivated depending on template parameters -- streams with active output can be deactivated during runtime -- redirecting to std::ostream or other DebugStream s during runtime -- stack oriented state + - output-syntax in the standard ostream-notation + - output can be totally deactivated depending on template parameters + - streams with active output can be deactivated during runtime + - redirecting to std::ostream or other DebugStream s during runtime + - stack oriented state -The Dune-components should use the streams explained in \ref StdStreams -for output so that applications may redirect the output globally. + The Dune-components should use the streams explained in \ref StdStreams + for output so that applications may redirect the output globally. -Changes in runtime are provided by three sets of methods: + Changes in runtime are provided by three sets of methods: -- push()/pop() sets new activation flag or restore old setting -- attach()/detach() redirects output to a different std::ostream or restore old stream -- tie()/untie() redirects output through another DebugStream. If the state of the master stream changes (activation or output-stream) it is changed in the tied stream as well + - push()/pop() sets new activation flag or restore old setting + - attach()/detach() redirects output to a different std::ostream or restore old stream + - tie()/untie() redirects output through another DebugStream. If the state of the master stream changes (activation or output-stream) it is changed in the tied stream as well -The first methods implement a full stack whereas tie() is a bit -different: though a tied stream may be (de)activated via -push()/pop() you cannot attach() or detach() an output. You'll need -to change the master stream instead. + The first methods implement a full stack whereas tie() is a bit + different: though a tied stream may be (de)activated via + push()/pop() you cannot attach() or detach() an output. You'll need + to change the master stream instead. -\section DebugAppl Applications + \section DebugAppl Applications -Applications using the Dune-library should create an independent set -of DebugStreams so that the debug levels can be changed separately. -Example: + Applications using the Dune-library should create an independent set + of DebugStreams so that the debug levels can be changed separately. + Example: -\code -static const Dune::DebugLevel APPL_MINLEVEL = 3; + \code + static const Dune::DebugLevel APPL_MINLEVEL = 3; -Dune::DebugStream<1, APPL_MINLEVEL> myverbose; -Dune::DebugStream<2, APPL_MINLEVEL> myinfo; -Dune::DebugStream<3, APPL_MINLEVEL> mywarn; -\endcode + Dune::DebugStream<1, APPL_MINLEVEL> myverbose; + Dune::DebugStream<2, APPL_MINLEVEL> myinfo; + Dune::DebugStream<3, APPL_MINLEVEL> mywarn; + \endcode -This code creates three streams of which only the last one really -creates output. The output-routines of the other streams vanish in -optimized executables. + This code creates three streams of which only the last one really + creates output. The output-routines of the other streams vanish in + optimized executables. -You can use the common_bits-Template to switch to a policy using bitflags: + You can use the common_bits-Template to switch to a policy using bitflags: -\code -enum { APPL_CORE = 1, APPL_IO = 2, APPL_GRAPHICS = 4}; + \code + enum { APPL_CORE = 1, APPL_IO = 2, APPL_GRAPHICS = 4}; -static const Dune::DebugLevel APPL_DEBUG_MASK = APPL_CORE | APPL_GRAPHICS; -static const Dune::DebugLevel APPL_ACTIVE_MASK = 0xff; + static const Dune::DebugLevel APPL_DEBUG_MASK = APPL_CORE | APPL_GRAPHICS; + static const Dune::DebugLevel APPL_ACTIVE_MASK = 0xff; -Dune::DebugStream<APPL_CORE, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> coreout; -Dune::DebugStream<APPL_IO, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> ioout; -Dune::DebugStream<APPL_GRAPHICS, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> graphout; -\endcode + Dune::DebugStream<APPL_CORE, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> coreout; + Dune::DebugStream<APPL_IO, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> ioout; + Dune::DebugStream<APPL_GRAPHICS, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> graphout; + \endcode -Applications that wish to redirect the \ref StdStreams through their -private streams may use the tie()-mechanism: + Applications that wish to redirect the \ref StdStreams through their + private streams may use the tie()-mechanism: -\code -// initialize streams like above + \code + // initialize streams like above -Dune::dwarn.tie(coreout); + Dune::dwarn.tie(coreout); -// ... Dune-output to dwarn will be directed through coreout ... + // ... Dune-output to dwarn will be directed through coreout ... -Dune::dwarn.untie(); -\endcode + Dune::dwarn.untie(); + \endcode -Keep in mind to untie() a stream before the tied stream is destructed. + Keep in mind to untie() a stream before the tied stream is destructed. -An alternative is to attach() an output stream defined by the application: + An alternative is to attach() an output stream defined by the application: -\code -std::ofstream mylog("application.log"); + \code + std::ofstream mylog("application.log"); -Dune::dwarn.attach(mylog); -\endcode -*/ -/** -\addtogroup DebugOut -\{ -*/ -/*! \file + Dune::dwarn.attach(mylog); + \endcode + */ + /** + \addtogroup DebugOut + \{ + */ + /*! \file -This file implements the class DebugStream to support output in a -variety of debug levels. Additionally, template parameters control -if the output operation is really performed so that unused debug -levels can be deactivated + This file implements the class DebugStream to support output in a + variety of debug levels. Additionally, template parameters control + if the output operation is really performed so that unused debug + levels can be deactivated -*/ + */ -/*! \brief Type for debug levels. + /*! \brief Type for debug levels. -Only positive values allowed -*/ -typedef unsigned int DebugLevel; + Only positive values allowed + */ + typedef unsigned int DebugLevel; -/*! + /*! -\brief Greater or equal template test. + \brief Greater or equal template test. -value is false if current is below the threshold, true otherwise + value is false if current is below the threshold, true otherwise -This is the default struct to control the activation policy of -DebugStream and deactivates output below the threshold -*/ -template <DebugLevel current, DebugLevel threshold> -struct greater_or_equal { -static const bool value = (current >= threshold); -}; + This is the default struct to control the activation policy of + DebugStream and deactivates output below the threshold + */ + template <DebugLevel current, DebugLevel threshold> + struct greater_or_equal { + static const bool value = (current >= threshold); + }; -/*! \brief activate if current and mask have common bits switched on. + /*! \brief activate if current and mask have common bits switched on. -This template implements an alternative strategy to activate or -deactivate a DebugStream. Keep in mind to number your streams as -powers of two if using this template -*/ -template <DebugLevel current, DebugLevel mask> -struct common_bits { -enum {value = ((current & mask)!=0) }; -}; + This template implements an alternative strategy to activate or + deactivate a DebugStream. Keep in mind to number your streams as + powers of two if using this template + */ + template <DebugLevel current, DebugLevel mask> + struct common_bits { + enum {value = ((current & mask)!=0) }; + }; -//! \brief standard exception for the debugstream -class DebugStreamError : public IOError {}; - -class StreamWrap { -public: -StreamWrap(std::ostream& _out) : out(_out) { } -std::ostream& out; -StreamWrap *next; -}; - -//! \brief Intermediate class to implement tie-operation of DebugStream -class DebugStreamState { -// !!! should be protected somehow but that won't be easy -public: -//! \brief current output stream and link to possibly pushed old output streams -StreamWrap* current; - -//! \brief flag to switch output during runtime -bool _active; - -//! \brief are we tied to another DebugStream? -bool _tied; - -//! \brief how many streams are tied to this state -unsigned int _tied_streams; -}; - -/*! -\brief Generic class to implement debug output streams - -The main function of a DebugStream is to provide output in a -standard ostream fashion that is fully deactivated if the level of -the stream does not meet the current requirements. More information in \ref DebugOut - -\param thislevel this level -\param dlevel level needed for any output to happen -\param alevel level needed to switch activation flag on -\param activator template describing the activation policy - -\todo Fix visibility of internal data -*/ -template <DebugLevel thislevel = 1, -DebugLevel dlevel = 1, -DebugLevel alevel = 1, -template<DebugLevel, DebugLevel> class activator = greater_or_equal> -class DebugStream : public DebugStreamState { -public: -/*! \brief Create a DebugStream and set initial output stream - -during runtime another stream can be attach()ed, however the -initial stream may not be detach()ed. -*/ -DebugStream(std::ostream& out = std::cerr) { -// start a new list of streams -current = new StreamWrap(out); -current->next = 0; - -// check if we are above the default activation level -_active = activator<thislevel,alevel>::value; - -// we're not tied to another DebugStream -_tied = false; - -// no child streams yet -_tied_streams = 0; -} - -/*! \brief Create a DebugStream and directly tie to another DebugStream - -The fallback is used if a DebugStream constructed via this method -is untie()ed later. Otherwise the stream would be broken afterwards. -*/ -DebugStream (DebugStreamState& master, -std::ostream& fallback = std::cerr) -{ -// start a new list of streams -current = new StreamWrap(fallback); -current->next = 0; - -// check if we are above the default activation level -_active = activator<thislevel,alevel>::value; -_tied_streams = 0; - -// tie to the provided stream -_tied = true; -tiedstate = &master; -tiedstate->_tied_streams++; -} - -/*! \brief Destroy stream. - -If other streams still tie() to this stream the destructor -will call std::terminate() because you can hardly recover -from this problem and the child streams would certainly break on the -next output. -*/ -~DebugStream() -{ -// untie -if (_tied) -tiedstate->_tied_streams--; -else { -// check if somebody still ties to us... -if (_tied_streams != 0) -{ -std::cerr << "DebugStream destructor is called while other streams are still tied to it. Terminating!" << std::endl; -std::terminate(); -} -} - -// remove ostream-stack -while (current != 0) { -StreamWrap *s = current; -current = current->next; -delete s; -} -} - -//! \brief Generic types are passed on to current output stream -template <class T> -DebugStream& operator<<(const T data) { -// remove the following code if stream wasn't compiled active -if (activator<thislevel, dlevel>::value) { -if (! _tied) { -if (_active) -current->out << data; -} else { -if (_active && tiedstate->_active) -tiedstate->current->out << data; -} -} - -return *this; -} - -/*! \brief explicit specialization so that enums can be printed - -Operators for built-in types follow special -rules (§11.2.3) so that enums won't fit into the generic -method above. With an existing operator<< for int however -the enum will be automatically casted. -*/ -DebugStream& operator<<(const int data) { -// remove the following code if stream wasn't compiled active -if (activator<thislevel, dlevel>::value) { -if (! _tied) { -if (_active) -current->out << data; -} else { -if (_active && tiedstate->_active) -tiedstate->current->out << data; -} -} - -return *this; -} - -//! \brief pass on manipulators to underlying output stream -DebugStream& operator<<(std::ostream& (*f)(std::ostream&)) { -if (activator<thislevel, dlevel>::value) { -if (! _tied) { -if (_active) -f(current->out); -} else { -if (_active && tiedstate->_active) -f(tiedstate->current->out); -} -} - -return *this; -} - -//! \brief pass on flush to underlying output stream -DebugStream& flush() { -if (activator<thislevel, dlevel>::value) { -if (! _tied) { -if (_active) -current->out.flush(); -} else { -if (_active && tiedstate->_active) -tiedstate->current->out.flush(); -} -} - -return *this; -} - -//! \brief set activation flag and store old value -void push(bool b) { -// are we at all active? -if (activator<thislevel,alevel>::value) { -_actstack.push(_active); -_active = b; -} else { -// stay off -_actstack.push(false); -} -} - -/*! \brief restore previously set activation flag -* \throws DebugStreamError -*/ -void pop() { -if (_actstack.empty()) -DUNE_THROW(DebugStreamError, "No previous activation setting!"); - -_active = _actstack.top(); -_actstack.pop(); -} - -/*! \brief reports if this stream will produce output - -a DebugStream that is deactivated because of its level will always -return false, otherwise the state of the internal activation is -returned -*/ -bool active() const { -return activator<thislevel, dlevel>::value && _active; -} - -/*! \brief set output to a different stream. - -Old stream data is stored -*/ -void attach(std::ostream& stream) { -if (_tied) -DUNE_THROW(DebugStreamError, "Cannot attach to a tied stream!"); - -StreamWrap* newcurr = new StreamWrap(stream); -newcurr->next = current; -current = newcurr; -} - -/*! \brief detach current output stream and restore to previous stream -* \throws DebugStreamError -*/ -void detach() { -if (current->next == 0) -DUNE_THROW(DebugStreamError, "Cannot detach initial stream!"); -if (_tied) -DUNE_THROW(DebugStreamError, "Cannot detach a tied stream!"); - -StreamWrap* old = current; -current = current->next; -delete old; -} - -/*! \brief Tie a stream to this one. -* \throws DebugStreamError -*/ -void tie(DebugStreamState& to) { -if (to._tied) -DUNE_THROW(DebugStreamError, "Cannot tie to an already tied stream!"); -if (_tied) -DUNE_THROW(DebugStreamError, "Stream already tied: untie first!"); - -_tied = true; -tiedstate = &to; - -// tell master class -tiedstate->_tied_streams++; -} - -/*! \brief Untie stream -* \throws DebugStreamError -*/ -void untie() { -if(! _tied) -DUNE_THROW(DebugStreamError, "Cannot untie, stream is not tied!"); - -tiedstate->_tied_streams--; -_tied = false; -tiedstate = 0; -} - -private: -//! \brief pointer to data of stream we're tied to -DebugStreamState* tiedstate; - -/*! \brief Activation state history. - -store old activation settings so that the outside code doesn't -need to remember */ -std::stack<bool> _actstack; -}; - -/** /} */ + //! \brief standard exception for the debugstream + class DebugStreamError : public IOError {}; + + class StreamWrap { + public: + StreamWrap(std::ostream& _out) : out(_out) { } + std::ostream& out; + StreamWrap *next; + }; + + //! \brief Intermediate class to implement tie-operation of DebugStream + class DebugStreamState { + // !!! should be protected somehow but that won't be easy + public: + //! \brief current output stream and link to possibly pushed old output streams + StreamWrap* current; + + //! \brief flag to switch output during runtime + bool _active; + + //! \brief are we tied to another DebugStream? + bool _tied; + + //! \brief how many streams are tied to this state + unsigned int _tied_streams; + }; + + /*! + \brief Generic class to implement debug output streams + + The main function of a DebugStream is to provide output in a + standard ostream fashion that is fully deactivated if the level of + the stream does not meet the current requirements. More information in \ref DebugOut + + \param thislevel this level + \param dlevel level needed for any output to happen + \param alevel level needed to switch activation flag on + \param activator template describing the activation policy + + \todo Fix visibility of internal data + */ + template <DebugLevel thislevel = 1, + DebugLevel dlevel = 1, + DebugLevel alevel = 1, + template<DebugLevel, DebugLevel> class activator = greater_or_equal> + class DebugStream : public DebugStreamState { + public: + /*! \brief Create a DebugStream and set initial output stream + + during runtime another stream can be attach()ed, however the + initial stream may not be detach()ed. + */ + DebugStream(std::ostream& out = std::cerr) { + // start a new list of streams + current = new StreamWrap(out); + current->next = 0; + + // check if we are above the default activation level + _active = activator<thislevel,alevel>::value; + + // we're not tied to another DebugStream + _tied = false; + + // no child streams yet + _tied_streams = 0; + } + + /*! \brief Create a DebugStream and directly tie to another DebugStream + + The fallback is used if a DebugStream constructed via this method + is untie()ed later. Otherwise the stream would be broken afterwards. + */ + DebugStream (DebugStreamState& master, + std::ostream& fallback = std::cerr) + { + // start a new list of streams + current = new StreamWrap(fallback); + current->next = 0; + + // check if we are above the default activation level + _active = activator<thislevel,alevel>::value; + _tied_streams = 0; + + // tie to the provided stream + _tied = true; + tiedstate = &master; + tiedstate->_tied_streams++; + } + + /*! \brief Destroy stream. + + If other streams still tie() to this stream the destructor + will call std::terminate() because you can hardly recover + from this problem and the child streams would certainly break on the + next output. + */ + ~DebugStream() + { + // untie + if (_tied) + tiedstate->_tied_streams--; + else { + // check if somebody still ties to us... + if (_tied_streams != 0) + { + std::cerr << "DebugStream destructor is called while other streams are still tied to it. Terminating!" << std::endl; + std::terminate(); + } + } + + // remove ostream-stack + while (current != 0) { + StreamWrap *s = current; + current = current->next; + delete s; + } + } + + //! \brief Generic types are passed on to current output stream + template <class T> + DebugStream& operator<<(const T data) { + // remove the following code if stream wasn't compiled active + if (activator<thislevel, dlevel>::value) { + if (! _tied) { + if (_active) + current->out << data; + } else { + if (_active && tiedstate->_active) + tiedstate->current->out << data; + } + } + + return *this; + } + + /*! \brief explicit specialization so that enums can be printed + + Operators for built-in types follow special + rules (§11.2.3) so that enums won't fit into the generic + method above. With an existing operator<< for int however + the enum will be automatically casted. + */ + DebugStream& operator<<(const int data) { + // remove the following code if stream wasn't compiled active + if (activator<thislevel, dlevel>::value) { + if (! _tied) { + if (_active) + current->out << data; + } else { + if (_active && tiedstate->_active) + tiedstate->current->out << data; + } + } + + return *this; + } + + //! \brief pass on manipulators to underlying output stream + DebugStream& operator<<(std::ostream& (*f)(std::ostream&)) { + if (activator<thislevel, dlevel>::value) { + if (! _tied) { + if (_active) + f(current->out); + } else { + if (_active && tiedstate->_active) + f(tiedstate->current->out); + } + } + + return *this; + } + + //! \brief pass on flush to underlying output stream + DebugStream& flush() { + if (activator<thislevel, dlevel>::value) { + if (! _tied) { + if (_active) + current->out.flush(); + } else { + if (_active && tiedstate->_active) + tiedstate->current->out.flush(); + } + } + + return *this; + } + + //! \brief set activation flag and store old value + void push(bool b) { + // are we at all active? + if (activator<thislevel,alevel>::value) { + _actstack.push(_active); + _active = b; + } else { + // stay off + _actstack.push(false); + } + } + + /*! \brief restore previously set activation flag + * \throws DebugStreamError + */ + void pop() { + if (_actstack.empty()) + DUNE_THROW(DebugStreamError, "No previous activation setting!"); + + _active = _actstack.top(); + _actstack.pop(); + } + + /*! \brief reports if this stream will produce output + + a DebugStream that is deactivated because of its level will always + return false, otherwise the state of the internal activation is + returned + */ + bool active() const { + return activator<thislevel, dlevel>::value && _active; + } + + /*! \brief set output to a different stream. + + Old stream data is stored + */ + void attach(std::ostream& stream) { + if (_tied) + DUNE_THROW(DebugStreamError, "Cannot attach to a tied stream!"); + + StreamWrap* newcurr = new StreamWrap(stream); + newcurr->next = current; + current = newcurr; + } + + /*! \brief detach current output stream and restore to previous stream + * \throws DebugStreamError + */ + void detach() { + if (current->next == 0) + DUNE_THROW(DebugStreamError, "Cannot detach initial stream!"); + if (_tied) + DUNE_THROW(DebugStreamError, "Cannot detach a tied stream!"); + + StreamWrap* old = current; + current = current->next; + delete old; + } + + /*! \brief Tie a stream to this one. + * \throws DebugStreamError + */ + void tie(DebugStreamState& to) { + if (to._tied) + DUNE_THROW(DebugStreamError, "Cannot tie to an already tied stream!"); + if (_tied) + DUNE_THROW(DebugStreamError, "Stream already tied: untie first!"); + + _tied = true; + tiedstate = &to; + + // tell master class + tiedstate->_tied_streams++; + } + + /*! \brief Untie stream + * \throws DebugStreamError + */ + void untie() { + if(! _tied) + DUNE_THROW(DebugStreamError, "Cannot untie, stream is not tied!"); + + tiedstate->_tied_streams--; + _tied = false; + tiedstate = 0; + } + + private: + //! \brief pointer to data of stream we're tied to + DebugStreamState* tiedstate; + + /*! \brief Activation state history. + + store old activation settings so that the outside code doesn't + need to remember */ + std::stack<bool> _actstack; + }; + + /** /} */ } diff --git a/dune/common/densematrix.hh b/dune/common/densematrix.hh index ce47926ca0388561746c147f236bcc262033d342..78c54255cac892fb1806ece3386d73189d20049f 100644 --- a/dune/common/densematrix.hh +++ b/dune/common/densematrix.hh @@ -26,1241 +26,1241 @@ namespace Dune { -template<typename M> class DenseMatrix; - -template<typename M> -struct FieldTraits< DenseMatrix<M> > -{ -typedef const typename FieldTraits< typename DenseMatVecTraits<M>::value_type >::field_type field_type; -typedef const typename FieldTraits< typename DenseMatVecTraits<M>::value_type >::real_type real_type; -}; - -/** -work around a problem of FieldMatrix/FieldVector, -there is no unique way to obtain the size of a class - -\deprecated VectorSize is deprecated; please call the 'size()' method directly instead. -This will be removed after Dune 2.8. -*/ -template<class K, int N, int M> class FieldMatrix; -template<class K, int N> class FieldVector; -namespace { -template<class V> -struct [[deprecated("VectorSize is deprecated; please call the 'size()' method directly instead")]] VectorSize -{ -static typename V::size_type size(const V & v) { return v.size(); } -}; - -DUNE_NO_DEPRECATED_BEGIN -template<class K, int N> -struct [[deprecated("VectorSize is deprecated; please call the 'size()' method directly instead")]] VectorSize< const FieldVector<K,N> > -{ -typedef FieldVector<K,N> V; -static typename V::size_type size(const V & v) -{ -DUNE_UNUSED_PARAMETER(v); -return N; -} -}; -DUNE_NO_DEPRECATED_END -} - -/** -@addtogroup DenseMatVec -@{ -*/ - -/*! \file - -\brief Implements a matrix constructed from a given type -representing a field and a compile-time given number of rows and columns. -*/ - - - -/** -\brief you have to specialize this structure for any type that should be assignable to a DenseMatrix -\tparam DenseMatrix Some type implementing the dense matrix interface -\tparam RHS Right hand side type -*/ -template< class DenseMatrix, class RHS > -struct DenseMatrixAssigner; + template<typename M> class DenseMatrix; + + template<typename M> + struct FieldTraits< DenseMatrix<M> > + { + typedef const typename FieldTraits< typename DenseMatVecTraits<M>::value_type >::field_type field_type; + typedef const typename FieldTraits< typename DenseMatVecTraits<M>::value_type >::real_type real_type; + }; + + /** + work around a problem of FieldMatrix/FieldVector, + there is no unique way to obtain the size of a class + + \deprecated VectorSize is deprecated; please call the 'size()' method directly instead. + This will be removed after Dune 2.8. + */ + template<class K, int N, int M> class FieldMatrix; + template<class K, int N> class FieldVector; + namespace { + template<class V> + struct [[deprecated("VectorSize is deprecated; please call the 'size()' method directly instead")]] VectorSize + { + static typename V::size_type size(const V & v) { return v.size(); } + }; + + DUNE_NO_DEPRECATED_BEGIN + template<class K, int N> + struct [[deprecated("VectorSize is deprecated; please call the 'size()' method directly instead")]] VectorSize< const FieldVector<K,N> > + { + typedef FieldVector<K,N> V; + static typename V::size_type size(const V & v) + { + DUNE_UNUSED_PARAMETER(v); + return N; + } + }; + DUNE_NO_DEPRECATED_END + } + + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + + \brief Implements a matrix constructed from a given type + representing a field and a compile-time given number of rows and columns. + */ + + + + /** + \brief you have to specialize this structure for any type that should be assignable to a DenseMatrix + \tparam DenseMatrix Some type implementing the dense matrix interface + \tparam RHS Right hand side type + */ + template< class DenseMatrix, class RHS > + struct DenseMatrixAssigner; #ifndef DOXYGEN -namespace Impl -{ - -template< class DenseMatrix, class RHS, class = void > -class DenseMatrixAssigner -{}; - -template< class DenseMatrix, class RHS > -class DenseMatrixAssigner< DenseMatrix, RHS, std::enable_if_t< Dune::IsNumber< RHS >::value > > -{ -public: -static void apply ( DenseMatrix &denseMatrix, const RHS &rhs ) -{ -typedef typename DenseMatrix::field_type field_type; -std::fill( denseMatrix.begin(), denseMatrix.end(), static_cast< field_type >( rhs ) ); -} -}; - -template< class DenseMatrix, class RHS > -class DenseMatrixAssigner< DenseMatrix, RHS, std::enable_if_t< !std::is_same< typename RHS::const_iterator, void >::value -&& std::is_convertible< typename RHS::const_iterator::value_type, typename DenseMatrix::iterator::value_type >::value > > -{ -public: -static void apply ( DenseMatrix &denseMatrix, const RHS &rhs ) -{ -DUNE_ASSERT_BOUNDS(rhs.N() == denseMatrix.N()); -DUNE_ASSERT_BOUNDS(rhs.M() == denseMatrix.M()); -typename DenseMatrix::iterator tIt = std::begin(denseMatrix); -typename RHS::const_iterator sIt = std::begin(rhs); -for(; sIt != std::end(rhs); ++tIt, ++sIt) -std::copy(std::begin(*sIt), std::end(*sIt), std::begin(*tIt)); -} -}; - -} // namespace Impl - - - -template< class DenseMatrix, class RHS > -struct DenseMatrixAssigner -: public Impl::DenseMatrixAssigner< DenseMatrix, RHS > -{}; - - -namespace Impl -{ - -template< class DenseMatrix, class RHS > -std::true_type hasDenseMatrixAssigner ( DenseMatrix &, const RHS &, decltype( Dune::DenseMatrixAssigner< DenseMatrix, RHS >::apply( std::declval< DenseMatrix & >(), std::declval< const RHS & >() ) ) * = nullptr ); - -std::false_type hasDenseMatrixAssigner ( ... ); - -} // namespace Impl - -template< class DenseMatrix, class RHS > -struct HasDenseMatrixAssigner -: public decltype( Impl::hasDenseMatrixAssigner( std::declval< DenseMatrix & >(), std::declval< const RHS & >() ) ) -{}; - -#endif // #ifndef DOXYGEN - - - -/** @brief Error thrown if operations of a FieldMatrix fail. */ -class FMatrixError : public MathError {}; - -/** -@brief A dense n x m matrix. - -Matrices represent linear maps from a vector space V to a vector space W. -This class represents such a linear map by storing a two-dimensional -%array of numbers of a given field type K. The number of rows and -columns is given at compile time. - -\tparam MAT type of the matrix implementation -*/ -template<typename MAT> -class DenseMatrix -{ -typedef DenseMatVecTraits<MAT> Traits; + namespace Impl + { -// Curiously recurring template pattern -constexpr MAT & asImp() { return static_cast<MAT&>(*this); } -constexpr const MAT & asImp() const { return static_cast<const MAT&>(*this); } + template< class DenseMatrix, class RHS, class = void > + class DenseMatrixAssigner + {}; -template <class> -friend class DenseMatrix; + template< class DenseMatrix, class RHS > + class DenseMatrixAssigner< DenseMatrix, RHS, std::enable_if_t< Dune::IsNumber< RHS >::value > > + { + public: + static void apply ( DenseMatrix &denseMatrix, const RHS &rhs ) + { + typedef typename DenseMatrix::field_type field_type; + std::fill( denseMatrix.begin(), denseMatrix.end(), static_cast< field_type >( rhs ) ); + } + }; -public: -//===== type definitions and constants + template< class DenseMatrix, class RHS > + class DenseMatrixAssigner< DenseMatrix, RHS, std::enable_if_t< !std::is_same< typename RHS::const_iterator, void >::value + && std::is_convertible< typename RHS::const_iterator::value_type, typename DenseMatrix::iterator::value_type >::value > > + { + public: + static void apply ( DenseMatrix &denseMatrix, const RHS &rhs ) + { + DUNE_ASSERT_BOUNDS(rhs.N() == denseMatrix.N()); + DUNE_ASSERT_BOUNDS(rhs.M() == denseMatrix.M()); + typename DenseMatrix::iterator tIt = std::begin(denseMatrix); + typename RHS::const_iterator sIt = std::begin(rhs); + for(; sIt != std::end(rhs); ++tIt, ++sIt) + std::copy(std::begin(*sIt), std::end(*sIt), std::begin(*tIt)); + } + }; -//! type of derived matrix class -typedef typename Traits::derived_type derived_type; + } // namespace Impl -//! export the type representing the field -typedef typename Traits::value_type value_type; -//! export the type representing the field -typedef typename Traits::value_type field_type; -//! export the type representing the components -typedef typename Traits::value_type block_type; + template< class DenseMatrix, class RHS > + struct DenseMatrixAssigner + : public Impl::DenseMatrixAssigner< DenseMatrix, RHS > + {}; -//! The type used for the index access and size operation -typedef typename Traits::size_type size_type; -//! The type used to represent a row (must fulfill the Dune::DenseVector interface) -typedef typename Traits::row_type row_type; + namespace Impl + { -//! The type used to represent a reference to a row (usually row_type &) -typedef typename Traits::row_reference row_reference; + template< class DenseMatrix, class RHS > + std::true_type hasDenseMatrixAssigner ( DenseMatrix &, const RHS &, decltype( Dune::DenseMatrixAssigner< DenseMatrix, RHS >::apply( std::declval< DenseMatrix & >(), std::declval< const RHS & >() ) ) * = nullptr ); -//! The type used to represent a reference to a constant row (usually const row_type &) -typedef typename Traits::const_row_reference const_row_reference; + std::false_type hasDenseMatrixAssigner ( ... ); -//! We are at the leaf of the block recursion -enum { -//! The number of block levels we contain. This is 1. -blocklevel = 1 -}; + } // namespace Impl -private: -//! \brief if value_type is a simd vector, then this is a simd vector of -//! the same length that can be used for indices. -using simd_index_type = Simd::Rebind<std::size_t, value_type>; + template< class DenseMatrix, class RHS > + struct HasDenseMatrixAssigner + : public decltype( Impl::hasDenseMatrixAssigner( std::declval< DenseMatrix & >(), std::declval< const RHS & >() ) ) + {}; -public: -//===== access to components - -//! random access -row_reference operator[] ( size_type i ) -{ -return asImp().mat_access(i); -} - -const_row_reference operator[] ( size_type i ) const -{ -return asImp().mat_access(i); -} - -//! size method (number of rows) -size_type size() const -{ -return rows(); -} - -//===== iterator interface to rows of the matrix -//! Iterator class for sequential access -typedef DenseIterator<DenseMatrix,row_type,row_reference> Iterator; -//! typedef for stl compliant access -typedef Iterator iterator; -//! rename the iterators for easier access -typedef Iterator RowIterator; -//! rename the iterators for easier access -typedef typename std::remove_reference<row_reference>::type::Iterator ColIterator; - -//! begin iterator -Iterator begin () -{ -return Iterator(*this,0); -} - -//! end iterator -Iterator end () -{ -return Iterator(*this,rows()); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the vector, i.e. at the last entry. -Iterator beforeEnd () -{ -return Iterator(*this,rows()-1); -} - -//! @returns an iterator that is positioned before -//! the first entry of the vector. -Iterator beforeBegin () -{ -return Iterator(*this,-1); -} - -//! Iterator class for sequential access -typedef DenseIterator<const DenseMatrix,const row_type,const_row_reference> ConstIterator; -//! typedef for stl compliant access -typedef ConstIterator const_iterator; -//! rename the iterators for easier access -typedef ConstIterator ConstRowIterator; -//! rename the iterators for easier access -typedef typename std::remove_reference<const_row_reference>::type::ConstIterator ConstColIterator; - -//! begin iterator -ConstIterator begin () const -{ -return ConstIterator(*this,0); -} - -//! end iterator -ConstIterator end () const -{ -return ConstIterator(*this,rows()); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the vector. i.e. at the last element -ConstIterator beforeEnd () const -{ -return ConstIterator(*this,rows()-1); -} - -//! @returns an iterator that is positioned before -//! the first entry of the vector. -ConstIterator beforeBegin () const -{ -return ConstIterator(*this,-1); -} - -//===== assignment - -template< class RHS, class = std::enable_if_t< HasDenseMatrixAssigner< MAT, RHS >::value > > -derived_type &operator= ( const RHS &rhs ) -{ -DenseMatrixAssigner< MAT, RHS >::apply( asImp(), rhs ); -return asImp(); -} +#endif // #ifndef DOXYGEN -//===== vector space arithmetic -//! vector space addition -template <class Other> -derived_type &operator+= (const DenseMatrix<Other>& x) -{ -DUNE_ASSERT_BOUNDS(rows() == x.rows()); -for (size_type i=0; i<rows(); i++) -(*this)[i] += x[i]; -return asImp(); -} - -//! Matrix negation -derived_type operator- () const -{ -MAT result; -typedef typename decltype(result)::size_type size_type; -for (size_type i = 0; i < rows(); ++i) -for (size_type j = 0; j < cols(); ++j) -result[i][j] = - asImp()[i][j]; + /** @brief Error thrown if operations of a FieldMatrix fail. */ + class FMatrixError : public MathError {}; -return result; -} + /** + @brief A dense n x m matrix. -//! vector space subtraction -template <class Other> -derived_type &operator-= (const DenseMatrix<Other>& x) -{ -DUNE_ASSERT_BOUNDS(rows() == x.rows()); -for (size_type i=0; i<rows(); i++) -(*this)[i] -= x[i]; -return asImp(); -} - -//! vector space multiplication with scalar -derived_type &operator*= (const field_type& k) -{ -for (size_type i=0; i<rows(); i++) -(*this)[i] *= k; -return asImp(); -} - -//! vector space division by scalar -derived_type &operator/= (const field_type& k) -{ -for (size_type i=0; i<rows(); i++) -(*this)[i] /= k; -return asImp(); -} - -//! vector space axpy operation (*this += a x) -template <class Other> -derived_type &axpy (const field_type &a, const DenseMatrix<Other> &x ) -{ -DUNE_ASSERT_BOUNDS(rows() == x.rows()); -for( size_type i = 0; i < rows(); ++i ) -(*this)[ i ].axpy( a, x[ i ] ); -return asImp(); -} - -//! Binary matrix comparison -template <class Other> -bool operator== (const DenseMatrix<Other>& x) const -{ -DUNE_ASSERT_BOUNDS(rows() == x.rows()); -for (size_type i=0; i<rows(); i++) -if ((*this)[i]!=x[i]) -return false; -return true; -} -//! Binary matrix incomparison -template <class Other> -bool operator!= (const DenseMatrix<Other>& x) const -{ -return !operator==(x); -} - - -//===== linear maps - -//! y = A x -template<class X, class Y> -void mv (const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS((void*)(&x) != (void*)(&y)); -DUNE_ASSERT_BOUNDS(xx.N() == M()); -DUNE_ASSERT_BOUNDS(yy.N() == N()); - -using field_type = typename FieldTraits<Y>::field_type; -for (size_type i=0; i<rows(); ++i) -{ -yy[i] = field_type(0); -for (size_type j=0; j<cols(); j++) -yy[i] += (*this)[i][j] * xx[j]; -} -} - -//! y = A^T x -template< class X, class Y > -void mtv ( const X &x, Y &y ) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS((void*)(&x) != (void*)(&y)); -DUNE_ASSERT_BOUNDS(xx.N() == N()); -DUNE_ASSERT_BOUNDS(yy.N() == M()); - -using field_type = typename FieldTraits<Y>::field_type; -for(size_type i = 0; i < cols(); ++i) -{ -yy[i] = field_type(0); -for(size_type j = 0; j < rows(); ++j) -yy[i] += (*this)[j][i] * xx[j]; -} -} - -//! y += A x -template<class X, class Y> -void umv (const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == M()); -DUNE_ASSERT_BOUNDS(yy.N() == N()); -for (size_type i=0; i<rows(); ++i) -for (size_type j=0; j<cols(); j++) -yy[i] += (*this)[i][j] * xx[j]; -} - -//! y += A^T x -template<class X, class Y> -void umtv (const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == N()); -DUNE_ASSERT_BOUNDS(yy.N() == M()); -for(size_type i = 0; i<rows(); ++i) -for (size_type j=0; j<cols(); j++) -yy[j] += (*this)[i][j]*xx[i]; -} - -//! y += A^H x -template<class X, class Y> -void umhv (const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == N()); -DUNE_ASSERT_BOUNDS(yy.N() == M()); -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) -yy[j] += conjugateComplex((*this)[i][j])*xx[i]; -} - -//! y -= A x -template<class X, class Y> -void mmv (const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == M()); -DUNE_ASSERT_BOUNDS(yy.N() == N()); -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) -yy[i] -= (*this)[i][j] * xx[j]; -} - -//! y -= A^T x -template<class X, class Y> -void mmtv (const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == N()); -DUNE_ASSERT_BOUNDS(yy.N() == M()); -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) -yy[j] -= (*this)[i][j]*xx[i]; -} - -//! y -= A^H x -template<class X, class Y> -void mmhv (const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == N()); -DUNE_ASSERT_BOUNDS(yy.N() == M()); -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) -yy[j] -= conjugateComplex((*this)[i][j])*xx[i]; -} - -//! y += alpha A x -template<class X, class Y> -void usmv (const typename FieldTraits<Y>::field_type & alpha, -const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == M()); -DUNE_ASSERT_BOUNDS(yy.N() == N()); -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) -yy[i] += alpha * (*this)[i][j] * xx[j]; -} - -//! y += alpha A^T x -template<class X, class Y> -void usmtv (const typename FieldTraits<Y>::field_type & alpha, -const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == N()); -DUNE_ASSERT_BOUNDS(yy.N() == M()); -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) -yy[j] += alpha*(*this)[i][j]*xx[i]; -} - -//! y += alpha A^H x -template<class X, class Y> -void usmhv (const typename FieldTraits<Y>::field_type & alpha, -const X& x, Y& y) const -{ -auto&& xx = Impl::asVector(x); -auto&& yy = Impl::asVector(y); -DUNE_ASSERT_BOUNDS(xx.N() == N()); -DUNE_ASSERT_BOUNDS(yy.N() == M()); -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) -yy[j] += -alpha*conjugateComplex((*this)[i][j])*xx[i]; -} - -//===== norms - -//! frobenius norm: sqrt(sum over squared values of entries) -typename FieldTraits<value_type>::real_type frobenius_norm () const -{ -typename FieldTraits<value_type>::real_type sum=(0.0); -for (size_type i=0; i<rows(); ++i) sum += (*this)[i].two_norm2(); -return fvmeta::sqrt(sum); -} - -//! square of frobenius norm, need for block recursion -typename FieldTraits<value_type>::real_type frobenius_norm2 () const -{ -typename FieldTraits<value_type>::real_type sum=(0.0); -for (size_type i=0; i<rows(); ++i) sum += (*this)[i].two_norm2(); -return sum; -} - -//! infinity norm (row sum norm, how to generalize for blocks?) -template <typename vt = value_type, -typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::max; - -real_type norm = 0; -for (auto const &x : *this) { -real_type const a = x.one_norm(); -norm = max(a, norm); -} -return norm; -} - -//! simplified infinity norm (uses Manhattan norm for complex values) -template <typename vt = value_type, -typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm_real() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::max; - -real_type norm = 0; -for (auto const &x : *this) { -real_type const a = x.one_norm_real(); -norm = max(a, norm); -} -return norm; -} - -//! infinity norm (row sum norm, how to generalize for blocks?) -template <typename vt = value_type, -typename std::enable_if<HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::max; - -real_type norm = 0; -real_type isNaN = 1; -for (auto const &x : *this) { -real_type const a = x.one_norm(); -norm = max(a, norm); -isNaN += a; -} -return norm * (isNaN / isNaN); -} - -//! simplified infinity norm (uses Manhattan norm for complex values) -template <typename vt = value_type, -typename std::enable_if<HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm_real() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::max; - -real_type norm = 0; -real_type isNaN = 1; -for (auto const &x : *this) { -real_type const a = x.one_norm_real(); -norm = max(a, norm); -isNaN += a; -} -return norm * (isNaN / isNaN); -} - -//===== solve - -/** \brief Solve system A x = b -* -* \exception FMatrixError if the matrix is singular -*/ -template <class V1, class V2> -void solve (V1& x, const V2& b, bool doPivoting = true) const; - -/** \brief Compute inverse -* -* \exception FMatrixError if the matrix is singular -*/ -void invert(bool doPivoting = true); - -//! calculates the determinant of this matrix -field_type determinant (bool doPivoting = true) const; - -//! Multiplies M from the left to this matrix -template<typename M2> -MAT& leftmultiply (const DenseMatrix<M2>& M) -{ -DUNE_ASSERT_BOUNDS(M.rows() == M.cols()); -DUNE_ASSERT_BOUNDS(M.rows() == rows()); -AutonomousValue<MAT> C(asImp()); - -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) { -(*this)[i][j] = 0; -for (size_type k=0; k<rows(); k++) -(*this)[i][j] += M[i][k]*C[k][j]; -} - -return asImp(); -} - -//! Multiplies M from the right to this matrix -template<typename M2> -MAT& rightmultiply (const DenseMatrix<M2>& M) -{ -DUNE_ASSERT_BOUNDS(M.rows() == M.cols()); -DUNE_ASSERT_BOUNDS(M.cols() == cols()); -AutonomousValue<MAT> C(asImp()); - -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<cols(); j++) { -(*this)[i][j] = 0; -for (size_type k=0; k<cols(); k++) -(*this)[i][j] += C[i][k]*M[k][j]; -} -return asImp(); -} + Matrices represent linear maps from a vector space V to a vector space W. + This class represents such a linear map by storing a two-dimensional + %array of numbers of a given field type K. The number of rows and + columns is given at compile time. + + \tparam MAT type of the matrix implementation + */ + template<typename MAT> + class DenseMatrix + { + typedef DenseMatVecTraits<MAT> Traits; + + // Curiously recurring template pattern + constexpr MAT & asImp() { return static_cast<MAT&>(*this); } + constexpr const MAT & asImp() const { return static_cast<const MAT&>(*this); } + + template <class> + friend class DenseMatrix; + + public: + //===== type definitions and constants + + //! type of derived matrix class + typedef typename Traits::derived_type derived_type; + + //! export the type representing the field + typedef typename Traits::value_type value_type; + + //! export the type representing the field + typedef typename Traits::value_type field_type; + + //! export the type representing the components + typedef typename Traits::value_type block_type; + + //! The type used for the index access and size operation + typedef typename Traits::size_type size_type; + + //! The type used to represent a row (must fulfill the Dune::DenseVector interface) + typedef typename Traits::row_type row_type; + + //! The type used to represent a reference to a row (usually row_type &) + typedef typename Traits::row_reference row_reference; + + //! The type used to represent a reference to a constant row (usually const row_type &) + typedef typename Traits::const_row_reference const_row_reference; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. This is 1. + blocklevel = 1 + }; + + private: + //! \brief if value_type is a simd vector, then this is a simd vector of + //! the same length that can be used for indices. + using simd_index_type = Simd::Rebind<std::size_t, value_type>; + + public: + //===== access to components + + //! random access + row_reference operator[] ( size_type i ) + { + return asImp().mat_access(i); + } + + const_row_reference operator[] ( size_type i ) const + { + return asImp().mat_access(i); + } + + //! size method (number of rows) + size_type size() const + { + return rows(); + } + + //===== iterator interface to rows of the matrix + //! Iterator class for sequential access + typedef DenseIterator<DenseMatrix,row_type,row_reference> Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + //! rename the iterators for easier access + typedef Iterator RowIterator; + //! rename the iterators for easier access + typedef typename std::remove_reference<row_reference>::type::Iterator ColIterator; + + //! begin iterator + Iterator begin () + { + return Iterator(*this,0); + } + + //! end iterator + Iterator end () + { + return Iterator(*this,rows()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector, i.e. at the last entry. + Iterator beforeEnd () + { + return Iterator(*this,rows()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + Iterator beforeBegin () + { + return Iterator(*this,-1); + } + + //! Iterator class for sequential access + typedef DenseIterator<const DenseMatrix,const row_type,const_row_reference> ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + //! rename the iterators for easier access + typedef ConstIterator ConstRowIterator; + //! rename the iterators for easier access + typedef typename std::remove_reference<const_row_reference>::type::ConstIterator ConstColIterator; + + //! begin iterator + ConstIterator begin () const + { + return ConstIterator(*this,0); + } + + //! end iterator + ConstIterator end () const + { + return ConstIterator(*this,rows()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector. i.e. at the last element + ConstIterator beforeEnd () const + { + return ConstIterator(*this,rows()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + ConstIterator beforeBegin () const + { + return ConstIterator(*this,-1); + } + + //===== assignment + + template< class RHS, class = std::enable_if_t< HasDenseMatrixAssigner< MAT, RHS >::value > > + derived_type &operator= ( const RHS &rhs ) + { + DenseMatrixAssigner< MAT, RHS >::apply( asImp(), rhs ); + return asImp(); + } + + //===== vector space arithmetic + + //! vector space addition + template <class Other> + derived_type &operator+= (const DenseMatrix<Other>& x) + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for (size_type i=0; i<rows(); i++) + (*this)[i] += x[i]; + return asImp(); + } + + //! Matrix negation + derived_type operator- () const + { + MAT result; + typedef typename decltype(result)::size_type size_type; + + for (size_type i = 0; i < rows(); ++i) + for (size_type j = 0; j < cols(); ++j) + result[i][j] = - asImp()[i][j]; + + return result; + } + + //! vector space subtraction + template <class Other> + derived_type &operator-= (const DenseMatrix<Other>& x) + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for (size_type i=0; i<rows(); i++) + (*this)[i] -= x[i]; + return asImp(); + } + + //! vector space multiplication with scalar + derived_type &operator*= (const field_type& k) + { + for (size_type i=0; i<rows(); i++) + (*this)[i] *= k; + return asImp(); + } + + //! vector space division by scalar + derived_type &operator/= (const field_type& k) + { + for (size_type i=0; i<rows(); i++) + (*this)[i] /= k; + return asImp(); + } + + //! vector space axpy operation (*this += a x) + template <class Other> + derived_type &axpy (const field_type &a, const DenseMatrix<Other> &x ) + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for( size_type i = 0; i < rows(); ++i ) + (*this)[ i ].axpy( a, x[ i ] ); + return asImp(); + } + + //! Binary matrix comparison + template <class Other> + bool operator== (const DenseMatrix<Other>& x) const + { + DUNE_ASSERT_BOUNDS(rows() == x.rows()); + for (size_type i=0; i<rows(); i++) + if ((*this)[i]!=x[i]) + return false; + return true; + } + //! Binary matrix incomparison + template <class Other> + bool operator!= (const DenseMatrix<Other>& x) const + { + return !operator==(x); + } + + + //===== linear maps + + //! y = A x + template<class X, class Y> + void mv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS((void*)(&x) != (void*)(&y)); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + + using field_type = typename FieldTraits<Y>::field_type; + for (size_type i=0; i<rows(); ++i) + { + yy[i] = field_type(0); + for (size_type j=0; j<cols(); j++) + yy[i] += (*this)[i][j] * xx[j]; + } + } + + //! y = A^T x + template< class X, class Y > + void mtv ( const X &x, Y &y ) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS((void*)(&x) != (void*)(&y)); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + + using field_type = typename FieldTraits<Y>::field_type; + for(size_type i = 0; i < cols(); ++i) + { + yy[i] = field_type(0); + for(size_type j = 0; j < rows(); ++j) + yy[i] += (*this)[j][i] * xx[j]; + } + } + + //! y += A x + template<class X, class Y> + void umv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + for (size_type i=0; i<rows(); ++i) + for (size_type j=0; j<cols(); j++) + yy[i] += (*this)[i][j] * xx[j]; + } + + //! y += A^T x + template<class X, class Y> + void umtv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for(size_type i = 0; i<rows(); ++i) + for (size_type j=0; j<cols(); j++) + yy[j] += (*this)[i][j]*xx[i]; + } + + //! y += A^H x + template<class X, class Y> + void umhv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) + yy[j] += conjugateComplex((*this)[i][j])*xx[i]; + } + + //! y -= A x + template<class X, class Y> + void mmv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) + yy[i] -= (*this)[i][j] * xx[j]; + } + + //! y -= A^T x + template<class X, class Y> + void mmtv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) + yy[j] -= (*this)[i][j]*xx[i]; + } + + //! y -= A^H x + template<class X, class Y> + void mmhv (const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) + yy[j] -= conjugateComplex((*this)[i][j])*xx[i]; + } + + //! y += alpha A x + template<class X, class Y> + void usmv (const typename FieldTraits<Y>::field_type & alpha, + const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == M()); + DUNE_ASSERT_BOUNDS(yy.N() == N()); + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) + yy[i] += alpha * (*this)[i][j] * xx[j]; + } + + //! y += alpha A^T x + template<class X, class Y> + void usmtv (const typename FieldTraits<Y>::field_type & alpha, + const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) + yy[j] += alpha*(*this)[i][j]*xx[i]; + } + + //! y += alpha A^H x + template<class X, class Y> + void usmhv (const typename FieldTraits<Y>::field_type & alpha, + const X& x, Y& y) const + { + auto&& xx = Impl::asVector(x); + auto&& yy = Impl::asVector(y); + DUNE_ASSERT_BOUNDS(xx.N() == N()); + DUNE_ASSERT_BOUNDS(yy.N() == M()); + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) + yy[j] += + alpha*conjugateComplex((*this)[i][j])*xx[i]; + } + + //===== norms + + //! frobenius norm: sqrt(sum over squared values of entries) + typename FieldTraits<value_type>::real_type frobenius_norm () const + { + typename FieldTraits<value_type>::real_type sum=(0.0); + for (size_type i=0; i<rows(); ++i) sum += (*this)[i].two_norm2(); + return fvmeta::sqrt(sum); + } + + //! square of frobenius norm, need for block recursion + typename FieldTraits<value_type>::real_type frobenius_norm2 () const + { + typename FieldTraits<value_type>::real_type sum=(0.0); + for (size_type i=0; i<rows(); ++i) sum += (*this)[i].two_norm2(); + return sum; + } + + //! infinity norm (row sum norm, how to generalize for blocks?) + template <typename vt = value_type, + typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = x.one_norm(); + norm = max(a, norm); + } + return norm; + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template <typename vt = value_type, + typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm_real() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = x.one_norm_real(); + norm = max(a, norm); + } + return norm; + } + + //! infinity norm (row sum norm, how to generalize for blocks?) + template <typename vt = value_type, + typename std::enable_if<HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = x.one_norm(); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template <typename vt = value_type, + typename std::enable_if<HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm_real() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = x.one_norm_real(); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //===== solve + + /** \brief Solve system A x = b + * + * \exception FMatrixError if the matrix is singular + */ + template <class V1, class V2> + void solve (V1& x, const V2& b, bool doPivoting = true) const; + + /** \brief Compute inverse + * + * \exception FMatrixError if the matrix is singular + */ + void invert(bool doPivoting = true); + + //! calculates the determinant of this matrix + field_type determinant (bool doPivoting = true) const; + + //! Multiplies M from the left to this matrix + template<typename M2> + MAT& leftmultiply (const DenseMatrix<M2>& M) + { + DUNE_ASSERT_BOUNDS(M.rows() == M.cols()); + DUNE_ASSERT_BOUNDS(M.rows() == rows()); + AutonomousValue<MAT> C(asImp()); + + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) { + (*this)[i][j] = 0; + for (size_type k=0; k<rows(); k++) + (*this)[i][j] += M[i][k]*C[k][j]; + } + + return asImp(); + } + + //! Multiplies M from the right to this matrix + template<typename M2> + MAT& rightmultiply (const DenseMatrix<M2>& M) + { + DUNE_ASSERT_BOUNDS(M.rows() == M.cols()); + DUNE_ASSERT_BOUNDS(M.cols() == cols()); + AutonomousValue<MAT> C(asImp()); + + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<cols(); j++) { + (*this)[i][j] = 0; + for (size_type k=0; k<cols(); k++) + (*this)[i][j] += C[i][k]*M[k][j]; + } + return asImp(); + } #if 0 -//! Multiplies M from the left to this matrix, this matrix is not modified -template<int l> -DenseMatrix<K,l,cols> leftmultiplyany (const FieldMatrix<K,l,rows>& M) const -{ -FieldMatrix<K,l,cols> C; - -for (size_type i=0; i<l; i++) { -for (size_type j=0; j<cols(); j++) { -C[i][j] = 0; -for (size_type k=0; k<rows(); k++) -C[i][j] += M[i][k]*(*this)[k][j]; -} -} -return C; -} - -//! Multiplies M from the right to this matrix, this matrix is not modified -template<int l> -FieldMatrix<K,rows,l> rightmultiplyany (const FieldMatrix<K,cols,l>& M) const -{ -FieldMatrix<K,rows,l> C; - -for (size_type i=0; i<rows(); i++) { -for (size_type j=0; j<l; j++) { -C[i][j] = 0; -for (size_type k=0; k<cols(); k++) -C[i][j] += (*this)[i][k]*M[k][j]; -} -} -return C; -} + //! Multiplies M from the left to this matrix, this matrix is not modified + template<int l> + DenseMatrix<K,l,cols> leftmultiplyany (const FieldMatrix<K,l,rows>& M) const + { + FieldMatrix<K,l,cols> C; + + for (size_type i=0; i<l; i++) { + for (size_type j=0; j<cols(); j++) { + C[i][j] = 0; + for (size_type k=0; k<rows(); k++) + C[i][j] += M[i][k]*(*this)[k][j]; + } + } + return C; + } + + //! Multiplies M from the right to this matrix, this matrix is not modified + template<int l> + FieldMatrix<K,rows,l> rightmultiplyany (const FieldMatrix<K,cols,l>& M) const + { + FieldMatrix<K,rows,l> C; + + for (size_type i=0; i<rows(); i++) { + for (size_type j=0; j<l; j++) { + C[i][j] = 0; + for (size_type k=0; k<cols(); k++) + C[i][j] += (*this)[i][k]*M[k][j]; + } + } + return C; + } #endif -//===== sizes - -//! number of rows -constexpr size_type N () const -{ -return rows(); -} - -//! number of columns -constexpr size_type M () const -{ -return cols(); -} - -//! number of rows -constexpr size_type rows() const -{ -return asImp().mat_rows(); -} - -//! number of columns -constexpr size_type cols() const -{ -return asImp().mat_cols(); -} - -//===== query - -//! return true when (i,j) is in pattern -bool exists (size_type i, size_type j) const -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_UNUSED_PARAMETER(j); -DUNE_ASSERT_BOUNDS(i >= 0 && i < rows()); -DUNE_ASSERT_BOUNDS(j >= 0 && j < cols()); -return true; -} - -protected: + //===== sizes + + //! number of rows + constexpr size_type N () const + { + return rows(); + } + + //! number of columns + constexpr size_type M () const + { + return cols(); + } + + //! number of rows + constexpr size_type rows() const + { + return asImp().mat_rows(); + } + + //! number of columns + constexpr size_type cols() const + { + return asImp().mat_cols(); + } + + //===== query + + //! return true when (i,j) is in pattern + bool exists (size_type i, size_type j) const + { + DUNE_UNUSED_PARAMETER(i); + DUNE_UNUSED_PARAMETER(j); + DUNE_ASSERT_BOUNDS(i >= 0 && i < rows()); + DUNE_ASSERT_BOUNDS(j >= 0 && j < cols()); + return true; + } + + protected: #ifndef DOXYGEN -struct ElimPivot -{ -ElimPivot(std::vector<simd_index_type> & pivot); + struct ElimPivot + { + ElimPivot(std::vector<simd_index_type> & pivot); -void swap(std::size_t i, simd_index_type j); + void swap(std::size_t i, simd_index_type j); -template<typename T> -void operator()(const T&, int, int) -{} + template<typename T> + void operator()(const T&, int, int) + {} -std::vector<simd_index_type> & pivot_; -}; + std::vector<simd_index_type> & pivot_; + }; -template<typename V> -struct Elim -{ -Elim(V& rhs); + template<typename V> + struct Elim + { + Elim(V& rhs); -void swap(std::size_t i, simd_index_type j); + void swap(std::size_t i, simd_index_type j); -void operator()(const typename V::field_type& factor, int k, int i); + void operator()(const typename V::field_type& factor, int k, int i); -V* rhs_; -}; + V* rhs_; + }; -struct ElimDet -{ -ElimDet(field_type& sign) : sign_(sign) -{ sign_ = 1; } + struct ElimDet + { + ElimDet(field_type& sign) : sign_(sign) + { sign_ = 1; } -void swap(std::size_t i, simd_index_type j) -{ -sign_ *= -Simd::cond(simd_index_type(i) == j, field_type(1), field_type(-1)); -} + void swap(std::size_t i, simd_index_type j) + { + sign_ *= + Simd::cond(simd_index_type(i) == j, field_type(1), field_type(-1)); + } -void operator()(const field_type&, int, int) -{} + void operator()(const field_type&, int, int) + {} -field_type& sign_; -}; + field_type& sign_; + }; #endif // DOXYGEN -//! do an LU-Decomposition on matrix A -/** -* \param A The matrix to decompose, and to store the -* result in. -* \param func Functor used for swapping lanes and to conduct -* the elimination. Depending on the functor, \c -* luDecomposition() can be used for solving, for -* inverting, or to compute the determinant. -* \param nonsingularLanes SimdMask of lanes that are nonsingular. -* \param throwEarly Whether to throw an \c FMatrixError immediately -* as soon as one lane is discovered to be -* singular. If \c false, do not throw, instead -* continue until finished or all lanes are -* singular, and exit via return in both cases. -* \param doPivoting Enable pivoting. -* -* There are two modes of operation: -* <ul> -* <li>Terminate as soon as one lane is discovered to be singular. Early -* termination is done by throwing an \c FMatrixError. On entry, \c -* Simd::allTrue(nonsingularLanes) and \c throwEarly==true should hold. -* After early termination, the contents of \c A should be considered -* bogus, and \c nonsingularLanes has the lane(s) that triggered the -* early termination unset. There may be more singular lanes than the -* one reported in \c nonsingularLanes, which just havent been -* discovered yet; so the value of \c nonsingularLanes is mostly -* useful for diagnostics.</li> -* <li>Terminate only when all lanes are discovered to be singular. Use -* this when you want to apply special postprocessing in singular -* lines (e.g. setting the determinant of singular lanes to 0 in \c -* determinant()). On entry, \c nonsingularLanes may have any value -* and \c throwEarly==false should hold. The function will not throw -* an exception if some lanes are discovered to be singular, instead -* it will continue running until all lanes are singular or until -* finished, and terminate only via normal return. On exit, \c -* nonsingularLanes contains the map of lanes that are valid in \c -* A.</li> -* </ul> -*/ -template<class Func, class Mask> -static void luDecomposition(DenseMatrix<MAT>& A, Func func, -Mask &nonsingularLanes, bool throwEarly, bool doPivoting); -}; + //! do an LU-Decomposition on matrix A + /** + * \param A The matrix to decompose, and to store the + * result in. + * \param func Functor used for swapping lanes and to conduct + * the elimination. Depending on the functor, \c + * luDecomposition() can be used for solving, for + * inverting, or to compute the determinant. + * \param nonsingularLanes SimdMask of lanes that are nonsingular. + * \param throwEarly Whether to throw an \c FMatrixError immediately + * as soon as one lane is discovered to be + * singular. If \c false, do not throw, instead + * continue until finished or all lanes are + * singular, and exit via return in both cases. + * \param doPivoting Enable pivoting. + * + * There are two modes of operation: + * <ul> + * <li>Terminate as soon as one lane is discovered to be singular. Early + * termination is done by throwing an \c FMatrixError. On entry, \c + * Simd::allTrue(nonsingularLanes) and \c throwEarly==true should hold. + * After early termination, the contents of \c A should be considered + * bogus, and \c nonsingularLanes has the lane(s) that triggered the + * early termination unset. There may be more singular lanes than the + * one reported in \c nonsingularLanes, which just havent been + * discovered yet; so the value of \c nonsingularLanes is mostly + * useful for diagnostics.</li> + * <li>Terminate only when all lanes are discovered to be singular. Use + * this when you want to apply special postprocessing in singular + * lines (e.g. setting the determinant of singular lanes to 0 in \c + * determinant()). On entry, \c nonsingularLanes may have any value + * and \c throwEarly==false should hold. The function will not throw + * an exception if some lanes are discovered to be singular, instead + * it will continue running until all lanes are singular or until + * finished, and terminate only via normal return. On exit, \c + * nonsingularLanes contains the map of lanes that are valid in \c + * A.</li> + * </ul> + */ + template<class Func, class Mask> + static void luDecomposition(DenseMatrix<MAT>& A, Func func, + Mask &nonsingularLanes, bool throwEarly, bool doPivoting); + }; #ifndef DOXYGEN -template<typename MAT> -DenseMatrix<MAT>::ElimPivot::ElimPivot(std::vector<simd_index_type> & pivot) -: pivot_(pivot) -{ -typedef typename std::vector<size_type>::size_type size_type; -for(size_type i=0; i < pivot_.size(); ++i) pivot_[i]=i; -} - -template<typename MAT> -void DenseMatrix<MAT>::ElimPivot::swap(std::size_t i, simd_index_type j) -{ -pivot_[i] = -Simd::cond(Simd::Scalar<simd_index_type>(i) == j, pivot_[i], j); -} - -template<typename MAT> -template<typename V> -DenseMatrix<MAT>::Elim<V>::Elim(V& rhs) -: rhs_(&rhs) -{} - -template<typename MAT> -template<typename V> -void DenseMatrix<MAT>::Elim<V>::swap(std::size_t i, simd_index_type j) -{ -using std::swap; - -// see the comment in luDecomposition() -for(std::size_t l = 0; l < Simd::lanes(j); ++l) -swap(Simd::lane(l, (*rhs_)[ i ]), -Simd::lane(l, (*rhs_)[Simd::lane(l, j)])); -} - -template<typename MAT> -template<typename V> -void DenseMatrix<MAT>:: -Elim<V>::operator()(const typename V::field_type& factor, int k, int i) -{ -(*rhs_)[k] -= factor*(*rhs_)[i]; -} - -template<typename MAT> -template<typename Func, class Mask> -inline void DenseMatrix<MAT>:: -luDecomposition(DenseMatrix<MAT>& A, Func func, Mask &nonsingularLanes, -bool throwEarly, bool doPivoting) -{ -using std::max; -using std::swap; - -typedef typename FieldTraits<value_type>::real_type real_type; - -// LU decomposition of A in A -for (size_type i=0; i<A.rows(); i++) // loop over all rows -{ -real_type pivmax = fvmeta::absreal(A[i][i]); - -if (doPivoting) -{ -// compute maximum of column -simd_index_type imax=i; -for (size_type k=i+1; k<A.rows(); k++) -{ -auto abs = fvmeta::absreal(A[k][i]); -auto mask = abs > pivmax; -pivmax = Simd::cond(mask, abs, pivmax); -imax = Simd::cond(mask, simd_index_type(k), imax); -} -// swap rows -for (size_type j=0; j<A.rows(); j++) -{ -// This is a swap operation where the second operand is scattered, -// and on top of that is also extracted from deep within a -// moderately complicated data structure (a DenseMatrix), where we -// can't assume much on the memory layout. On intel processors, -// the only instruction that might help us here is vgather, but it -// is unclear whether that is even faster than a software -// implementation, and we would also need vscatter which does not -// exist. So break vectorization here and do it manually. -for(std::size_t l = 0; l < Simd::lanes(A[i][j]); ++l) -swap(Simd::lane(l, A[i][j]), -Simd::lane(l, A[Simd::lane(l, imax)][j])); -} -func.swap(i, imax); // swap the pivot or rhs -} - -// singular ? -nonsingularLanes = nonsingularLanes && (pivmax != real_type(0)); -if (throwEarly) { -if(!Simd::allTrue(nonsingularLanes)) -DUNE_THROW(FMatrixError, "matrix is singular"); -} -else { // !throwEarly -if(!Simd::anyTrue(nonsingularLanes)) -return; -} - -// eliminate -for (size_type k=i+1; k<A.rows(); k++) -{ -// in the simd case, A[i][i] may be close to zero in some lanes. Pray -// that the result is no worse than a quiet NaN. -field_type factor = A[k][i]/A[i][i]; -A[k][i] = factor; -for (size_type j=i+1; j<A.rows(); j++) -A[k][j] -= factor*A[i][j]; -func(factor, k, i); -} -} -} - -template<typename MAT> -template <class V1, class V2> -inline void DenseMatrix<MAT>::solve(V1& x, const V2& b, bool doPivoting) const -{ -using real_type = typename FieldTraits<value_type>::real_type; -// never mind those ifs, because they get optimized away -if (rows()!=cols()) -DUNE_THROW(FMatrixError, "Can't solve for a " << rows() << "x" << cols() << " matrix!"); - -if (rows()==1) { + template<typename MAT> + DenseMatrix<MAT>::ElimPivot::ElimPivot(std::vector<simd_index_type> & pivot) + : pivot_(pivot) + { + typedef typename std::vector<size_type>::size_type size_type; + for(size_type i=0; i < pivot_.size(); ++i) pivot_[i]=i; + } + + template<typename MAT> + void DenseMatrix<MAT>::ElimPivot::swap(std::size_t i, simd_index_type j) + { + pivot_[i] = + Simd::cond(Simd::Scalar<simd_index_type>(i) == j, pivot_[i], j); + } + + template<typename MAT> + template<typename V> + DenseMatrix<MAT>::Elim<V>::Elim(V& rhs) + : rhs_(&rhs) + {} + + template<typename MAT> + template<typename V> + void DenseMatrix<MAT>::Elim<V>::swap(std::size_t i, simd_index_type j) + { + using std::swap; + + // see the comment in luDecomposition() + for(std::size_t l = 0; l < Simd::lanes(j); ++l) + swap(Simd::lane(l, (*rhs_)[ i ]), + Simd::lane(l, (*rhs_)[Simd::lane(l, j)])); + } + + template<typename MAT> + template<typename V> + void DenseMatrix<MAT>:: + Elim<V>::operator()(const typename V::field_type& factor, int k, int i) + { + (*rhs_)[k] -= factor*(*rhs_)[i]; + } + + template<typename MAT> + template<typename Func, class Mask> + inline void DenseMatrix<MAT>:: + luDecomposition(DenseMatrix<MAT>& A, Func func, Mask &nonsingularLanes, + bool throwEarly, bool doPivoting) + { + using std::max; + using std::swap; + + typedef typename FieldTraits<value_type>::real_type real_type; + + // LU decomposition of A in A + for (size_type i=0; i<A.rows(); i++) // loop over all rows + { + real_type pivmax = fvmeta::absreal(A[i][i]); + + if (doPivoting) + { + // compute maximum of column + simd_index_type imax=i; + for (size_type k=i+1; k<A.rows(); k++) + { + auto abs = fvmeta::absreal(A[k][i]); + auto mask = abs > pivmax; + pivmax = Simd::cond(mask, abs, pivmax); + imax = Simd::cond(mask, simd_index_type(k), imax); + } + // swap rows + for (size_type j=0; j<A.rows(); j++) + { + // This is a swap operation where the second operand is scattered, + // and on top of that is also extracted from deep within a + // moderately complicated data structure (a DenseMatrix), where we + // can't assume much on the memory layout. On intel processors, + // the only instruction that might help us here is vgather, but it + // is unclear whether that is even faster than a software + // implementation, and we would also need vscatter which does not + // exist. So break vectorization here and do it manually. + for(std::size_t l = 0; l < Simd::lanes(A[i][j]); ++l) + swap(Simd::lane(l, A[i][j]), + Simd::lane(l, A[Simd::lane(l, imax)][j])); + } + func.swap(i, imax); // swap the pivot or rhs + } + + // singular ? + nonsingularLanes = nonsingularLanes && (pivmax != real_type(0)); + if (throwEarly) { + if(!Simd::allTrue(nonsingularLanes)) + DUNE_THROW(FMatrixError, "matrix is singular"); + } + else { // !throwEarly + if(!Simd::anyTrue(nonsingularLanes)) + return; + } + + // eliminate + for (size_type k=i+1; k<A.rows(); k++) + { + // in the simd case, A[i][i] may be close to zero in some lanes. Pray + // that the result is no worse than a quiet NaN. + field_type factor = A[k][i]/A[i][i]; + A[k][i] = factor; + for (size_type j=i+1; j<A.rows(); j++) + A[k][j] -= factor*A[i][j]; + func(factor, k, i); + } + } + } + + template<typename MAT> + template <class V1, class V2> + inline void DenseMatrix<MAT>::solve(V1& x, const V2& b, bool doPivoting) const + { + using real_type = typename FieldTraits<value_type>::real_type; + // never mind those ifs, because they get optimized away + if (rows()!=cols()) + DUNE_THROW(FMatrixError, "Can't solve for a " << rows() << "x" << cols() << " matrix!"); + + if (rows()==1) { #ifdef DUNE_FMatrix_WITH_CHECKING -if (Simd::anyTrue(fvmeta::absreal((*this)[0][0]) -< FMatrixPrecision<>::absolute_limit())) -DUNE_THROW(FMatrixError,"matrix is singular"); + if (Simd::anyTrue(fvmeta::absreal((*this)[0][0]) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); #endif -x[0] = b[0]/(*this)[0][0]; + x[0] = b[0]/(*this)[0][0]; -} -else if (rows()==2) { + } + else if (rows()==2) { -field_type detinv = (*this)[0][0]*(*this)[1][1]-(*this)[0][1]*(*this)[1][0]; + field_type detinv = (*this)[0][0]*(*this)[1][1]-(*this)[0][1]*(*this)[1][0]; #ifdef DUNE_FMatrix_WITH_CHECKING -if (Simd::anyTrue(fvmeta::absreal(detinv) -< FMatrixPrecision<>::absolute_limit())) -DUNE_THROW(FMatrixError,"matrix is singular"); + if (Simd::anyTrue(fvmeta::absreal(detinv) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); #endif -detinv = real_type(1.0)/detinv; + detinv = real_type(1.0)/detinv; -x[0] = detinv*((*this)[1][1]*b[0]-(*this)[0][1]*b[1]); -x[1] = detinv*((*this)[0][0]*b[1]-(*this)[1][0]*b[0]); + x[0] = detinv*((*this)[1][1]*b[0]-(*this)[0][1]*b[1]); + x[1] = detinv*((*this)[0][0]*b[1]-(*this)[1][0]*b[0]); -} -else if (rows()==3) { + } + else if (rows()==3) { -field_type d = determinant(doPivoting); + field_type d = determinant(doPivoting); #ifdef DUNE_FMatrix_WITH_CHECKING -if (Simd::anyTrue(fvmeta::absreal(d) -< FMatrixPrecision<>::absolute_limit())) -DUNE_THROW(FMatrixError,"matrix is singular"); + if (Simd::anyTrue(fvmeta::absreal(d) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); #endif -x[0] = (b[0]*(*this)[1][1]*(*this)[2][2] - b[0]*(*this)[2][1]*(*this)[1][2] -- b[1] *(*this)[0][1]*(*this)[2][2] + b[1]*(*this)[2][1]*(*this)[0][2] -+ b[2] *(*this)[0][1]*(*this)[1][2] - b[2]*(*this)[1][1]*(*this)[0][2]) / d; - -x[1] = ((*this)[0][0]*b[1]*(*this)[2][2] - (*this)[0][0]*b[2]*(*this)[1][2] -- (*this)[1][0] *b[0]*(*this)[2][2] + (*this)[1][0]*b[2]*(*this)[0][2] -+ (*this)[2][0] *b[0]*(*this)[1][2] - (*this)[2][0]*b[1]*(*this)[0][2]) / d; - -x[2] = ((*this)[0][0]*(*this)[1][1]*b[2] - (*this)[0][0]*(*this)[2][1]*b[1] -- (*this)[1][0] *(*this)[0][1]*b[2] + (*this)[1][0]*(*this)[2][1]*b[0] -+ (*this)[2][0] *(*this)[0][1]*b[1] - (*this)[2][0]*(*this)[1][1]*b[0]) / d; - -} -else { - -V1& rhs = x; // use x to store rhs -rhs = b; // copy data -Elim<V1> elim(rhs); -AutonomousValue<MAT> A(asImp()); -Simd::Mask<typename FieldTraits<value_type>::real_type> -nonsingularLanes(true); - -AutonomousValue<MAT>::luDecomposition(A, elim, nonsingularLanes, true, doPivoting); - -// backsolve -for(int i=rows()-1; i>=0; i--) { -for (size_type j=i+1; j<rows(); j++) -rhs[i] -= A[i][j]*x[j]; -x[i] = rhs[i]/A[i][i]; -} -} -} - -template<typename MAT> -inline void DenseMatrix<MAT>::invert(bool doPivoting) -{ -using real_type = typename FieldTraits<MAT>::real_type; -using std::swap; + x[0] = (b[0]*(*this)[1][1]*(*this)[2][2] - b[0]*(*this)[2][1]*(*this)[1][2] + - b[1] *(*this)[0][1]*(*this)[2][2] + b[1]*(*this)[2][1]*(*this)[0][2] + + b[2] *(*this)[0][1]*(*this)[1][2] - b[2]*(*this)[1][1]*(*this)[0][2]) / d; -// never mind those ifs, because they get optimized away -if (rows()!=cols()) -DUNE_THROW(FMatrixError, "Can't invert a " << rows() << "x" << cols() << " matrix!"); + x[1] = ((*this)[0][0]*b[1]*(*this)[2][2] - (*this)[0][0]*b[2]*(*this)[1][2] + - (*this)[1][0] *b[0]*(*this)[2][2] + (*this)[1][0]*b[2]*(*this)[0][2] + + (*this)[2][0] *b[0]*(*this)[1][2] - (*this)[2][0]*b[1]*(*this)[0][2]) / d; -if (rows()==1) { - -#ifdef DUNE_FMatrix_WITH_CHECKING -if (Simd::anyTrue(fvmeta::absreal((*this)[0][0]) -< FMatrixPrecision<>::absolute_limit())) -DUNE_THROW(FMatrixError,"matrix is singular"); -#endif -(*this)[0][0] = real_type( 1 ) / (*this)[0][0]; - -} -else if (rows()==2) { - -field_type detinv = (*this)[0][0]*(*this)[1][1]-(*this)[0][1]*(*this)[1][0]; -#ifdef DUNE_FMatrix_WITH_CHECKING -if (Simd::anyTrue(fvmeta::absreal(detinv) -< FMatrixPrecision<>::absolute_limit())) -DUNE_THROW(FMatrixError,"matrix is singular"); -#endif -detinv = real_type( 1 ) / detinv; + x[2] = ((*this)[0][0]*(*this)[1][1]*b[2] - (*this)[0][0]*(*this)[2][1]*b[1] + - (*this)[1][0] *(*this)[0][1]*b[2] + (*this)[1][0]*(*this)[2][1]*b[0] + + (*this)[2][0] *(*this)[0][1]*b[1] - (*this)[2][0]*(*this)[1][1]*b[0]) / d; -field_type temp=(*this)[0][0]; -(*this)[0][0] = (*this)[1][1]*detinv; -(*this)[0][1] = -(*this)[0][1]*detinv; -(*this)[1][0] = -(*this)[1][0]*detinv; -(*this)[1][1] = temp*detinv; + } + else { -} -else if (rows()==3) -{ -using K = field_type; -// code generated by maple -K t4 = (*this)[0][0] * (*this)[1][1]; -K t6 = (*this)[0][0] * (*this)[1][2]; -K t8 = (*this)[0][1] * (*this)[1][0]; -K t10 = (*this)[0][2] * (*this)[1][0]; -K t12 = (*this)[0][1] * (*this)[2][0]; -K t14 = (*this)[0][2] * (*this)[2][0]; - -K det = (t4*(*this)[2][2]-t6*(*this)[2][1]-t8*(*this)[2][2]+ -t10*(*this)[2][1]+t12*(*this)[1][2]-t14*(*this)[1][1]); -K t17 = K(1.0)/det; - -K matrix01 = (*this)[0][1]; -K matrix00 = (*this)[0][0]; -K matrix10 = (*this)[1][0]; -K matrix11 = (*this)[1][1]; - -(*this)[0][0] = ((*this)[1][1] * (*this)[2][2] - (*this)[1][2] * (*this)[2][1])*t17; -(*this)[0][1] = -((*this)[0][1] * (*this)[2][2] - (*this)[0][2] * (*this)[2][1])*t17; -(*this)[0][2] = (matrix01 * (*this)[1][2] - (*this)[0][2] * (*this)[1][1])*t17; -(*this)[1][0] = -((*this)[1][0] * (*this)[2][2] - (*this)[1][2] * (*this)[2][0])*t17; -(*this)[1][1] = (matrix00 * (*this)[2][2] - t14) * t17; -(*this)[1][2] = -(t6-t10) * t17; -(*this)[2][0] = (matrix10 * (*this)[2][1] - matrix11 * (*this)[2][0]) * t17; -(*this)[2][1] = -(matrix00 * (*this)[2][1] - t12) * t17; -(*this)[2][2] = (t4-t8) * t17; -} -else { -using std::swap; - -AutonomousValue<MAT> A(asImp()); -std::vector<simd_index_type> pivot(rows()); -Simd::Mask<typename FieldTraits<value_type>::real_type> -nonsingularLanes(true); -AutonomousValue<MAT>::luDecomposition(A, ElimPivot(pivot), nonsingularLanes, true, doPivoting); -auto& L=A; -auto& U=A; - -// initialize inverse -*this=field_type(); - -for(size_type i=0; i<rows(); ++i) -(*this)[i][i]=1; - -// L Y = I; multiple right hand sides -for (size_type i=0; i<rows(); i++) -for (size_type j=0; j<i; j++) -for (size_type k=0; k<rows(); k++) -(*this)[i][k] -= L[i][j]*(*this)[j][k]; - -// U A^{-1} = Y -for (size_type i=rows(); i>0;) { ---i; -for (size_type k=0; k<rows(); k++) { -for (size_type j=i+1; j<rows(); j++) -(*this)[i][k] -= U[i][j]*(*this)[j][k]; -(*this)[i][k] /= U[i][i]; -} -} - -for(size_type i=rows(); i>0; ) { ---i; -for(std::size_t l = 0; l < Simd::lanes((*this)[0][0]); ++l) -{ -std::size_t pi = Simd::lane(l, pivot[i]); -if(i!=pi) -for(size_type j=0; j<rows(); ++j) -swap(Simd::lane(l, (*this)[j][pi]), -Simd::lane(l, (*this)[j][ i])); -} -} -} -} - -// implementation of the determinant -template<typename MAT> -inline typename DenseMatrix<MAT>::field_type -DenseMatrix<MAT>::determinant(bool doPivoting) const -{ -// never mind those ifs, because they get optimized away -if (rows()!=cols()) -DUNE_THROW(FMatrixError, "There is no determinant for a " << rows() << "x" << cols() << " matrix!"); + V1& rhs = x; // use x to store rhs + rhs = b; // copy data + Elim<V1> elim(rhs); + AutonomousValue<MAT> A(asImp()); + Simd::Mask<typename FieldTraits<value_type>::real_type> + nonsingularLanes(true); -if (rows()==1) -return (*this)[0][0]; + AutonomousValue<MAT>::luDecomposition(A, elim, nonsingularLanes, true, doPivoting); -if (rows()==2) -return (*this)[0][0]*(*this)[1][1] - (*this)[0][1]*(*this)[1][0]; + // backsolve + for(int i=rows()-1; i>=0; i--) { + for (size_type j=i+1; j<rows(); j++) + rhs[i] -= A[i][j]*x[j]; + x[i] = rhs[i]/A[i][i]; + } + } + } -if (rows()==3) { -// code generated by maple -field_type t4 = (*this)[0][0] * (*this)[1][1]; -field_type t6 = (*this)[0][0] * (*this)[1][2]; -field_type t8 = (*this)[0][1] * (*this)[1][0]; -field_type t10 = (*this)[0][2] * (*this)[1][0]; -field_type t12 = (*this)[0][1] * (*this)[2][0]; -field_type t14 = (*this)[0][2] * (*this)[2][0]; + template<typename MAT> + inline void DenseMatrix<MAT>::invert(bool doPivoting) + { + using real_type = typename FieldTraits<MAT>::real_type; + using std::swap; -return (t4*(*this)[2][2]-t6*(*this)[2][1]-t8*(*this)[2][2]+ -t10*(*this)[2][1]+t12*(*this)[1][2]-t14*(*this)[1][1]); + // never mind those ifs, because they get optimized away + if (rows()!=cols()) + DUNE_THROW(FMatrixError, "Can't invert a " << rows() << "x" << cols() << " matrix!"); -} + if (rows()==1) { -AutonomousValue<MAT> A(asImp()); -field_type det; -Simd::Mask<typename FieldTraits<value_type>::real_type> -nonsingularLanes(true); +#ifdef DUNE_FMatrix_WITH_CHECKING + if (Simd::anyTrue(fvmeta::absreal((*this)[0][0]) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); +#endif + (*this)[0][0] = real_type( 1 ) / (*this)[0][0]; -AutonomousValue<MAT>::luDecomposition(A, ElimDet(det), nonsingularLanes, false, doPivoting); -det = Simd::cond(nonsingularLanes, det, field_type(0)); + } + else if (rows()==2) { -for (size_type i = 0; i < rows(); ++i) -det *= A[i][i]; -return det; -} + field_type detinv = (*this)[0][0]*(*this)[1][1]-(*this)[0][1]*(*this)[1][0]; +#ifdef DUNE_FMatrix_WITH_CHECKING + if (Simd::anyTrue(fvmeta::absreal(detinv) + < FMatrixPrecision<>::absolute_limit())) + DUNE_THROW(FMatrixError,"matrix is singular"); +#endif + detinv = real_type( 1 ) / detinv; + + field_type temp=(*this)[0][0]; + (*this)[0][0] = (*this)[1][1]*detinv; + (*this)[0][1] = -(*this)[0][1]*detinv; + (*this)[1][0] = -(*this)[1][0]*detinv; + (*this)[1][1] = temp*detinv; + + } + else if (rows()==3) + { + using K = field_type; + // code generated by maple + K t4 = (*this)[0][0] * (*this)[1][1]; + K t6 = (*this)[0][0] * (*this)[1][2]; + K t8 = (*this)[0][1] * (*this)[1][0]; + K t10 = (*this)[0][2] * (*this)[1][0]; + K t12 = (*this)[0][1] * (*this)[2][0]; + K t14 = (*this)[0][2] * (*this)[2][0]; + + K det = (t4*(*this)[2][2]-t6*(*this)[2][1]-t8*(*this)[2][2]+ + t10*(*this)[2][1]+t12*(*this)[1][2]-t14*(*this)[1][1]); + K t17 = K(1.0)/det; + + K matrix01 = (*this)[0][1]; + K matrix00 = (*this)[0][0]; + K matrix10 = (*this)[1][0]; + K matrix11 = (*this)[1][1]; + + (*this)[0][0] = ((*this)[1][1] * (*this)[2][2] - (*this)[1][2] * (*this)[2][1])*t17; + (*this)[0][1] = -((*this)[0][1] * (*this)[2][2] - (*this)[0][2] * (*this)[2][1])*t17; + (*this)[0][2] = (matrix01 * (*this)[1][2] - (*this)[0][2] * (*this)[1][1])*t17; + (*this)[1][0] = -((*this)[1][0] * (*this)[2][2] - (*this)[1][2] * (*this)[2][0])*t17; + (*this)[1][1] = (matrix00 * (*this)[2][2] - t14) * t17; + (*this)[1][2] = -(t6-t10) * t17; + (*this)[2][0] = (matrix10 * (*this)[2][1] - matrix11 * (*this)[2][0]) * t17; + (*this)[2][1] = -(matrix00 * (*this)[2][1] - t12) * t17; + (*this)[2][2] = (t4-t8) * t17; + } + else { + using std::swap; + + AutonomousValue<MAT> A(asImp()); + std::vector<simd_index_type> pivot(rows()); + Simd::Mask<typename FieldTraits<value_type>::real_type> + nonsingularLanes(true); + AutonomousValue<MAT>::luDecomposition(A, ElimPivot(pivot), nonsingularLanes, true, doPivoting); + auto& L=A; + auto& U=A; + + // initialize inverse + *this=field_type(); + + for(size_type i=0; i<rows(); ++i) + (*this)[i][i]=1; + + // L Y = I; multiple right hand sides + for (size_type i=0; i<rows(); i++) + for (size_type j=0; j<i; j++) + for (size_type k=0; k<rows(); k++) + (*this)[i][k] -= L[i][j]*(*this)[j][k]; + + // U A^{-1} = Y + for (size_type i=rows(); i>0;) { + --i; + for (size_type k=0; k<rows(); k++) { + for (size_type j=i+1; j<rows(); j++) + (*this)[i][k] -= U[i][j]*(*this)[j][k]; + (*this)[i][k] /= U[i][i]; + } + } + + for(size_type i=rows(); i>0; ) { + --i; + for(std::size_t l = 0; l < Simd::lanes((*this)[0][0]); ++l) + { + std::size_t pi = Simd::lane(l, pivot[i]); + if(i!=pi) + for(size_type j=0; j<rows(); ++j) + swap(Simd::lane(l, (*this)[j][pi]), + Simd::lane(l, (*this)[j][ i])); + } + } + } + } + + // implementation of the determinant + template<typename MAT> + inline typename DenseMatrix<MAT>::field_type + DenseMatrix<MAT>::determinant(bool doPivoting) const + { + // never mind those ifs, because they get optimized away + if (rows()!=cols()) + DUNE_THROW(FMatrixError, "There is no determinant for a " << rows() << "x" << cols() << " matrix!"); + + if (rows()==1) + return (*this)[0][0]; + + if (rows()==2) + return (*this)[0][0]*(*this)[1][1] - (*this)[0][1]*(*this)[1][0]; + + if (rows()==3) { + // code generated by maple + field_type t4 = (*this)[0][0] * (*this)[1][1]; + field_type t6 = (*this)[0][0] * (*this)[1][2]; + field_type t8 = (*this)[0][1] * (*this)[1][0]; + field_type t10 = (*this)[0][2] * (*this)[1][0]; + field_type t12 = (*this)[0][1] * (*this)[2][0]; + field_type t14 = (*this)[0][2] * (*this)[2][0]; + + return (t4*(*this)[2][2]-t6*(*this)[2][1]-t8*(*this)[2][2]+ + t10*(*this)[2][1]+t12*(*this)[1][2]-t14*(*this)[1][1]); + + } + + AutonomousValue<MAT> A(asImp()); + field_type det; + Simd::Mask<typename FieldTraits<value_type>::real_type> + nonsingularLanes(true); + + AutonomousValue<MAT>::luDecomposition(A, ElimDet(det), nonsingularLanes, false, doPivoting); + det = Simd::cond(nonsingularLanes, det, field_type(0)); + + for (size_type i = 0; i < rows(); ++i) + det *= A[i][i]; + return det; + } #endif // DOXYGEN -namespace DenseMatrixHelp { - -//! calculates ret = matrix * x -template <typename MAT, typename V1, typename V2> -static inline void multAssign(const DenseMatrix<MAT> &matrix, const DenseVector<V1> & x, DenseVector<V2> & ret) -{ -DUNE_ASSERT_BOUNDS(x.size() == matrix.cols()); -DUNE_ASSERT_BOUNDS(ret.size() == matrix.rows()); -typedef typename DenseMatrix<MAT>::size_type size_type; - -for(size_type i=0; i<matrix.rows(); ++i) -{ -ret[i] = 0.0; -for(size_type j=0; j<matrix.cols(); ++j) -{ -ret[i] += matrix[i][j]*x[j]; -} -} -} + namespace DenseMatrixHelp { + + //! calculates ret = matrix * x + template <typename MAT, typename V1, typename V2> + static inline void multAssign(const DenseMatrix<MAT> &matrix, const DenseVector<V1> & x, DenseVector<V2> & ret) + { + DUNE_ASSERT_BOUNDS(x.size() == matrix.cols()); + DUNE_ASSERT_BOUNDS(ret.size() == matrix.rows()); + typedef typename DenseMatrix<MAT>::size_type size_type; + + for(size_type i=0; i<matrix.rows(); ++i) + { + ret[i] = 0.0; + for(size_type j=0; j<matrix.cols(); ++j) + { + ret[i] += matrix[i][j]*x[j]; + } + } + } #if 0 -//! calculates ret = matrix^T * x -template <typename K, int rows, int cols> -static inline void multAssignTransposed( const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x, FieldVector<K,cols> & ret) -{ -typedef typename FieldMatrix<K,rows,cols>::size_type size_type; - -for(size_type i=0; i<cols(); ++i) -{ -ret[i] = 0.0; -for(size_type j=0; j<rows(); ++j) -ret[i] += matrix[j][i]*x[j]; -} -} - -//! calculates ret = matrix * x -template <typename K, int rows, int cols> -static inline FieldVector<K,rows> mult(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,cols> & x) -{ -FieldVector<K,rows> ret; -multAssign(matrix,x,ret); -return ret; -} - -//! calculates ret = matrix^T * x -template <typename K, int rows, int cols> -static inline FieldVector<K,cols> multTransposed(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x) -{ -FieldVector<K,cols> ret; -multAssignTransposed( matrix, x, ret ); -return ret; -} + //! calculates ret = matrix^T * x + template <typename K, int rows, int cols> + static inline void multAssignTransposed( const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x, FieldVector<K,cols> & ret) + { + typedef typename FieldMatrix<K,rows,cols>::size_type size_type; + + for(size_type i=0; i<cols(); ++i) + { + ret[i] = 0.0; + for(size_type j=0; j<rows(); ++j) + ret[i] += matrix[j][i]*x[j]; + } + } + + //! calculates ret = matrix * x + template <typename K, int rows, int cols> + static inline FieldVector<K,rows> mult(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,cols> & x) + { + FieldVector<K,rows> ret; + multAssign(matrix,x,ret); + return ret; + } + + //! calculates ret = matrix^T * x + template <typename K, int rows, int cols> + static inline FieldVector<K,cols> multTransposed(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x) + { + FieldVector<K,cols> ret; + multAssignTransposed( matrix, x, ret ); + return ret; + } #endif -} // end namespace DenseMatrixHelp + } // end namespace DenseMatrixHelp -/** \brief Sends the matrix to an output stream */ -template<typename MAT> -std::ostream& operator<< (std::ostream& s, const DenseMatrix<MAT>& a) -{ -for (typename DenseMatrix<MAT>::size_type i=0; i<a.rows(); i++) -s << a[i] << std::endl; -return s; -} + /** \brief Sends the matrix to an output stream */ + template<typename MAT> + std::ostream& operator<< (std::ostream& s, const DenseMatrix<MAT>& a) + { + for (typename DenseMatrix<MAT>::size_type i=0; i<a.rows(); i++) + s << a[i] << std::endl; + return s; + } -/** @} end documentation */ + /** @} end documentation */ } // end namespace Dune diff --git a/dune/common/densevector.hh b/dune/common/densevector.hh index 22376484631f90006570cd2b4d475b2f1d6dfb5f..f37c02d2ff745575bc15a81606c257a7106857ae 100644 --- a/dune/common/densevector.hh +++ b/dune/common/densevector.hh @@ -17,745 +17,745 @@ namespace Dune { -// forward declaration of template -template<typename V> class DenseVector; - -template<typename V> -struct FieldTraits< DenseVector<V> > -{ -typedef typename FieldTraits< typename DenseMatVecTraits<V>::value_type >::field_type field_type; -typedef typename FieldTraits< typename DenseMatVecTraits<V>::value_type >::real_type real_type; -}; - -/** @defgroup DenseMatVec Dense Matrix and Vector Template Library -@ingroup Common -@{ -*/ - -/*! \file -* \brief Implements the dense vector interface, with an exchangeable storage class -*/ - -namespace fvmeta -{ -/** -\private -\memberof Dune::DenseVector -*/ -template<class K> -inline typename FieldTraits<K>::real_type absreal (const K& k) -{ -using std::abs; -return abs(k); -} - -/** -\private -\memberof Dune::DenseVector -*/ -template<class K> -inline typename FieldTraits<K>::real_type absreal (const std::complex<K>& c) -{ -using std::abs; -return abs(c.real()) + abs(c.imag()); -} - -/** -\private -\memberof Dune::DenseVector -*/ -template<class K> -inline typename FieldTraits<K>::real_type abs2 (const K& k) -{ -return k*k; -} - -/** -\private -\memberof Dune::DenseVector -*/ -template<class K> -inline typename FieldTraits<K>::real_type abs2 (const std::complex<K>& c) -{ -return c.real()*c.real() + c.imag()*c.imag(); -} - -/** -\private -\memberof Dune::DenseVector -*/ -template<class K, bool isInteger = std::numeric_limits<K>::is_integer> -struct Sqrt -{ -static inline typename FieldTraits<K>::real_type sqrt (const K& k) -{ -using std::sqrt; -return sqrt(k); -} -}; - -/** -\private -\memberof Dune::DenseVector -*/ -template<class K> -struct Sqrt<K, true> -{ -static inline typename FieldTraits<K>::real_type sqrt (const K& k) -{ -using std::sqrt; -return typename FieldTraits<K>::real_type(sqrt(double(k))); -} -}; - -/** -\private -\memberof Dune::DenseVector -*/ -template<class K> -inline typename FieldTraits<K>::real_type sqrt (const K& k) -{ -return Sqrt<K>::sqrt(k); -} - -} - -/*! \brief Generic iterator class for dense vector and matrix implementations - -provides sequential access to DenseVector, FieldVector and FieldMatrix -*/ -template<class C, class T, class R =T&> -class DenseIterator : -public Dune::RandomAccessIteratorFacade<DenseIterator<C,T,R>,T, R, std::ptrdiff_t> -{ -friend class DenseIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type >; -friend class DenseIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type >; - -typedef DenseIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type > MutableIterator; -typedef DenseIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type > ConstIterator; -public: - -/** -* @brief The type of the difference between two positions. -*/ -typedef std::ptrdiff_t DifferenceType; - -/** -* @brief The type to index the underlying container. -*/ -typedef typename C::size_type SizeType; - -// Constructors needed by the base iterators. -DenseIterator() -: container_(0), position_() -{} - -DenseIterator(C& cont, SizeType pos) -: container_(&cont), position_(pos) -{} - -DenseIterator(const MutableIterator & other) -: container_(other.container_), position_(other.position_) -{} - -DenseIterator(const ConstIterator & other) -: container_(other.container_), position_(other.position_) -{} - -// Methods needed by the forward iterator -bool equals(const MutableIterator &other) const -{ -return position_ == other.position_ && container_ == other.container_; -} - - -bool equals(const ConstIterator & other) const -{ -return position_ == other.position_ && container_ == other.container_; -} - -R dereference() const { -return container_->operator[](position_); -} - -void increment(){ -++position_; -} - -// Additional function needed by BidirectionalIterator -void decrement(){ ---position_; -} - -// Additional function needed by RandomAccessIterator -R elementAt(DifferenceType i) const { -return container_->operator[](position_+i); -} - -void advance(DifferenceType n){ -position_=position_+n; -} - -DifferenceType distanceTo(DenseIterator<const typename std::remove_const<C>::type,const typename std::remove_const<T>::type> other) const -{ -assert(other.container_==container_); -return static_cast< DifferenceType >( other.position_ ) - static_cast< DifferenceType >( position_ ); -} - -DifferenceType distanceTo(DenseIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type> other) const -{ -assert(other.container_==container_); -return static_cast< DifferenceType >( other.position_ ) - static_cast< DifferenceType >( position_ ); -} - -//! return index -SizeType index () const -{ -return this->position_; -} - -private: -C *container_; -SizeType position_; -}; - -/** \brief Interface for a class of dense vectors over a given field. -* -* \tparam V implementation class of the vector -*/ -template<typename V> -class DenseVector -{ -typedef DenseMatVecTraits<V> Traits; -// typedef typename Traits::value_type K; - -// Curiously recurring template pattern -V & asImp() { return static_cast<V&>(*this); } -const V & asImp() const { return static_cast<const V&>(*this); } - -protected: -// construction allowed to derived classes only -constexpr DenseVector() = default; -// copying only allowed by derived classes -DenseVector(const DenseVector&) = default; - -public: -//===== type definitions and constants - -//! type of derived vector class -typedef typename Traits::derived_type derived_type; - -//! export the type representing the field -typedef typename Traits::value_type value_type; - -//! export the type representing the field -typedef typename FieldTraits< value_type >::field_type field_type; - -//! export the type representing the components -typedef typename Traits::value_type block_type; - -//! The type used for the index access and size operation -typedef typename Traits::size_type size_type; - -//! We are at the leaf of the block recursion -enum { -//! The number of block levels we contain -blocklevel = 1 -}; - -//===== assignment from scalar -//! Assignment operator for scalar -inline derived_type& operator= (const value_type& k) -{ -for (size_type i=0; i<size(); i++) -asImp()[i] = k; -return asImp(); -} - -//===== assignment from other DenseVectors -protected: -//! Assignment operator for other DenseVector of same type -DenseVector& operator=(const DenseVector&) = default; - -public: - -//! Assignment operator for other DenseVector of different type -template <typename W, -std::enable_if_t< -std::is_assignable<value_type&, typename DenseVector<W>::value_type>::value, int> = 0> -derived_type& operator= (const DenseVector<W>& other) -{ -assert(other.size() == size()); -for (size_type i=0; i<size(); i++) -asImp()[i] = other[i]; -return asImp(); -} - -//===== access to components - -//! random access -value_type & operator[] (size_type i) -{ -return asImp()[i]; -} - -const value_type & operator[] (size_type i) const -{ -return asImp()[i]; -} - -//! return reference to first element -value_type& front() -{ -return asImp()[0]; -} - -//! return reference to first element -const value_type& front() const -{ -return asImp()[0]; -} - -//! return reference to last element -value_type& back() -{ -return asImp()[size()-1]; -} - -//! return reference to last element -const value_type& back() const -{ -return asImp()[size()-1]; -} - -//! checks whether the container is empty -bool empty() const -{ -return size() == 0; -} - -//! size method -size_type size() const -{ -return asImp().size(); -} - -//! Iterator class for sequential access -typedef DenseIterator<DenseVector,value_type> Iterator; -//! typedef for stl compliant access -typedef Iterator iterator; - -//! begin iterator -Iterator begin () -{ -return Iterator(*this,0); -} - -//! end iterator -Iterator end () -{ -return Iterator(*this,size()); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the vector, i.e. at the last entry. -Iterator beforeEnd () -{ -return Iterator(*this,size()-1); -} - -//! @returns an iterator that is positioned before -//! the first entry of the vector. -Iterator beforeBegin () -{ -return Iterator(*this,-1); -} - -//! return iterator to given element or end() -Iterator find (size_type i) -{ -return Iterator(*this,std::min(i,size())); -} - -//! ConstIterator class for sequential access -typedef DenseIterator<const DenseVector,const value_type> ConstIterator; -//! typedef for stl compliant access -typedef ConstIterator const_iterator; - -//! begin ConstIterator -ConstIterator begin () const -{ -return ConstIterator(*this,0); -} - -//! end ConstIterator -ConstIterator end () const -{ -return ConstIterator(*this,size()); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the vector. i.e. at the last element -ConstIterator beforeEnd () const -{ -return ConstIterator(*this,size()-1); -} - -//! @returns an iterator that is positioned before -//! the first entry of the vector. -ConstIterator beforeBegin () const -{ -return ConstIterator(*this,-1); -} - -//! return iterator to given element or end() -ConstIterator find (size_type i) const -{ -return ConstIterator(*this,std::min(i,size())); -} - -//===== vector space arithmetic - -//! vector space addition -template <class Other> -derived_type& operator+= (const DenseVector<Other>& x) -{ -DUNE_ASSERT_BOUNDS(x.size() == size()); -for (size_type i=0; i<size(); i++) -(*this)[i] += x[i]; -return asImp(); -} - -//! vector space subtraction -template <class Other> -derived_type& operator-= (const DenseVector<Other>& x) -{ -DUNE_ASSERT_BOUNDS(x.size() == size()); -for (size_type i=0; i<size(); i++) -(*this)[i] -= x[i]; -return asImp(); -} - -//! Binary vector addition -template <class Other> -derived_type operator+ (const DenseVector<Other>& b) const -{ -derived_type z = asImp(); -return (z+=b); -} - -//! Binary vector subtraction -template <class Other> -derived_type operator- (const DenseVector<Other>& b) const -{ -derived_type z = asImp(); -return (z-=b); -} - -//! Vector negation -derived_type operator- () const -{ -V result; -typedef typename decltype(result)::size_type size_type; - -for (size_type i = 0; i < size(); ++i) -result[i] = -asImp()[i]; - -return result; -} - -//! \brief vector space add scalar to all comps -/** -we use enable_if to avoid an ambiguity, if the -function parameter can be converted to value_type implicitly. -(see FS#1457) - -The function is only enabled, if the parameter is directly -convertible to value_type. -*/ -template <typename ValueType> -typename std::enable_if< -std::is_convertible<ValueType, value_type>::value, -derived_type ->::type& -operator+= (const ValueType& kk) -{ -const value_type& k = kk; -for (size_type i=0; i<size(); i++) -(*this)[i] += k; -return asImp(); -} - -//! \brief vector space subtract scalar from all comps -/** -we use enable_if to avoid an ambiguity, if the -function parameter can be converted to value_type implicitly. -(see FS#1457) - -The function is only enabled, if the parameter is directly -convertible to value_type. -*/ -template <typename ValueType> -typename std::enable_if< -std::is_convertible<ValueType, value_type>::value, -derived_type ->::type& -operator-= (const ValueType& kk) -{ -const value_type& k = kk; -for (size_type i=0; i<size(); i++) -(*this)[i] -= k; -return asImp(); -} - -//! \brief vector space multiplication with scalar -/** -we use enable_if to avoid an ambiguity, if the -function parameter can be converted to field_type implicitly. -(see FS#1457) - -The function is only enabled, if the parameter is directly -convertible to field_type. -*/ -template <typename FieldType> -typename std::enable_if< -std::is_convertible<FieldType, field_type>::value, -derived_type ->::type& -operator*= (const FieldType& kk) -{ -const field_type& k = kk; -for (size_type i=0; i<size(); i++) -(*this)[i] *= k; -return asImp(); -} - -//! \brief vector space division by scalar -/** -we use enable_if to avoid an ambiguity, if the -function parameter can be converted to field_type implicitly. -(see FS#1457) - -The function is only enabled, if the parameter is directly -convertible to field_type. -*/ -template <typename FieldType> -typename std::enable_if< -std::is_convertible<FieldType, field_type>::value, -derived_type ->::type& -operator/= (const FieldType& kk) -{ -const field_type& k = kk; -for (size_type i=0; i<size(); i++) -(*this)[i] /= k; -return asImp(); -} - -//! Binary vector comparison -template <class Other> -bool operator== (const DenseVector<Other>& x) const -{ -DUNE_ASSERT_BOUNDS(x.size() == size()); -for (size_type i=0; i<size(); i++) -if ((*this)[i]!=x[i]) -return false; - -return true; -} - -//! Binary vector incomparison -template <class Other> -bool operator!= (const DenseVector<Other>& x) const -{ -return !operator==(x); -} - - -//! vector space axpy operation ( *this += a x ) -template <class Other> -derived_type& axpy (const field_type& a, const DenseVector<Other>& x) -{ -DUNE_ASSERT_BOUNDS(x.size() == size()); -for (size_type i=0; i<size(); i++) -(*this)[i] += a*x[i]; -return asImp(); -} - -/** -* \brief indefinite vector dot product \f$\left (x^T \cdot y \right)\f$ which corresponds to Petsc's VecTDot -* -* http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html -* @param x other vector -* @return -*/ -template<class Other> -typename PromotionTraits<field_type,typename DenseVector<Other>::field_type>::PromotedType operator* (const DenseVector<Other>& x) const { -typedef typename PromotionTraits<field_type, typename DenseVector<Other>::field_type>::PromotedType PromotedType; -PromotedType result(0); -assert(x.size() == size()); -for (size_type i=0; i<size(); i++) { -result += PromotedType((*this)[i]*x[i]); -} -return result; -} - -/** -* @brief vector dot product \f$\left (x^H \cdot y \right)\f$ which corresponds to Petsc's VecDot -* -* http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecDot.html -* @param x other vector -* @return -*/ -template<class Other> -typename PromotionTraits<field_type,typename DenseVector<Other>::field_type>::PromotedType dot(const DenseVector<Other>& x) const { -typedef typename PromotionTraits<field_type, typename DenseVector<Other>::field_type>::PromotedType PromotedType; -PromotedType result(0); -assert(x.size() == size()); -for (size_type i=0; i<size(); i++) { -result += Dune::dot((*this)[i],x[i]); -} -return result; -} - -//===== norms - -//! one norm (sum over absolute values of entries) -typename FieldTraits<value_type>::real_type one_norm() const { -using std::abs; -typename FieldTraits<value_type>::real_type result( 0 ); -for (size_type i=0; i<size(); i++) -result += abs((*this)[i]); -return result; -} - - -//! simplified one norm (uses Manhattan norm for complex values) -typename FieldTraits<value_type>::real_type one_norm_real () const -{ -typename FieldTraits<value_type>::real_type result( 0 ); -for (size_type i=0; i<size(); i++) -result += fvmeta::absreal((*this)[i]); -return result; -} - -//! two norm sqrt(sum over squared values of entries) -typename FieldTraits<value_type>::real_type two_norm () const -{ -typename FieldTraits<value_type>::real_type result( 0 ); -for (size_type i=0; i<size(); i++) -result += fvmeta::abs2((*this)[i]); -return fvmeta::sqrt(result); -} - -//! square of two norm (sum over squared values of entries), need for block recursion -typename FieldTraits<value_type>::real_type two_norm2 () const -{ -typename FieldTraits<value_type>::real_type result( 0 ); -for (size_type i=0; i<size(); i++) -result += fvmeta::abs2((*this)[i]); -return result; -} - -//! infinity norm (maximum of absolute values of entries) -template <typename vt = value_type, -typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::abs; -using std::max; - -real_type norm = 0; -for (auto const &x : *this) { -real_type const a = abs(x); -norm = max(a, norm); -} -return norm; -} - -//! simplified infinity norm (uses Manhattan norm for complex values) -template <typename vt = value_type, -typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm_real() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::max; - -real_type norm = 0; -for (auto const &x : *this) { -real_type const a = fvmeta::absreal(x); -norm = max(a, norm); -} -return norm; -} - -//! infinity norm (maximum of absolute values of entries) -template <typename vt = value_type, -typename std::enable_if<HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::abs; -using std::max; - -real_type norm = 0; -real_type isNaN = 1; -for (auto const &x : *this) { -real_type const a = abs(x); -norm = max(a, norm); -isNaN += a; -} -return norm * (isNaN / isNaN); -} - -//! simplified infinity norm (uses Manhattan norm for complex values) -template <typename vt = value_type, -typename std::enable_if<HasNaN<vt>::value, int>::type = 0> -typename FieldTraits<vt>::real_type infinity_norm_real() const { -using real_type = typename FieldTraits<vt>::real_type; -using std::max; - -real_type norm = 0; -real_type isNaN = 1; -for (auto const &x : *this) { -real_type const a = fvmeta::absreal(x); -norm = max(a, norm); -isNaN += a; -} -return norm * (isNaN / isNaN); -} - -//===== sizes - -//! number of blocks in the vector (are of size 1 here) -size_type N () const -{ -return size(); -} - -//! dimension of the vector space -size_type dim () const -{ -return size(); -} - -}; - -/** \brief Write a DenseVector to an output stream -* \relates DenseVector -* -* \param[in] s std :: ostream to write to -* \param[in] v DenseVector to write -* -* \returns the output stream (s) -*/ -template<typename V> -std::ostream& operator<< (std::ostream& s, const DenseVector<V>& v) -{ -for (typename DenseVector<V>::size_type i=0; i<v.size(); i++) -s << ((i>0) ? " " : "") << v[i]; -return s; -} - -/** @} end documentation */ + // forward declaration of template + template<typename V> class DenseVector; + + template<typename V> + struct FieldTraits< DenseVector<V> > + { + typedef typename FieldTraits< typename DenseMatVecTraits<V>::value_type >::field_type field_type; + typedef typename FieldTraits< typename DenseMatVecTraits<V>::value_type >::real_type real_type; + }; + + /** @defgroup DenseMatVec Dense Matrix and Vector Template Library + @ingroup Common + @{ + */ + + /*! \file + * \brief Implements the dense vector interface, with an exchangeable storage class + */ + + namespace fvmeta + { + /** + \private + \memberof Dune::DenseVector + */ + template<class K> + inline typename FieldTraits<K>::real_type absreal (const K& k) + { + using std::abs; + return abs(k); + } + + /** + \private + \memberof Dune::DenseVector + */ + template<class K> + inline typename FieldTraits<K>::real_type absreal (const std::complex<K>& c) + { + using std::abs; + return abs(c.real()) + abs(c.imag()); + } + + /** + \private + \memberof Dune::DenseVector + */ + template<class K> + inline typename FieldTraits<K>::real_type abs2 (const K& k) + { + return k*k; + } + + /** + \private + \memberof Dune::DenseVector + */ + template<class K> + inline typename FieldTraits<K>::real_type abs2 (const std::complex<K>& c) + { + return c.real()*c.real() + c.imag()*c.imag(); + } + + /** + \private + \memberof Dune::DenseVector + */ + template<class K, bool isInteger = std::numeric_limits<K>::is_integer> + struct Sqrt + { + static inline typename FieldTraits<K>::real_type sqrt (const K& k) + { + using std::sqrt; + return sqrt(k); + } + }; + + /** + \private + \memberof Dune::DenseVector + */ + template<class K> + struct Sqrt<K, true> + { + static inline typename FieldTraits<K>::real_type sqrt (const K& k) + { + using std::sqrt; + return typename FieldTraits<K>::real_type(sqrt(double(k))); + } + }; + + /** + \private + \memberof Dune::DenseVector + */ + template<class K> + inline typename FieldTraits<K>::real_type sqrt (const K& k) + { + return Sqrt<K>::sqrt(k); + } + + } + + /*! \brief Generic iterator class for dense vector and matrix implementations + + provides sequential access to DenseVector, FieldVector and FieldMatrix + */ + template<class C, class T, class R =T&> + class DenseIterator : + public Dune::RandomAccessIteratorFacade<DenseIterator<C,T,R>,T, R, std::ptrdiff_t> + { + friend class DenseIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type >; + friend class DenseIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type >; + + typedef DenseIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type > MutableIterator; + typedef DenseIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type > ConstIterator; + public: + + /** + * @brief The type of the difference between two positions. + */ + typedef std::ptrdiff_t DifferenceType; + + /** + * @brief The type to index the underlying container. + */ + typedef typename C::size_type SizeType; + + // Constructors needed by the base iterators. + DenseIterator() + : container_(0), position_() + {} + + DenseIterator(C& cont, SizeType pos) + : container_(&cont), position_(pos) + {} + + DenseIterator(const MutableIterator & other) + : container_(other.container_), position_(other.position_) + {} + + DenseIterator(const ConstIterator & other) + : container_(other.container_), position_(other.position_) + {} + + // Methods needed by the forward iterator + bool equals(const MutableIterator &other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + + bool equals(const ConstIterator & other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + R dereference() const { + return container_->operator[](position_); + } + + void increment(){ + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement(){ + --position_; + } + + // Additional function needed by RandomAccessIterator + R elementAt(DifferenceType i) const { + return container_->operator[](position_+i); + } + + void advance(DifferenceType n){ + position_=position_+n; + } + + DifferenceType distanceTo(DenseIterator<const typename std::remove_const<C>::type,const typename std::remove_const<T>::type> other) const + { + assert(other.container_==container_); + return static_cast< DifferenceType >( other.position_ ) - static_cast< DifferenceType >( position_ ); + } + + DifferenceType distanceTo(DenseIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type> other) const + { + assert(other.container_==container_); + return static_cast< DifferenceType >( other.position_ ) - static_cast< DifferenceType >( position_ ); + } + + //! return index + SizeType index () const + { + return this->position_; + } + + private: + C *container_; + SizeType position_; + }; + + /** \brief Interface for a class of dense vectors over a given field. + * + * \tparam V implementation class of the vector + */ + template<typename V> + class DenseVector + { + typedef DenseMatVecTraits<V> Traits; + // typedef typename Traits::value_type K; + + // Curiously recurring template pattern + V & asImp() { return static_cast<V&>(*this); } + const V & asImp() const { return static_cast<const V&>(*this); } + + protected: + // construction allowed to derived classes only + constexpr DenseVector() = default; + // copying only allowed by derived classes + DenseVector(const DenseVector&) = default; + + public: + //===== type definitions and constants + + //! type of derived vector class + typedef typename Traits::derived_type derived_type; + + //! export the type representing the field + typedef typename Traits::value_type value_type; + + //! export the type representing the field + typedef typename FieldTraits< value_type >::field_type field_type; + + //! export the type representing the components + typedef typename Traits::value_type block_type; + + //! The type used for the index access and size operation + typedef typename Traits::size_type size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain + blocklevel = 1 + }; + + //===== assignment from scalar + //! Assignment operator for scalar + inline derived_type& operator= (const value_type& k) + { + for (size_type i=0; i<size(); i++) + asImp()[i] = k; + return asImp(); + } + + //===== assignment from other DenseVectors + protected: + //! Assignment operator for other DenseVector of same type + DenseVector& operator=(const DenseVector&) = default; + + public: + + //! Assignment operator for other DenseVector of different type + template <typename W, + std::enable_if_t< + std::is_assignable<value_type&, typename DenseVector<W>::value_type>::value, int> = 0> + derived_type& operator= (const DenseVector<W>& other) + { + assert(other.size() == size()); + for (size_type i=0; i<size(); i++) + asImp()[i] = other[i]; + return asImp(); + } + + //===== access to components + + //! random access + value_type & operator[] (size_type i) + { + return asImp()[i]; + } + + const value_type & operator[] (size_type i) const + { + return asImp()[i]; + } + + //! return reference to first element + value_type& front() + { + return asImp()[0]; + } + + //! return reference to first element + const value_type& front() const + { + return asImp()[0]; + } + + //! return reference to last element + value_type& back() + { + return asImp()[size()-1]; + } + + //! return reference to last element + const value_type& back() const + { + return asImp()[size()-1]; + } + + //! checks whether the container is empty + bool empty() const + { + return size() == 0; + } + + //! size method + size_type size() const + { + return asImp().size(); + } + + //! Iterator class for sequential access + typedef DenseIterator<DenseVector,value_type> Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + + //! begin iterator + Iterator begin () + { + return Iterator(*this,0); + } + + //! end iterator + Iterator end () + { + return Iterator(*this,size()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector, i.e. at the last entry. + Iterator beforeEnd () + { + return Iterator(*this,size()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + Iterator beforeBegin () + { + return Iterator(*this,-1); + } + + //! return iterator to given element or end() + Iterator find (size_type i) + { + return Iterator(*this,std::min(i,size())); + } + + //! ConstIterator class for sequential access + typedef DenseIterator<const DenseVector,const value_type> ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + + //! begin ConstIterator + ConstIterator begin () const + { + return ConstIterator(*this,0); + } + + //! end ConstIterator + ConstIterator end () const + { + return ConstIterator(*this,size()); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the vector. i.e. at the last element + ConstIterator beforeEnd () const + { + return ConstIterator(*this,size()-1); + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + ConstIterator beforeBegin () const + { + return ConstIterator(*this,-1); + } + + //! return iterator to given element or end() + ConstIterator find (size_type i) const + { + return ConstIterator(*this,std::min(i,size())); + } + + //===== vector space arithmetic + + //! vector space addition + template <class Other> + derived_type& operator+= (const DenseVector<Other>& x) + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i<size(); i++) + (*this)[i] += x[i]; + return asImp(); + } + + //! vector space subtraction + template <class Other> + derived_type& operator-= (const DenseVector<Other>& x) + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i<size(); i++) + (*this)[i] -= x[i]; + return asImp(); + } + + //! Binary vector addition + template <class Other> + derived_type operator+ (const DenseVector<Other>& b) const + { + derived_type z = asImp(); + return (z+=b); + } + + //! Binary vector subtraction + template <class Other> + derived_type operator- (const DenseVector<Other>& b) const + { + derived_type z = asImp(); + return (z-=b); + } + + //! Vector negation + derived_type operator- () const + { + V result; + typedef typename decltype(result)::size_type size_type; + + for (size_type i = 0; i < size(); ++i) + result[i] = -asImp()[i]; + + return result; + } + + //! \brief vector space add scalar to all comps + /** + we use enable_if to avoid an ambiguity, if the + function parameter can be converted to value_type implicitly. + (see FS#1457) + + The function is only enabled, if the parameter is directly + convertible to value_type. + */ + template <typename ValueType> + typename std::enable_if< + std::is_convertible<ValueType, value_type>::value, + derived_type + >::type& + operator+= (const ValueType& kk) + { + const value_type& k = kk; + for (size_type i=0; i<size(); i++) + (*this)[i] += k; + return asImp(); + } + + //! \brief vector space subtract scalar from all comps + /** + we use enable_if to avoid an ambiguity, if the + function parameter can be converted to value_type implicitly. + (see FS#1457) + + The function is only enabled, if the parameter is directly + convertible to value_type. + */ + template <typename ValueType> + typename std::enable_if< + std::is_convertible<ValueType, value_type>::value, + derived_type + >::type& + operator-= (const ValueType& kk) + { + const value_type& k = kk; + for (size_type i=0; i<size(); i++) + (*this)[i] -= k; + return asImp(); + } + + //! \brief vector space multiplication with scalar + /** + we use enable_if to avoid an ambiguity, if the + function parameter can be converted to field_type implicitly. + (see FS#1457) + + The function is only enabled, if the parameter is directly + convertible to field_type. + */ + template <typename FieldType> + typename std::enable_if< + std::is_convertible<FieldType, field_type>::value, + derived_type + >::type& + operator*= (const FieldType& kk) + { + const field_type& k = kk; + for (size_type i=0; i<size(); i++) + (*this)[i] *= k; + return asImp(); + } + + //! \brief vector space division by scalar + /** + we use enable_if to avoid an ambiguity, if the + function parameter can be converted to field_type implicitly. + (see FS#1457) + + The function is only enabled, if the parameter is directly + convertible to field_type. + */ + template <typename FieldType> + typename std::enable_if< + std::is_convertible<FieldType, field_type>::value, + derived_type + >::type& + operator/= (const FieldType& kk) + { + const field_type& k = kk; + for (size_type i=0; i<size(); i++) + (*this)[i] /= k; + return asImp(); + } + + //! Binary vector comparison + template <class Other> + bool operator== (const DenseVector<Other>& x) const + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i<size(); i++) + if ((*this)[i]!=x[i]) + return false; + + return true; + } + + //! Binary vector incomparison + template <class Other> + bool operator!= (const DenseVector<Other>& x) const + { + return !operator==(x); + } + + + //! vector space axpy operation ( *this += a x ) + template <class Other> + derived_type& axpy (const field_type& a, const DenseVector<Other>& x) + { + DUNE_ASSERT_BOUNDS(x.size() == size()); + for (size_type i=0; i<size(); i++) + (*this)[i] += a*x[i]; + return asImp(); + } + + /** + * \brief indefinite vector dot product \f$\left (x^T \cdot y \right)\f$ which corresponds to Petsc's VecTDot + * + * http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html + * @param x other vector + * @return + */ + template<class Other> + typename PromotionTraits<field_type,typename DenseVector<Other>::field_type>::PromotedType operator* (const DenseVector<Other>& x) const { + typedef typename PromotionTraits<field_type, typename DenseVector<Other>::field_type>::PromotedType PromotedType; + PromotedType result(0); + assert(x.size() == size()); + for (size_type i=0; i<size(); i++) { + result += PromotedType((*this)[i]*x[i]); + } + return result; + } + + /** + * @brief vector dot product \f$\left (x^H \cdot y \right)\f$ which corresponds to Petsc's VecDot + * + * http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecDot.html + * @param x other vector + * @return + */ + template<class Other> + typename PromotionTraits<field_type,typename DenseVector<Other>::field_type>::PromotedType dot(const DenseVector<Other>& x) const { + typedef typename PromotionTraits<field_type, typename DenseVector<Other>::field_type>::PromotedType PromotedType; + PromotedType result(0); + assert(x.size() == size()); + for (size_type i=0; i<size(); i++) { + result += Dune::dot((*this)[i],x[i]); + } + return result; + } + + //===== norms + + //! one norm (sum over absolute values of entries) + typename FieldTraits<value_type>::real_type one_norm() const { + using std::abs; + typename FieldTraits<value_type>::real_type result( 0 ); + for (size_type i=0; i<size(); i++) + result += abs((*this)[i]); + return result; + } + + + //! simplified one norm (uses Manhattan norm for complex values) + typename FieldTraits<value_type>::real_type one_norm_real () const + { + typename FieldTraits<value_type>::real_type result( 0 ); + for (size_type i=0; i<size(); i++) + result += fvmeta::absreal((*this)[i]); + return result; + } + + //! two norm sqrt(sum over squared values of entries) + typename FieldTraits<value_type>::real_type two_norm () const + { + typename FieldTraits<value_type>::real_type result( 0 ); + for (size_type i=0; i<size(); i++) + result += fvmeta::abs2((*this)[i]); + return fvmeta::sqrt(result); + } + + //! square of two norm (sum over squared values of entries), need for block recursion + typename FieldTraits<value_type>::real_type two_norm2 () const + { + typename FieldTraits<value_type>::real_type result( 0 ); + for (size_type i=0; i<size(); i++) + result += fvmeta::abs2((*this)[i]); + return result; + } + + //! infinity norm (maximum of absolute values of entries) + template <typename vt = value_type, + typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::abs; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = abs(x); + norm = max(a, norm); + } + return norm; + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template <typename vt = value_type, + typename std::enable_if<!HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm_real() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::max; + + real_type norm = 0; + for (auto const &x : *this) { + real_type const a = fvmeta::absreal(x); + norm = max(a, norm); + } + return norm; + } + + //! infinity norm (maximum of absolute values of entries) + template <typename vt = value_type, + typename std::enable_if<HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::abs; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = abs(x); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + template <typename vt = value_type, + typename std::enable_if<HasNaN<vt>::value, int>::type = 0> + typename FieldTraits<vt>::real_type infinity_norm_real() const { + using real_type = typename FieldTraits<vt>::real_type; + using std::max; + + real_type norm = 0; + real_type isNaN = 1; + for (auto const &x : *this) { + real_type const a = fvmeta::absreal(x); + norm = max(a, norm); + isNaN += a; + } + return norm * (isNaN / isNaN); + } + + //===== sizes + + //! number of blocks in the vector (are of size 1 here) + size_type N () const + { + return size(); + } + + //! dimension of the vector space + size_type dim () const + { + return size(); + } + + }; + + /** \brief Write a DenseVector to an output stream + * \relates DenseVector + * + * \param[in] s std :: ostream to write to + * \param[in] v DenseVector to write + * + * \returns the output stream (s) + */ + template<typename V> + std::ostream& operator<< (std::ostream& s, const DenseVector<V>& v) + { + for (typename DenseVector<V>::size_type i=0; i<v.size(); i++) + s << ((i>0) ? " " : "") << v[i]; + return s; + } + + /** @} end documentation */ } // end namespace diff --git a/dune/common/deprecated.hh b/dune/common/deprecated.hh index cf6c3b872be88781d9164d3f21853e8b54118da9..73848b971250a3a9b713ac95dc2ada5765ca694b 100644 --- a/dune/common/deprecated.hh +++ b/dune/common/deprecated.hh @@ -5,19 +5,19 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Definition of the DUNE_DEPRECATED macro for the case that config.h -* is not available -*/ + * \brief Definition of the DUNE_DEPRECATED macro for the case that config.h + * is not available + */ //! @addtogroup CxxUtilities //! @{ #if defined(DOXYGEN) || !defined(HAS_ATTRIBUTE_DEPRECATED) //! Mark some entity as deprecated /** -* \deprecated Use C++14's \code[[deprecated]]\endcode instead. It will be -* removed after Dune 2.8. Be aware that it must be sometimes placed at -* different position in the code. -*/ + * \deprecated Use C++14's \code[[deprecated]]\endcode instead. It will be + * removed after Dune 2.8. Be aware that it must be sometimes placed at + * different position in the code. + */ #define DUNE_DEPRECATED #else // defined(HAS_ATTRIBUTE_DEPRECATED) #define DUNE_DEPRECATED __attribute__((deprecated)) @@ -26,10 +26,10 @@ #if defined(DOXYGEN) || !defined(HAS_ATTRIBUTE_DEPRECATED_MSG) //! Mark some entity as deprecated /** -* \deprecated Use C++14's \code[[deprecated(msg)]]\endcode instead. It -* will be removed after Dune 2.8. Be aware that it must be sometimes -* placed at different position in the code. -*/ + * \deprecated Use C++14's \code[[deprecated(msg)]]\endcode instead. It + * will be removed after Dune 2.8. Be aware that it must be sometimes + * placed at different position in the code. + */ #define DUNE_DEPRECATED_MSG(text) DUNE_DEPRECATED #else // defined(HAS_ATTRIBUTE_DEPRECATED_MSG) #define DUNE_DEPRECATED_MSG(text) __attribute__((deprecated(# text))) @@ -37,45 +37,45 @@ #ifdef DOXYGEN /** -* \brief Ignore deprecation warnings (start) -* -* This macro can be used together with `DUNE_NO_DEPRECATED_END` to mark a -* block in which deprecation warnings are ignored. This can be useful for -* implementations of deprecated methods that call other deprecated methods -* or for testing deprecated methods in the testsuite. -* -* \code -DUNE_NO_DEPRECATED_BEGIN -some_deprecated_function(); -another_deprecated_function(); -DUNE_NO_DEPRECATED_END -* \endcode -* -* \warning This macro must always be used together with `DUNE_NO_DEPRECATED_END` -*/ + * \brief Ignore deprecation warnings (start) + * + * This macro can be used together with `DUNE_NO_DEPRECATED_END` to mark a + * block in which deprecation warnings are ignored. This can be useful for + * implementations of deprecated methods that call other deprecated methods + * or for testing deprecated methods in the testsuite. + * + * \code + DUNE_NO_DEPRECATED_BEGIN + some_deprecated_function(); + another_deprecated_function(); + DUNE_NO_DEPRECATED_END + * \endcode + * + * \warning This macro must always be used together with `DUNE_NO_DEPRECATED_END` + */ #define DUNE_NO_DEPRECATED_BEGIN ... /** -* \brief Ignore deprecation warnings (end) -* -* \warning This macro must always be used together with `DUNE_NO_DEPRECATED_BEGIN` -*/ + * \brief Ignore deprecation warnings (end) + * + * \warning This macro must always be used together with `DUNE_NO_DEPRECATED_BEGIN` + */ #define DUNE_NO_DEPRECATED_END ... #else # if defined __clang__ # define DUNE_NO_DEPRECATED_BEGIN \ -_Pragma("clang diagnostic push") \ -_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") # define DUNE_NO_DEPRECATED_END _Pragma("clang diagnostic pop") # elif defined __INTEL_COMPILER # define DUNE_NO_DEPRECATED_BEGIN \ -_Pragma("warning push") \ -_Pragma("warning(disable:1478)") \ -_Pragma("warning(disable:1786)") + _Pragma("warning push") \ + _Pragma("warning(disable:1478)") \ + _Pragma("warning(disable:1786)") # define DUNE_NO_DEPRECATED_END _Pragma("warning pop") # elif defined __GNUC__ # define DUNE_NO_DEPRECATED_BEGIN \ -_Pragma("GCC diagnostic push") \ -_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") # define DUNE_NO_DEPRECATED_END _Pragma("GCC diagnostic pop") # else # define DUNE_NO_DEPRECATED_BEGIN /* Noop. */ diff --git a/dune/common/diagonalmatrix.hh b/dune/common/diagonalmatrix.hh index 5e98b0d489527fac94c382e4911cfe02b142b646..ce265b677e6f65d36fcd5507a2d5ab9fa09da068 100644 --- a/dune/common/diagonalmatrix.hh +++ b/dune/common/diagonalmatrix.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /*! \file -\brief This file implements a quadratic diagonal matrix of fixed size. -*/ + \brief This file implements a quadratic diagonal matrix of fixed size. + */ #include <algorithm> #include <cassert> @@ -29,1076 +29,1076 @@ namespace Dune { -template< class K, int n > class DiagonalRowVectorConst; -template< class K, int n > class DiagonalRowVector; -template< class DiagonalMatrixType > class DiagonalMatrixWrapper; -template< class C, class T, class R> class ContainerWrapperIterator; - -/** -@addtogroup DenseMatVec -@{ -*/ - -/** -*@brief A diagonal matrix of static size. -* -* This is meant to be a replacement of FieldMatrix for the case that -* it is a diagonal matrix. -* -* \tparam K Type used for scalars -* \tparam n Matrix size -*/ -template<class K, int n> -class DiagonalMatrix -{ -typedef DiagonalMatrixWrapper< DiagonalMatrix<K,n> > WrapperType; - -public: -//===== type definitions and constants - -//! export the type representing the field -typedef K value_type; -typedef value_type field_type; - -//! export the type representing the components -typedef K block_type; - -//! The type used for the index access and size operations. -typedef std::size_t size_type; - -//! We are at the leaf of the block recursion -enum { -//! The number of block levels we contain. This is 1. -blocklevel = 1 -}; - -//! Each row is implemented by a field vector -typedef DiagonalRowVector<K,n> row_type; -typedef row_type reference; -typedef row_type row_reference; -typedef DiagonalRowVectorConst<K,n> const_row_type; -typedef const_row_type const_reference; -typedef const_row_type const_row_reference; - -//! export size -enum { -//! The number of rows -rows = n, -//! The number of columns -cols = n -}; - -//==== size - -static constexpr size_type size () -{ -return rows; -} - -//===== constructors - -//! Default constructor -constexpr DiagonalMatrix() = default; - -//! Constructor initializing the whole matrix with a scalar -DiagonalMatrix (const K& k) -: diag_(k) -{} - -//! Constructor initializing the diagonal with a vector -DiagonalMatrix (const FieldVector<K,n>& diag) -: diag_(diag) -{} - -/** \brief Construct diagonal matrix from an initializer list -* -* The elements of the list are copied into the diagonal elements of the matrix. -* If the initializer list is shorter than the matrix diagonal (which has n elements), -* the remaining matrix diagonal elements are left uninitialized. If the initializer -* list is longer, than only the first n elements will be copied into the matrix -* diagonal. -*/ -DiagonalMatrix (std::initializer_list<K> const &l) -{ -std::copy_n(l.begin(), std::min(static_cast<std::size_t>(rows), -l.size()), -diag_.begin()); -} - -/** \brief Assignment from a scalar */ -DiagonalMatrix& operator= (const K& k) -{ -diag_ = k; -return *this; -} - -/** \brief Check if matrix is the same object as the other matrix */ -bool identical(const DiagonalMatrix<K,n>& other) const -{ -return (this==&other); -} - -//===== iterator interface to rows of the matrix -//! Iterator class for sequential access -typedef ContainerWrapperIterator<const WrapperType, reference, reference> Iterator; -//! typedef for stl compliant access -typedef Iterator iterator; -//! rename the iterators for easier access -typedef Iterator RowIterator; -//! rename the iterators for easier access -typedef typename row_type::Iterator ColIterator; - -//! begin iterator -Iterator begin () -{ -return Iterator(WrapperType(this),0); -} - -//! end iterator -Iterator end () -{ -return Iterator(WrapperType(this),n); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the rows, i.e. at the last row. -Iterator beforeEnd () -{ -return Iterator(WrapperType(this),n-1); -} - -//! @returns an iterator that is positioned before -//! the first row of the matrix. -Iterator beforeBegin () -{ -return Iterator(WrapperType(this),-1); -} - - -//! Iterator class for sequential access -typedef ContainerWrapperIterator<const WrapperType, const_reference, const_reference> ConstIterator; -//! typedef for stl compliant access -typedef ConstIterator const_iterator; -//! rename the iterators for easier access -typedef ConstIterator ConstRowIterator; -//! rename the iterators for easier access -typedef typename const_row_type::ConstIterator ConstColIterator; - -//! begin iterator -ConstIterator begin () const -{ -return ConstIterator(WrapperType(this),0); -} - -//! end iterator -ConstIterator end () const -{ -return ConstIterator(WrapperType(this),n); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the rows. i.e. at the last row. -ConstIterator beforeEnd() const -{ -return ConstIterator(WrapperType(this),n-1); -} - -//! @returns an iterator that is positioned before -//! the first row of the matrix. -ConstIterator beforeBegin () const -{ -return ConstIterator(WrapperType(this),-1); -} - - - -//===== vector space arithmetic - -//! vector space addition -DiagonalMatrix& operator+= (const DiagonalMatrix& y) -{ -diag_ += y.diag_; -return *this; -} - -//! vector space subtraction -DiagonalMatrix& operator-= (const DiagonalMatrix& y) -{ -diag_ -= y.diag_; -return *this; -} - -//! vector space multiplication with scalar -DiagonalMatrix& operator+= (const K& k) -{ -diag_ += k; -return *this; -} - -//! vector space division by scalar -DiagonalMatrix& operator-= (const K& k) -{ -diag_ -= k; -return *this; -} - -//! vector space multiplication with scalar -DiagonalMatrix& operator*= (const K& k) -{ -diag_ *= k; -return *this; -} - -//! vector space division by scalar -DiagonalMatrix& operator/= (const K& k) -{ -diag_ /= k; -return *this; -} - -//===== comparison ops - -//! comparison operator -bool operator==(const DiagonalMatrix& other) const -{ -return diag_==other.diagonal(); -} - -//! incomparison operator -bool operator!=(const DiagonalMatrix& other) const -{ -return diag_!=other.diagonal(); -} - - -//===== linear maps - -//! y = A x -template<class X, class Y> -void mv (const X& x, Y& y) const -{ + template< class K, int n > class DiagonalRowVectorConst; + template< class K, int n > class DiagonalRowVector; + template< class DiagonalMatrixType > class DiagonalMatrixWrapper; + template< class C, class T, class R> class ContainerWrapperIterator; + + /** + @addtogroup DenseMatVec + @{ + */ + + /** + *@brief A diagonal matrix of static size. + * + * This is meant to be a replacement of FieldMatrix for the case that + * it is a diagonal matrix. + * + * \tparam K Type used for scalars + * \tparam n Matrix size + */ + template<class K, int n> + class DiagonalMatrix + { + typedef DiagonalMatrixWrapper< DiagonalMatrix<K,n> > WrapperType; + + public: + //===== type definitions and constants + + //! export the type representing the field + typedef K value_type; + typedef value_type field_type; + + //! export the type representing the components + typedef K block_type; + + //! The type used for the index access and size operations. + typedef std::size_t size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. This is 1. + blocklevel = 1 + }; + + //! Each row is implemented by a field vector + typedef DiagonalRowVector<K,n> row_type; + typedef row_type reference; + typedef row_type row_reference; + typedef DiagonalRowVectorConst<K,n> const_row_type; + typedef const_row_type const_reference; + typedef const_row_type const_row_reference; + + //! export size + enum { + //! The number of rows + rows = n, + //! The number of columns + cols = n + }; + + //==== size + + static constexpr size_type size () + { + return rows; + } + + //===== constructors + + //! Default constructor + constexpr DiagonalMatrix() = default; + + //! Constructor initializing the whole matrix with a scalar + DiagonalMatrix (const K& k) + : diag_(k) + {} + + //! Constructor initializing the diagonal with a vector + DiagonalMatrix (const FieldVector<K,n>& diag) + : diag_(diag) + {} + + /** \brief Construct diagonal matrix from an initializer list + * + * The elements of the list are copied into the diagonal elements of the matrix. + * If the initializer list is shorter than the matrix diagonal (which has n elements), + * the remaining matrix diagonal elements are left uninitialized. If the initializer + * list is longer, than only the first n elements will be copied into the matrix + * diagonal. + */ + DiagonalMatrix (std::initializer_list<K> const &l) + { + std::copy_n(l.begin(), std::min(static_cast<std::size_t>(rows), + l.size()), + diag_.begin()); + } + + /** \brief Assignment from a scalar */ + DiagonalMatrix& operator= (const K& k) + { + diag_ = k; + return *this; + } + + /** \brief Check if matrix is the same object as the other matrix */ + bool identical(const DiagonalMatrix<K,n>& other) const + { + return (this==&other); + } + + //===== iterator interface to rows of the matrix + //! Iterator class for sequential access + typedef ContainerWrapperIterator<const WrapperType, reference, reference> Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + //! rename the iterators for easier access + typedef Iterator RowIterator; + //! rename the iterators for easier access + typedef typename row_type::Iterator ColIterator; + + //! begin iterator + Iterator begin () + { + return Iterator(WrapperType(this),0); + } + + //! end iterator + Iterator end () + { + return Iterator(WrapperType(this),n); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows, i.e. at the last row. + Iterator beforeEnd () + { + return Iterator(WrapperType(this),n-1); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + Iterator beforeBegin () + { + return Iterator(WrapperType(this),-1); + } + + + //! Iterator class for sequential access + typedef ContainerWrapperIterator<const WrapperType, const_reference, const_reference> ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + //! rename the iterators for easier access + typedef ConstIterator ConstRowIterator; + //! rename the iterators for easier access + typedef typename const_row_type::ConstIterator ConstColIterator; + + //! begin iterator + ConstIterator begin () const + { + return ConstIterator(WrapperType(this),0); + } + + //! end iterator + ConstIterator end () const + { + return ConstIterator(WrapperType(this),n); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows. i.e. at the last row. + ConstIterator beforeEnd() const + { + return ConstIterator(WrapperType(this),n-1); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + ConstIterator beforeBegin () const + { + return ConstIterator(WrapperType(this),-1); + } + + + + //===== vector space arithmetic + + //! vector space addition + DiagonalMatrix& operator+= (const DiagonalMatrix& y) + { + diag_ += y.diag_; + return *this; + } + + //! vector space subtraction + DiagonalMatrix& operator-= (const DiagonalMatrix& y) + { + diag_ -= y.diag_; + return *this; + } + + //! vector space multiplication with scalar + DiagonalMatrix& operator+= (const K& k) + { + diag_ += k; + return *this; + } + + //! vector space division by scalar + DiagonalMatrix& operator-= (const K& k) + { + diag_ -= k; + return *this; + } + + //! vector space multiplication with scalar + DiagonalMatrix& operator*= (const K& k) + { + diag_ *= k; + return *this; + } + + //! vector space division by scalar + DiagonalMatrix& operator/= (const K& k) + { + diag_ /= k; + return *this; + } + + //===== comparison ops + + //! comparison operator + bool operator==(const DiagonalMatrix& other) const + { + return diag_==other.diagonal(); + } + + //! incomparison operator + bool operator!=(const DiagonalMatrix& other) const + { + return diag_!=other.diagonal(); + } + + + //===== linear maps + + //! y = A x + template<class X, class Y> + void mv (const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; ++i) -y[i] = diag_[i] * x[i]; -} - -//! y = A^T x -template<class X, class Y> -void mtv (const X& x, Y& y) const -{ -mv(x, y); -} - -//! y += A x -template<class X, class Y> -void umv (const X& x, Y& y) const -{ + for (size_type i=0; i<n; ++i) + y[i] = diag_[i] * x[i]; + } + + //! y = A^T x + template<class X, class Y> + void mtv (const X& x, Y& y) const + { + mv(x, y); + } + + //! y += A x + template<class X, class Y> + void umv (const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; ++i) -y[i] += diag_[i] * x[i]; -} - -//! y += A^T x -template<class X, class Y> -void umtv (const X& x, Y& y) const -{ + for (size_type i=0; i<n; ++i) + y[i] += diag_[i] * x[i]; + } + + //! y += A^T x + template<class X, class Y> + void umtv (const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; ++i) -y[i] += diag_[i] * x[i]; -} - -//! y += A^H x -template<class X, class Y> -void umhv (const X& x, Y& y) const -{ + for (size_type i=0; i<n; ++i) + y[i] += diag_[i] * x[i]; + } + + //! y += A^H x + template<class X, class Y> + void umhv (const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; i++) -y[i] += conjugateComplex(diag_[i])*x[i]; -} - -//! y -= A x -template<class X, class Y> -void mmv (const X& x, Y& y) const -{ + for (size_type i=0; i<n; i++) + y[i] += conjugateComplex(diag_[i])*x[i]; + } + + //! y -= A x + template<class X, class Y> + void mmv (const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; ++i) -y[i] -= diag_[i] * x[i]; -} - -//! y -= A^T x -template<class X, class Y> -void mmtv (const X& x, Y& y) const -{ + for (size_type i=0; i<n; ++i) + y[i] -= diag_[i] * x[i]; + } + + //! y -= A^T x + template<class X, class Y> + void mmtv (const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; ++i) -y[i] -= diag_[i] * x[i]; -} - -//! y -= A^H x -template<class X, class Y> -void mmhv (const X& x, Y& y) const -{ + for (size_type i=0; i<n; ++i) + y[i] -= diag_[i] * x[i]; + } + + //! y -= A^H x + template<class X, class Y> + void mmhv (const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; i++) -y[i] -= conjugateComplex(diag_[i])*x[i]; -} - -//! y += alpha A x -template<class X, class Y> -void usmv (const typename FieldTraits<Y>::field_type & alpha, -const X& x, Y& y) const -{ + for (size_type i=0; i<n; i++) + y[i] -= conjugateComplex(diag_[i])*x[i]; + } + + //! y += alpha A x + template<class X, class Y> + void usmv (const typename FieldTraits<Y>::field_type & alpha, + const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; i++) -y[i] += alpha * diag_[i] * x[i]; -} - -//! y += alpha A^T x -template<class X, class Y> -void usmtv (const typename FieldTraits<Y>::field_type & alpha, -const X& x, Y& y) const -{ + for (size_type i=0; i<n; i++) + y[i] += alpha * diag_[i] * x[i]; + } + + //! y += alpha A^T x + template<class X, class Y> + void usmtv (const typename FieldTraits<Y>::field_type & alpha, + const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; i++) -y[i] += alpha * diag_[i] * x[i]; -} - -//! y += alpha A^H x -template<class X, class Y> -void usmhv (const typename FieldTraits<Y>::field_type & alpha, -const X& x, Y& y) const -{ + for (size_type i=0; i<n; i++) + y[i] += alpha * diag_[i] * x[i]; + } + + //! y += alpha A^H x + template<class X, class Y> + void usmhv (const typename FieldTraits<Y>::field_type & alpha, + const X& x, Y& y) const + { #ifdef DUNE_FMatrix_WITH_CHECKING -if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); -if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); + if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range"); + if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range"); #endif -for (size_type i=0; i<n; i++) -y[i] += alpha * conjugateComplex(diag_[i]) * x[i]; -} - -//===== norms - -//! frobenius norm: sqrt(sum over squared values of entries) -double frobenius_norm () const -{ -return diag_.two_norm(); -} - -//! square of frobenius norm, need for block recursion -double frobenius_norm2 () const -{ -return diag_.two_norm2(); -} - -//! infinity norm (row sum norm, how to generalize for blocks?) -double infinity_norm () const -{ -return diag_.infinity_norm(); -} - -//! simplified infinity norm (uses Manhattan norm for complex values) -double infinity_norm_real () const -{ -return diag_.infinity_norm_real(); -} - - - -//===== solve - -//! Solve system A x = b -template<class V> -void solve (V& x, const V& b) const -{ -for (int i=0; i<n; i++) -x[i] = b[i]/diag_[i]; -} - -//! Compute inverse -void invert() -{ -using real_type = typename FieldTraits<K>::real_type; -for (int i=0; i<n; i++) -diag_[i] = real_type(1.0)/diag_[i]; -} - -//! calculates the determinant of this matrix -K determinant () const -{ -K det = diag_[0]; -for (int i=1; i<n; i++) -det *= diag_[i]; -return det; -} - - - -//===== sizes - -//! number of blocks in row direction -static constexpr size_type N () -{ -return n; -} - -//! number of blocks in column direction -static constexpr size_type M () -{ -return n; -} - - - -//===== query - -//! return true when (i,j) is in pattern -bool exists (size_type i, size_type j) const -{ -DUNE_ASSERT_BOUNDS(i >= 0 && i < n); -DUNE_ASSERT_BOUNDS(j >= 0 && j < n); -return i==j; -} - - - -//! Sends the matrix to an output stream -friend std::ostream& operator<< (std::ostream& s, const DiagonalMatrix<K,n>& a) -{ -for (size_type i=0; i<n; i++) { -for (size_type j=0; j<n; j++) -s << ((i==j) ? a.diag_[i] : 0) << " "; -s << std::endl; -} -return s; -} - -//! Return reference object as row replacement -reference operator[](size_type i) -{ -return reference(const_cast<K*>(&diag_[i]), i); -} - -//! Return const_reference object as row replacement -const_reference operator[](size_type i) const -{ -return const_reference(const_cast<K*>(&diag_[i]), i); -} - -//! Get const reference to diagonal entry -const K& diagonal(size_type i) const -{ -return diag_[i]; -} - -//! Get reference to diagonal entry -K& diagonal(size_type i) -{ -return diag_[i]; -} - -//! Get const reference to diagonal vector -const FieldVector<K,n>& diagonal() const -{ -return diag_; -} - -//! Get reference to diagonal vector -FieldVector<K,n>& diagonal() -{ -return diag_; -} - -private: - -// the data, a FieldVector storing the diagonal -FieldVector<K,n> diag_; -}; - -template< class K, int n > -struct FieldTraits< DiagonalMatrix<K,n> > -{ -typedef typename FieldTraits<K>::field_type field_type; -typedef typename FieldTraits<K>::real_type real_type; -}; + for (size_type i=0; i<n; i++) + y[i] += alpha * conjugateComplex(diag_[i]) * x[i]; + } + + //===== norms + + //! frobenius norm: sqrt(sum over squared values of entries) + double frobenius_norm () const + { + return diag_.two_norm(); + } + + //! square of frobenius norm, need for block recursion + double frobenius_norm2 () const + { + return diag_.two_norm2(); + } + + //! infinity norm (row sum norm, how to generalize for blocks?) + double infinity_norm () const + { + return diag_.infinity_norm(); + } + + //! simplified infinity norm (uses Manhattan norm for complex values) + double infinity_norm_real () const + { + return diag_.infinity_norm_real(); + } + + + + //===== solve + + //! Solve system A x = b + template<class V> + void solve (V& x, const V& b) const + { + for (int i=0; i<n; i++) + x[i] = b[i]/diag_[i]; + } + + //! Compute inverse + void invert() + { + using real_type = typename FieldTraits<K>::real_type; + for (int i=0; i<n; i++) + diag_[i] = real_type(1.0)/diag_[i]; + } + + //! calculates the determinant of this matrix + K determinant () const + { + K det = diag_[0]; + for (int i=1; i<n; i++) + det *= diag_[i]; + return det; + } + + + + //===== sizes + + //! number of blocks in row direction + static constexpr size_type N () + { + return n; + } + + //! number of blocks in column direction + static constexpr size_type M () + { + return n; + } + + + + //===== query + + //! return true when (i,j) is in pattern + bool exists (size_type i, size_type j) const + { + DUNE_ASSERT_BOUNDS(i >= 0 && i < n); + DUNE_ASSERT_BOUNDS(j >= 0 && j < n); + return i==j; + } + + + + //! Sends the matrix to an output stream + friend std::ostream& operator<< (std::ostream& s, const DiagonalMatrix<K,n>& a) + { + for (size_type i=0; i<n; i++) { + for (size_type j=0; j<n; j++) + s << ((i==j) ? a.diag_[i] : 0) << " "; + s << std::endl; + } + return s; + } + + //! Return reference object as row replacement + reference operator[](size_type i) + { + return reference(const_cast<K*>(&diag_[i]), i); + } + + //! Return const_reference object as row replacement + const_reference operator[](size_type i) const + { + return const_reference(const_cast<K*>(&diag_[i]), i); + } + + //! Get const reference to diagonal entry + const K& diagonal(size_type i) const + { + return diag_[i]; + } + + //! Get reference to diagonal entry + K& diagonal(size_type i) + { + return diag_[i]; + } + + //! Get const reference to diagonal vector + const FieldVector<K,n>& diagonal() const + { + return diag_; + } + + //! Get reference to diagonal vector + FieldVector<K,n>& diagonal() + { + return diag_; + } + + private: + + // the data, a FieldVector storing the diagonal + FieldVector<K,n> diag_; + }; + + template< class K, int n > + struct FieldTraits< DiagonalMatrix<K,n> > + { + typedef typename FieldTraits<K>::field_type field_type; + typedef typename FieldTraits<K>::real_type real_type; + }; #ifndef DOXYGEN // hide specialization -/** \brief Special type for 1x1 matrices -*/ -template< class K > -class DiagonalMatrix<K, 1> : public FieldMatrix<K, 1, 1> -{ -typedef FieldMatrix<K,1,1> Base; -public: -//! The type used for index access and size operations -typedef typename Base::size_type size_type; - -//! We are at the leaf of the block recursion -enum { -//! The number of block levels we contain. -//! This is always one for this type. -blocklevel = 1 -}; - -typedef typename Base::row_type row_type; - -typedef typename Base::row_reference row_reference; -typedef typename Base::const_row_reference const_row_reference; - -//! export size -enum { -//! \brief The number of rows. -//! This is always one for this type. -rows = 1, -//! \brief The number of columns. -//! This is always one for this type. -cols = 1 -}; - - -//! Default Constructor -constexpr DiagonalMatrix() = default; - -//! Constructor initializing the whole matrix with a scalar -DiagonalMatrix(const K& scalar) -{ -(*this)[0][0] = scalar; -} - -//! Get const reference to diagonal entry -const K& diagonal(size_type) const -{ -return (*this)[0][0]; -} - -//! Get reference to diagonal entry -K& diagonal(size_type) -{ -return (*this)[0][0]; -} - -//! Get const reference to diagonal vector -const FieldVector<K,1>& diagonal() const -{ -return (*this)[0]; -} - -//! Get reference to diagonal vector -FieldVector<K,1>& diagonal() -{ -return (*this)[0]; -} - -}; + /** \brief Special type for 1x1 matrices + */ + template< class K > + class DiagonalMatrix<K, 1> : public FieldMatrix<K, 1, 1> + { + typedef FieldMatrix<K,1,1> Base; + public: + //! The type used for index access and size operations + typedef typename Base::size_type size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. + //! This is always one for this type. + blocklevel = 1 + }; + + typedef typename Base::row_type row_type; + + typedef typename Base::row_reference row_reference; + typedef typename Base::const_row_reference const_row_reference; + + //! export size + enum { + //! \brief The number of rows. + //! This is always one for this type. + rows = 1, + //! \brief The number of columns. + //! This is always one for this type. + cols = 1 + }; + + + //! Default Constructor + constexpr DiagonalMatrix() = default; + + //! Constructor initializing the whole matrix with a scalar + DiagonalMatrix(const K& scalar) + { + (*this)[0][0] = scalar; + } + + //! Get const reference to diagonal entry + const K& diagonal(size_type) const + { + return (*this)[0][0]; + } + + //! Get reference to diagonal entry + K& diagonal(size_type) + { + return (*this)[0][0]; + } + + //! Get const reference to diagonal vector + const FieldVector<K,1>& diagonal() const + { + return (*this)[0]; + } + + //! Get reference to diagonal vector + FieldVector<K,1>& diagonal() + { + return (*this)[0]; + } + + }; #endif -template<class DiagonalMatrixType> -class DiagonalMatrixWrapper -{ -typedef typename DiagonalMatrixType::reference reference; -typedef typename DiagonalMatrixType::const_reference const_reference; -typedef typename DiagonalMatrixType::field_type K; -typedef DiagonalRowVector<K, DiagonalMatrixType::rows> row_type; -typedef std::size_t size_type; -typedef DiagonalMatrixWrapper< DiagonalMatrixType> MyType; - -friend class ContainerWrapperIterator<const MyType, reference, reference>; -friend class ContainerWrapperIterator<const MyType, const_reference, const_reference>; - -public: - -DiagonalMatrixWrapper() : -mat_(0) -{} - -DiagonalMatrixWrapper(const DiagonalMatrixType* mat) : -mat_(const_cast<DiagonalMatrixType*>(mat)) -{} - -size_type realIndex(int i) const -{ -return i; -} - -row_type* pointer(int i) const -{ -row_ = row_type(&(mat_->diagonal(i)), i); -return &row_; -} - -bool identical(const DiagonalMatrixWrapper& other) const -{ -return mat_==other.mat_; -} - -private: - -mutable DiagonalMatrixType* mat_; -mutable row_type row_; -}; - -/** \brief -* -*/ -template< class K, int n > -class DiagonalRowVectorConst -{ -template<class DiagonalMatrixType> -friend class DiagonalMatrixWrapper; -friend class ContainerWrapperIterator<DiagonalRowVectorConst<K,n>, const K, const K&>; - -public: -// remember size of vector -enum { dimension = n }; - -// standard constructor and everything is sufficient ... - -//===== type definitions and constants - -//! export the type representing the field -typedef K field_type; - -//! export the type representing the components -typedef K block_type; - -//! The type used for the index access and size operation -typedef std::size_t size_type; - -//! We are at the leaf of the block recursion -enum { -//! The number of block levels we contain -blocklevel = 1 -}; - -//! export size -enum { -//! The size of this vector. -size = n -}; - -//! Constructor making uninitialized vector -DiagonalRowVectorConst() : -p_(0), -row_(0) -{} - -//! Constructor making vector with identical coordinates -explicit DiagonalRowVectorConst (K* p, int col) : -p_(p), -row_(col) -{} - -//===== access to components - -//! same for read only access -const K& operator[] (size_type i) const -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == row_); -return *p_; -} - -// check if row is identical to other row (not only identical values) -// since this is a proxy class we need to check equality of the stored pointer -bool identical(const DiagonalRowVectorConst<K,n>& other) const -{ -return ((p_ == other.p_)and (row_ == other.row_)); -} - -//! ConstIterator class for sequential access -typedef ContainerWrapperIterator<DiagonalRowVectorConst<K,n>, const K, const K&> ConstIterator; -//! typedef for stl compliant access -typedef ConstIterator const_iterator; - -//! begin ConstIterator -ConstIterator begin () const -{ -return ConstIterator(*this,0); -} - -//! end ConstIterator -ConstIterator end () const -{ -return ConstIterator(*this,1); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the rows. i.e. at the row. -ConstIterator beforeEnd() const -{ -return ConstIterator(*this,0); -} - -//! @returns an iterator that is positioned before -//! the first row of the matrix. -ConstIterator beforeBegin () const -{ -return ConstIterator(*this,-1); -} - -//! Binary vector comparison -bool operator== (const DiagonalRowVectorConst& y) const -{ -return ((p_==y.p_)and (row_==y.row_)); -} - -//===== sizes - -//! number of blocks in the vector (are of size 1 here) -size_type N () const -{ -return n; -} - -//! dimension of the vector space -size_type dim () const -{ -return n; -} - -//! index of this row in surrounding matrix -size_type rowIndex() const -{ -return row_; -} - -//! the diagonal value -const K& diagonal() const -{ -return *p_; -} - -protected: - -size_type realIndex(int i) const -{ -DUNE_UNUSED_PARAMETER(i); -return rowIndex(); -} - -K* pointer(size_type i) const -{ -DUNE_UNUSED_PARAMETER(i); -return const_cast<K*>(p_); -} - -DiagonalRowVectorConst* operator&() -{ -return this; -} - -// the data, very simply a pointer to the diagonal value and the row number -K* p_; -size_type row_; -}; - -template< class K, int n > -class DiagonalRowVector : public DiagonalRowVectorConst<K,n> -{ -template<class DiagonalMatrixType> -friend class DiagonalMatrixWrapper; -friend class ContainerWrapperIterator<DiagonalRowVector<K,n>, K, K&>; - -public: -// standard constructor and everything is sufficient ... - -//===== type definitions and constants - -//! export the type representing the field -typedef K field_type; - -//! export the type representing the components -typedef K block_type; - -//! The type used for the index access and size operation -typedef std::size_t size_type; - -//! Constructor making uninitialized vector -DiagonalRowVector() : DiagonalRowVectorConst<K,n>() -{} - -//! Constructor making vector with identical coordinates -explicit DiagonalRowVector (K* p, int col) : DiagonalRowVectorConst<K,n>(p, col) -{} - -//===== assignment from scalar -//! Assignment operator for scalar -DiagonalRowVector& operator= (const K& k) -{ -*p_ = k; -return *this; -} - -//===== access to components - -//! random access -K& operator[] (size_type i) -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == row_); -return *p_; -} - -//! Iterator class for sequential access -typedef ContainerWrapperIterator<DiagonalRowVector<K,n>, K, K&> Iterator; -//! typedef for stl compliant access -typedef Iterator iterator; - -//! begin iterator -Iterator begin () -{ -return Iterator(*this, 0); -} - -//! end iterator -Iterator end () -{ -return Iterator(*this, 1); -} - -//! @returns an iterator that is positioned before -//! the end iterator of the rows, i.e. at the last row. -Iterator beforeEnd () -{ -return Iterator(*this, 0); -} - -//! @returns an iterator that is positioned before -//! the first row of the matrix. -Iterator beforeBegin () -{ -return Iterator(*this, -1); -} - -//! ConstIterator class for sequential access -typedef ContainerWrapperIterator<DiagonalRowVectorConst<K,n>, const K, const K&> ConstIterator; -//! typedef for stl compliant access -typedef ConstIterator const_iterator; - -using DiagonalRowVectorConst<K,n>::identical; -using DiagonalRowVectorConst<K,n>::operator[]; -using DiagonalRowVectorConst<K,n>::operator==; -using DiagonalRowVectorConst<K,n>::begin; -using DiagonalRowVectorConst<K,n>::end; -using DiagonalRowVectorConst<K,n>::beforeEnd; -using DiagonalRowVectorConst<K,n>::beforeBegin; -using DiagonalRowVectorConst<K,n>::N; -using DiagonalRowVectorConst<K,n>::dim; -using DiagonalRowVectorConst<K,n>::rowIndex; -using DiagonalRowVectorConst<K,n>::diagonal; - -protected: - -DiagonalRowVector* operator&() -{ -return this; -} - -private: - -using DiagonalRowVectorConst<K,n>::p_; -using DiagonalRowVectorConst<K,n>::row_; -}; - - -// implement type traits -template<class K, int n> -struct const_reference< DiagonalRowVector<K,n> > -{ -typedef DiagonalRowVectorConst<K,n> type; -}; - -template<class K, int n> -struct const_reference< DiagonalRowVectorConst<K,n> > -{ -typedef DiagonalRowVectorConst<K,n> type; -}; - -template<class K, int n> -struct mutable_reference< DiagonalRowVector<K,n> > -{ -typedef DiagonalRowVector<K,n> type; -}; - -template<class K, int n> -struct mutable_reference< DiagonalRowVectorConst<K,n> > -{ -typedef DiagonalRowVector<K,n> type; -}; - - - -/** \brief Iterator class for sparse vector-like containers -* -* This class provides an iterator for sparse vector like containers. -* It contains a ContainerWrapper that must provide the translation -* from the position in the underlying container to the index -* in the sparse container. -* -* The ContainerWrapper must be default and copy-constructable. -* Furthermore it must provide the methods: -* -* bool identical(other) - check if this is identical to other (same container, not only equal) -* T* pointer(position) - get pointer to data at position in underlying container -* size_t realIndex(position) - get index in sparse container for position in underlying container -* -* Notice that the iterator stores a ContainerWrapper. -* This allows one to use proxy classes as underlying container -* and as returned reference type. -* -* \tparam CW The container wrapper class -* \tparam T The contained type -* \tparam R The reference type returned by dereference -*/ -template<class CW, class T, class R> -class ContainerWrapperIterator : public BidirectionalIteratorFacade<ContainerWrapperIterator<CW,T,R>,T, R, int> -{ -typedef typename std::remove_const<CW>::type NonConstCW; - -friend class ContainerWrapperIterator<CW, typename mutable_reference<T>::type, typename mutable_reference<R>::type>; -friend class ContainerWrapperIterator<CW, typename const_reference<T>::type, typename const_reference<R>::type>; - -typedef ContainerWrapperIterator<CW, typename mutable_reference<T>::type, typename mutable_reference<R>::type> MyType; -typedef ContainerWrapperIterator<CW, typename const_reference<T>::type, typename const_reference<R>::type> MyConstType; - -public: - -// Constructors needed by the facade iterators. -ContainerWrapperIterator() : -containerWrapper_(), -position_(0) -{} - -ContainerWrapperIterator(CW containerWrapper, int position) : -containerWrapper_(containerWrapper), -position_(position) -{} - -template<class OtherContainerWrapperIteratorType> -ContainerWrapperIterator(OtherContainerWrapperIteratorType& other) : -containerWrapper_(other.containerWrapper_), -position_(other.position_) -{} - -ContainerWrapperIterator(const MyType& other) : -containerWrapper_(other.containerWrapper_), -position_(other.position_) -{} - -ContainerWrapperIterator(const MyConstType& other) : -containerWrapper_(other.containerWrapper_), -position_(other.position_) -{} - -template<class OtherContainerWrapperIteratorType> -ContainerWrapperIterator& operator=(OtherContainerWrapperIteratorType& other) -{ -containerWrapper_ = other.containerWrapper_; -position_ = other.position_; -return *this; -} - -// This operator is needed since we can not get the address of the -// temporary object returned by dereference -T* operator->() const -{ -return containerWrapper_.pointer(position_); -} - -// Methods needed by the forward iterator -bool equals(const MyType& other) const -{ -return position_ == other.position_ && containerWrapper_.identical(other.containerWrapper_); -} - -bool equals(const MyConstType& other) const -{ -return position_ == other.position_ && containerWrapper_.identical(other.containerWrapper_); -} - -R dereference() const -{ -return *containerWrapper_.pointer(position_); -} - -void increment() -{ -++position_; -} - -// Additional function needed by BidirectionalIterator -void decrement() -{ ---position_; -} - -// Additional function needed by RandomAccessIterator -R elementAt(int i) const -{ -return *containerWrapper_.pointer(position_+i); -} - -void advance(int n) -{ -position_=position_+n; -} - -template<class OtherContainerWrapperIteratorType> -std::ptrdiff_t distanceTo(OtherContainerWrapperIteratorType& other) const -{ -assert(containerWrapper_.identical(other)); -return other.position_ - position_; -} - -std::ptrdiff_t index() const -{ -return containerWrapper_.realIndex(position_); -} - -private: -NonConstCW containerWrapper_; -size_t position_; -}; - -template <class DenseMatrix, class field, int N> -struct DenseMatrixAssigner<DenseMatrix, DiagonalMatrix<field, N>> { -static void apply(DenseMatrix& denseMatrix, -DiagonalMatrix<field, N> const& rhs) { -DUNE_ASSERT_BOUNDS(denseMatrix.M() == N); -DUNE_ASSERT_BOUNDS(denseMatrix.N() == N); -denseMatrix = field(0); -for (int i = 0; i < N; ++i) -denseMatrix[i][i] = rhs.diagonal()[i]; -} -}; -/* @} */ + template<class DiagonalMatrixType> + class DiagonalMatrixWrapper + { + typedef typename DiagonalMatrixType::reference reference; + typedef typename DiagonalMatrixType::const_reference const_reference; + typedef typename DiagonalMatrixType::field_type K; + typedef DiagonalRowVector<K, DiagonalMatrixType::rows> row_type; + typedef std::size_t size_type; + typedef DiagonalMatrixWrapper< DiagonalMatrixType> MyType; + + friend class ContainerWrapperIterator<const MyType, reference, reference>; + friend class ContainerWrapperIterator<const MyType, const_reference, const_reference>; + + public: + + DiagonalMatrixWrapper() : + mat_(0) + {} + + DiagonalMatrixWrapper(const DiagonalMatrixType* mat) : + mat_(const_cast<DiagonalMatrixType*>(mat)) + {} + + size_type realIndex(int i) const + { + return i; + } + + row_type* pointer(int i) const + { + row_ = row_type(&(mat_->diagonal(i)), i); + return &row_; + } + + bool identical(const DiagonalMatrixWrapper& other) const + { + return mat_==other.mat_; + } + + private: + + mutable DiagonalMatrixType* mat_; + mutable row_type row_; + }; + + /** \brief + * + */ + template< class K, int n > + class DiagonalRowVectorConst + { + template<class DiagonalMatrixType> + friend class DiagonalMatrixWrapper; + friend class ContainerWrapperIterator<DiagonalRowVectorConst<K,n>, const K, const K&>; + + public: + // remember size of vector + enum { dimension = n }; + + // standard constructor and everything is sufficient ... + + //===== type definitions and constants + + //! export the type representing the field + typedef K field_type; + + //! export the type representing the components + typedef K block_type; + + //! The type used for the index access and size operation + typedef std::size_t size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain + blocklevel = 1 + }; + + //! export size + enum { + //! The size of this vector. + size = n + }; + + //! Constructor making uninitialized vector + DiagonalRowVectorConst() : + p_(0), + row_(0) + {} + + //! Constructor making vector with identical coordinates + explicit DiagonalRowVectorConst (K* p, int col) : + p_(p), + row_(col) + {} + + //===== access to components + + //! same for read only access + const K& operator[] (size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == row_); + return *p_; + } + + // check if row is identical to other row (not only identical values) + // since this is a proxy class we need to check equality of the stored pointer + bool identical(const DiagonalRowVectorConst<K,n>& other) const + { + return ((p_ == other.p_)and (row_ == other.row_)); + } + + //! ConstIterator class for sequential access + typedef ContainerWrapperIterator<DiagonalRowVectorConst<K,n>, const K, const K&> ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + + //! begin ConstIterator + ConstIterator begin () const + { + return ConstIterator(*this,0); + } + + //! end ConstIterator + ConstIterator end () const + { + return ConstIterator(*this,1); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows. i.e. at the row. + ConstIterator beforeEnd() const + { + return ConstIterator(*this,0); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + ConstIterator beforeBegin () const + { + return ConstIterator(*this,-1); + } + + //! Binary vector comparison + bool operator== (const DiagonalRowVectorConst& y) const + { + return ((p_==y.p_)and (row_==y.row_)); + } + + //===== sizes + + //! number of blocks in the vector (are of size 1 here) + size_type N () const + { + return n; + } + + //! dimension of the vector space + size_type dim () const + { + return n; + } + + //! index of this row in surrounding matrix + size_type rowIndex() const + { + return row_; + } + + //! the diagonal value + const K& diagonal() const + { + return *p_; + } + + protected: + + size_type realIndex(int i) const + { + DUNE_UNUSED_PARAMETER(i); + return rowIndex(); + } + + K* pointer(size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + return const_cast<K*>(p_); + } + + DiagonalRowVectorConst* operator&() + { + return this; + } + + // the data, very simply a pointer to the diagonal value and the row number + K* p_; + size_type row_; + }; + + template< class K, int n > + class DiagonalRowVector : public DiagonalRowVectorConst<K,n> + { + template<class DiagonalMatrixType> + friend class DiagonalMatrixWrapper; + friend class ContainerWrapperIterator<DiagonalRowVector<K,n>, K, K&>; + + public: + // standard constructor and everything is sufficient ... + + //===== type definitions and constants + + //! export the type representing the field + typedef K field_type; + + //! export the type representing the components + typedef K block_type; + + //! The type used for the index access and size operation + typedef std::size_t size_type; + + //! Constructor making uninitialized vector + DiagonalRowVector() : DiagonalRowVectorConst<K,n>() + {} + + //! Constructor making vector with identical coordinates + explicit DiagonalRowVector (K* p, int col) : DiagonalRowVectorConst<K,n>(p, col) + {} + + //===== assignment from scalar + //! Assignment operator for scalar + DiagonalRowVector& operator= (const K& k) + { + *p_ = k; + return *this; + } + + //===== access to components + + //! random access + K& operator[] (size_type i) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == row_); + return *p_; + } + + //! Iterator class for sequential access + typedef ContainerWrapperIterator<DiagonalRowVector<K,n>, K, K&> Iterator; + //! typedef for stl compliant access + typedef Iterator iterator; + + //! begin iterator + Iterator begin () + { + return Iterator(*this, 0); + } + + //! end iterator + Iterator end () + { + return Iterator(*this, 1); + } + + //! @returns an iterator that is positioned before + //! the end iterator of the rows, i.e. at the last row. + Iterator beforeEnd () + { + return Iterator(*this, 0); + } + + //! @returns an iterator that is positioned before + //! the first row of the matrix. + Iterator beforeBegin () + { + return Iterator(*this, -1); + } + + //! ConstIterator class for sequential access + typedef ContainerWrapperIterator<DiagonalRowVectorConst<K,n>, const K, const K&> ConstIterator; + //! typedef for stl compliant access + typedef ConstIterator const_iterator; + + using DiagonalRowVectorConst<K,n>::identical; + using DiagonalRowVectorConst<K,n>::operator[]; + using DiagonalRowVectorConst<K,n>::operator==; + using DiagonalRowVectorConst<K,n>::begin; + using DiagonalRowVectorConst<K,n>::end; + using DiagonalRowVectorConst<K,n>::beforeEnd; + using DiagonalRowVectorConst<K,n>::beforeBegin; + using DiagonalRowVectorConst<K,n>::N; + using DiagonalRowVectorConst<K,n>::dim; + using DiagonalRowVectorConst<K,n>::rowIndex; + using DiagonalRowVectorConst<K,n>::diagonal; + + protected: + + DiagonalRowVector* operator&() + { + return this; + } + + private: + + using DiagonalRowVectorConst<K,n>::p_; + using DiagonalRowVectorConst<K,n>::row_; + }; + + + // implement type traits + template<class K, int n> + struct const_reference< DiagonalRowVector<K,n> > + { + typedef DiagonalRowVectorConst<K,n> type; + }; + + template<class K, int n> + struct const_reference< DiagonalRowVectorConst<K,n> > + { + typedef DiagonalRowVectorConst<K,n> type; + }; + + template<class K, int n> + struct mutable_reference< DiagonalRowVector<K,n> > + { + typedef DiagonalRowVector<K,n> type; + }; + + template<class K, int n> + struct mutable_reference< DiagonalRowVectorConst<K,n> > + { + typedef DiagonalRowVector<K,n> type; + }; + + + + /** \brief Iterator class for sparse vector-like containers + * + * This class provides an iterator for sparse vector like containers. + * It contains a ContainerWrapper that must provide the translation + * from the position in the underlying container to the index + * in the sparse container. + * + * The ContainerWrapper must be default and copy-constructable. + * Furthermore it must provide the methods: + * + * bool identical(other) - check if this is identical to other (same container, not only equal) + * T* pointer(position) - get pointer to data at position in underlying container + * size_t realIndex(position) - get index in sparse container for position in underlying container + * + * Notice that the iterator stores a ContainerWrapper. + * This allows one to use proxy classes as underlying container + * and as returned reference type. + * + * \tparam CW The container wrapper class + * \tparam T The contained type + * \tparam R The reference type returned by dereference + */ + template<class CW, class T, class R> + class ContainerWrapperIterator : public BidirectionalIteratorFacade<ContainerWrapperIterator<CW,T,R>,T, R, int> + { + typedef typename std::remove_const<CW>::type NonConstCW; + + friend class ContainerWrapperIterator<CW, typename mutable_reference<T>::type, typename mutable_reference<R>::type>; + friend class ContainerWrapperIterator<CW, typename const_reference<T>::type, typename const_reference<R>::type>; + + typedef ContainerWrapperIterator<CW, typename mutable_reference<T>::type, typename mutable_reference<R>::type> MyType; + typedef ContainerWrapperIterator<CW, typename const_reference<T>::type, typename const_reference<R>::type> MyConstType; + + public: + + // Constructors needed by the facade iterators. + ContainerWrapperIterator() : + containerWrapper_(), + position_(0) + {} + + ContainerWrapperIterator(CW containerWrapper, int position) : + containerWrapper_(containerWrapper), + position_(position) + {} + + template<class OtherContainerWrapperIteratorType> + ContainerWrapperIterator(OtherContainerWrapperIteratorType& other) : + containerWrapper_(other.containerWrapper_), + position_(other.position_) + {} + + ContainerWrapperIterator(const MyType& other) : + containerWrapper_(other.containerWrapper_), + position_(other.position_) + {} + + ContainerWrapperIterator(const MyConstType& other) : + containerWrapper_(other.containerWrapper_), + position_(other.position_) + {} + + template<class OtherContainerWrapperIteratorType> + ContainerWrapperIterator& operator=(OtherContainerWrapperIteratorType& other) + { + containerWrapper_ = other.containerWrapper_; + position_ = other.position_; + return *this; + } + + // This operator is needed since we can not get the address of the + // temporary object returned by dereference + T* operator->() const + { + return containerWrapper_.pointer(position_); + } + + // Methods needed by the forward iterator + bool equals(const MyType& other) const + { + return position_ == other.position_ && containerWrapper_.identical(other.containerWrapper_); + } + + bool equals(const MyConstType& other) const + { + return position_ == other.position_ && containerWrapper_.identical(other.containerWrapper_); + } + + R dereference() const + { + return *containerWrapper_.pointer(position_); + } + + void increment() + { + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement() + { + --position_; + } + + // Additional function needed by RandomAccessIterator + R elementAt(int i) const + { + return *containerWrapper_.pointer(position_+i); + } + + void advance(int n) + { + position_=position_+n; + } + + template<class OtherContainerWrapperIteratorType> + std::ptrdiff_t distanceTo(OtherContainerWrapperIteratorType& other) const + { + assert(containerWrapper_.identical(other)); + return other.position_ - position_; + } + + std::ptrdiff_t index() const + { + return containerWrapper_.realIndex(position_); + } + + private: + NonConstCW containerWrapper_; + size_t position_; + }; + + template <class DenseMatrix, class field, int N> + struct DenseMatrixAssigner<DenseMatrix, DiagonalMatrix<field, N>> { + static void apply(DenseMatrix& denseMatrix, + DiagonalMatrix<field, N> const& rhs) { + DUNE_ASSERT_BOUNDS(denseMatrix.M() == N); + DUNE_ASSERT_BOUNDS(denseMatrix.N() == N); + denseMatrix = field(0); + for (int i = 0; i < N; ++i) + denseMatrix[i][i] = rhs.diagonal()[i]; + } + }; + /* @} */ } // end namespace #endif diff --git a/dune/common/documentation.hh b/dune/common/documentation.hh index 495c58685ac00520da07fb0b547ae3ef021278a0..6b2e0973edb0671ccdf1dac8427cb61b810d69c8 100644 --- a/dune/common/documentation.hh +++ b/dune/common/documentation.hh @@ -6,52 +6,52 @@ #include <dune/internal/dune-common.hh> /** \file -\brief Documentation related stuff -*/ + \brief Documentation related stuff + */ namespace Dune { -/** -* \brief Dummy struct used for documentation purposes -* -* This struct can be used for documenting interfaces. One example would -* be: -* \code -* // Traits class that determines some property for some other type T -* template<class T> -* class SomeTraits { -* static_assert(Std::to_false_type<T>::value, -* "Sorry, SomeTraits must be specialized for all types"); -* public: -* // The type of some property of T -* typedef ImplementationDefined type; -* }; -* #ifndef DOXYGEN -* template<> -* struct SomeTraits<int> -* typedef ... type; -* }; -* // ... -* #endif // DOXYGEN -* \endcode -* -* \sa implementationDefined -* \ingroup Common -*/ -struct ImplementationDefined {}; - -/** -* \brief Dummy integral value used for documentation purposes -* -* \var Dune::implementationDefined -* \code -* #include <dune/common/documentation.hh> -* \endcode -* -* \sa ImplementationDefined -* \ingroup Common -*/ -enum { implementationDefined }; + /** + * \brief Dummy struct used for documentation purposes + * + * This struct can be used for documenting interfaces. One example would + * be: + * \code + * // Traits class that determines some property for some other type T + * template<class T> + * class SomeTraits { + * static_assert(Std::to_false_type<T>::value, + * "Sorry, SomeTraits must be specialized for all types"); + * public: + * // The type of some property of T + * typedef ImplementationDefined type; + * }; + * #ifndef DOXYGEN + * template<> + * struct SomeTraits<int> + * typedef ... type; + * }; + * // ... + * #endif // DOXYGEN + * \endcode + * + * \sa implementationDefined + * \ingroup Common + */ + struct ImplementationDefined {}; + + /** + * \brief Dummy integral value used for documentation purposes + * + * \var Dune::implementationDefined + * \code + * #include <dune/common/documentation.hh> + * \endcode + * + * \sa ImplementationDefined + * \ingroup Common + */ + enum { implementationDefined }; } diff --git a/dune/common/dotproduct.hh b/dune/common/dotproduct.hh index 4c8bec9b1b1a14bd09624bcd93263114aead0f95..3a90b17586c9d37c3ff677bc1b54cf3a6e51ba64 100644 --- a/dune/common/dotproduct.hh +++ b/dune/common/dotproduct.hh @@ -8,90 +8,90 @@ #include "typetraits.hh" namespace Dune { -/** -* @file -* @brief Provides the functions dot(a,b) := \f$a^H \cdot b \f$ and dotT(a,b) := \f$a^T \cdot b \f$ -* -* The provided dot products dot,dotT are used to compute (indefinite) dot products for fundamental types as well as DUNE vector types, such as DenseVector, FieldVector, ISTLVector. -* Note that the definition of dot(a,b) conjugates the first argument. This agrees with the behaviour of Matlab and Petsc, but not with BLAS. -* @author Jö Fahlke, Matthias Wohlmuth -*/ + /** + * @file + * @brief Provides the functions dot(a,b) := \f$a^H \cdot b \f$ and dotT(a,b) := \f$a^T \cdot b \f$ + * + * The provided dot products dot,dotT are used to compute (indefinite) dot products for fundamental types as well as DUNE vector types, such as DenseVector, FieldVector, ISTLVector. + * Note that the definition of dot(a,b) conjugates the first argument. This agrees with the behaviour of Matlab and Petsc, but not with BLAS. + * @author Jö Fahlke, Matthias Wohlmuth + */ -/** @addtogroup Common -* -* @{ -*/ + /** @addtogroup Common + * + * @{ + */ -template<class T, class = void> -struct IsVector : std::false_type {}; + template<class T, class = void> + struct IsVector : std::false_type {}; -template<class T> -struct IsVector<T, void_t<typename T::field_type> > -: std::true_type {}; + template<class T> + struct IsVector<T, void_t<typename T::field_type> > + : std::true_type {}; -/** @brief computes the dot product for fundamental data types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b -* -* @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecDot.html#VecDot -* @param a -* @param b -* @return conj(a)*b -*/ -template<class A, class B> -auto -dot(const A & a, const B & b) -> typename std::enable_if<!IsVector<A>::value && !std::is_same<typename FieldTraits<A>::field_type,typename FieldTraits<A>::real_type> ::value, decltype(conj(a)*b)>::type -{ -return conj(a)*b; -} + /** @brief computes the dot product for fundamental data types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b + * + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecDot.html#VecDot + * @param a + * @param b + * @return conj(a)*b + */ + template<class A, class B> + auto + dot(const A & a, const B & b) -> typename std::enable_if<!IsVector<A>::value && !std::is_same<typename FieldTraits<A>::field_type,typename FieldTraits<A>::real_type> ::value, decltype(conj(a)*b)>::type + { + return conj(a)*b; + } -/** -* @brief computes the dot product for fundamental data types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b -* -* Specialization for real first arguments which replaces conj(a) by a. -* @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot -* @param a -* @param b -* @return a*b (which is the same as conj(a)*b in this case) -*/ -// fundamental type with A being a real type -template<class A, class B> -auto -dot(const A & a, const B & b) -> typename std::enable_if<!IsVector<A>::value && std::is_same<typename FieldTraits<A>::field_type,typename FieldTraits<A>::real_type>::value, decltype(a*b)>::type -{ -return a*b; -} + /** + * @brief computes the dot product for fundamental data types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b + * + * Specialization for real first arguments which replaces conj(a) by a. + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot + * @param a + * @param b + * @return a*b (which is the same as conj(a)*b in this case) + */ + // fundamental type with A being a real type + template<class A, class B> + auto + dot(const A & a, const B & b) -> typename std::enable_if<!IsVector<A>::value && std::is_same<typename FieldTraits<A>::field_type,typename FieldTraits<A>::real_type>::value, decltype(a*b)>::type + { + return a*b; + } -/** -* @brief computes the dot product for various dune vector types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b -* -* Specialization for real first arguments which replaces conj(a) by a. -* @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot -* @param a -* @param b -* @return dot(a,b) -*/ -template<typename A, typename B> -auto -dot(const A & a, const B & b) -> typename std::enable_if<IsVector<A>::value, decltype(a.dot(b))>::type -{ -return a.dot(b); -} -/** @} */ + /** + * @brief computes the dot product for various dune vector types according to Petsc's VectDot function: dot(a,b) := std::conj(a)*b + * + * Specialization for real first arguments which replaces conj(a) by a. + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot + * @param a + * @param b + * @return dot(a,b) + */ + template<typename A, typename B> + auto + dot(const A & a, const B & b) -> typename std::enable_if<IsVector<A>::value, decltype(a.dot(b))>::type + { + return a.dot(b); + } + /** @} */ -/** -* @brief Computes an indefinite vector dot product for fundamental data types according to Petsc's VectTDot function: dotT(a,b) := a*b -* @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot -* @param a -* @param b -* @return a*b -*/ -template<class A, class B> -auto -dotT(const A & a, const B & b) -> decltype(a*b) -{ -return a*b; -} + /** + * @brief Computes an indefinite vector dot product for fundamental data types according to Petsc's VectTDot function: dotT(a,b) := a*b + * @see http://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Vec/VecTDot.html#VecTDot + * @param a + * @param b + * @return a*b + */ + template<class A, class B> + auto + dotT(const A & a, const B & b) -> decltype(a*b) + { + return a*b; + } -/** @} */ + /** @} */ } // end namespace DUNE #endif // DUNE_DOTPRODUCT_HH diff --git a/dune/common/dynmatrix.hh b/dune/common/dynmatrix.hh index 7ec799b8dfa257895c7b2ed712a580f1b826e9e9..55136af3b69765ab561b02848bb8c48116fb36d3 100644 --- a/dune/common/dynmatrix.hh +++ b/dune/common/dynmatrix.hh @@ -18,132 +18,132 @@ namespace Dune { -/** -@addtogroup DenseMatVec -@{ -*/ - -/*! \file -* \brief This file implements a dense matrix with dynamic numbers of rows and columns. -*/ - -template< class K > class DynamicMatrix; - -template< class K > -struct DenseMatVecTraits< DynamicMatrix<K> > -{ -typedef DynamicMatrix<K> derived_type; - -typedef DynamicVector<K> row_type; - -typedef row_type &row_reference; -typedef const row_type &const_row_reference; - -typedef std::vector<K> container_type; -typedef K value_type; -typedef typename container_type::size_type size_type; -}; - -template< class K > -struct FieldTraits< DynamicMatrix<K> > -{ -typedef typename FieldTraits<K>::field_type field_type; -typedef typename FieldTraits<K>::real_type real_type; -}; - -/** \brief Construct a matrix with a dynamic size. -* -* \tparam K is the field type (use float, double, complex, etc) -*/ -template<class K> -class DynamicMatrix : public DenseMatrix< DynamicMatrix<K> > -{ -std::vector< DynamicVector<K> > _data; -typedef DenseMatrix< DynamicMatrix<K> > Base; -public: -typedef typename Base::size_type size_type; -typedef typename Base::value_type value_type; -typedef typename Base::row_type row_type; - -//===== constructors -//! \brief Default constructor -DynamicMatrix () {} - -//! \brief Constructor initializing the whole matrix with a scalar -DynamicMatrix (size_type r, size_type c, value_type v = value_type() ) : -_data(r, row_type(c, v) ) -{} - -/** \brief Constructor initializing the matrix from a list of vector -*/ -DynamicMatrix (std::initializer_list<DynamicVector<K>> const &ll) -: _data(ll) -{} - - -template <class T, -typename = std::enable_if_t<!Dune::IsNumber<T>::value && HasDenseMatrixAssigner<DynamicMatrix, T>::value>> -DynamicMatrix(T const& rhs) -{ -*this = rhs; -} - -//==== resize related methods -/** -* \brief resize matrix to <code>r × c</code> -* -* Resize the matrix to <code>r × c</code>, using <code>v</code> -* as the value of all entries. -* -* \warning All previous entries are lost, even when the matrix -* was not actually resized. -* -* \param r number of rows -* \param c number of columns -* \param v value of matrix entries -*/ -void resize (size_type r, size_type c, value_type v = value_type() ) -{ -_data.resize(0); -_data.resize(r, row_type(c, v) ); -} - -//===== assignment -// General assignment with resizing -template <typename T, -typename = std::enable_if_t<!Dune::IsNumber<T>::value>> -DynamicMatrix& operator=(T const& rhs) { -_data.resize(rhs.N()); -std::fill(_data.begin(), _data.end(), row_type(rhs.M(), K(0))); -Base::operator=(rhs); -return *this; -} - -// Specialisation: scalar assignment (no resizing) -template <typename T, -typename = std::enable_if_t<Dune::IsNumber<T>::value>> -DynamicMatrix& operator=(T scalar) { -std::fill(_data.begin(), _data.end(), scalar); -return *this; -} - -// make this thing a matrix -size_type mat_rows() const { return _data.size(); } -size_type mat_cols() const { -assert(this->rows()); -return _data.front().size(); -} -row_type & mat_access(size_type i) { -DUNE_ASSERT_BOUNDS(i < _data.size()); -return _data[i]; -} -const row_type & mat_access(size_type i) const { -DUNE_ASSERT_BOUNDS(i < _data.size()); -return _data[i]; -} -}; - -/** @} end documentation */ + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief This file implements a dense matrix with dynamic numbers of rows and columns. + */ + + template< class K > class DynamicMatrix; + + template< class K > + struct DenseMatVecTraits< DynamicMatrix<K> > + { + typedef DynamicMatrix<K> derived_type; + + typedef DynamicVector<K> row_type; + + typedef row_type &row_reference; + typedef const row_type &const_row_reference; + + typedef std::vector<K> container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K > + struct FieldTraits< DynamicMatrix<K> > + { + typedef typename FieldTraits<K>::field_type field_type; + typedef typename FieldTraits<K>::real_type real_type; + }; + + /** \brief Construct a matrix with a dynamic size. + * + * \tparam K is the field type (use float, double, complex, etc) + */ + template<class K> + class DynamicMatrix : public DenseMatrix< DynamicMatrix<K> > + { + std::vector< DynamicVector<K> > _data; + typedef DenseMatrix< DynamicMatrix<K> > Base; + public: + typedef typename Base::size_type size_type; + typedef typename Base::value_type value_type; + typedef typename Base::row_type row_type; + + //===== constructors + //! \brief Default constructor + DynamicMatrix () {} + + //! \brief Constructor initializing the whole matrix with a scalar + DynamicMatrix (size_type r, size_type c, value_type v = value_type() ) : + _data(r, row_type(c, v) ) + {} + + /** \brief Constructor initializing the matrix from a list of vector + */ + DynamicMatrix (std::initializer_list<DynamicVector<K>> const &ll) + : _data(ll) + {} + + + template <class T, + typename = std::enable_if_t<!Dune::IsNumber<T>::value && HasDenseMatrixAssigner<DynamicMatrix, T>::value>> + DynamicMatrix(T const& rhs) + { + *this = rhs; + } + + //==== resize related methods + /** + * \brief resize matrix to <code>r × c</code> + * + * Resize the matrix to <code>r × c</code>, using <code>v</code> + * as the value of all entries. + * + * \warning All previous entries are lost, even when the matrix + * was not actually resized. + * + * \param r number of rows + * \param c number of columns + * \param v value of matrix entries + */ + void resize (size_type r, size_type c, value_type v = value_type() ) + { + _data.resize(0); + _data.resize(r, row_type(c, v) ); + } + + //===== assignment + // General assignment with resizing + template <typename T, + typename = std::enable_if_t<!Dune::IsNumber<T>::value>> + DynamicMatrix& operator=(T const& rhs) { + _data.resize(rhs.N()); + std::fill(_data.begin(), _data.end(), row_type(rhs.M(), K(0))); + Base::operator=(rhs); + return *this; + } + + // Specialisation: scalar assignment (no resizing) + template <typename T, + typename = std::enable_if_t<Dune::IsNumber<T>::value>> + DynamicMatrix& operator=(T scalar) { + std::fill(_data.begin(), _data.end(), scalar); + return *this; + } + + // make this thing a matrix + size_type mat_rows() const { return _data.size(); } + size_type mat_cols() const { + assert(this->rows()); + return _data.front().size(); + } + row_type & mat_access(size_type i) { + DUNE_ASSERT_BOUNDS(i < _data.size()); + return _data[i]; + } + const row_type & mat_access(size_type i) const { + DUNE_ASSERT_BOUNDS(i < _data.size()); + return _data[i]; + } + }; + + /** @} end documentation */ } // end namespace diff --git a/dune/common/dynmatrixev.hh b/dune/common/dynmatrixev.hh index af5bbd4a04f328c3d4d9c7b6705cc646cad9e826..963e846f2f5163ab378e059650f96df91140be73 100644 --- a/dune/common/dynmatrixev.hh +++ b/dune/common/dynmatrixev.hh @@ -11,96 +11,96 @@ #include "fmatrixev.hh" /*! -\file -\brief utility functions to compute eigenvalues for -dense matrices. -\addtogroup DenseMatVec -@{ -*/ + \file + \brief utility functions to compute eigenvalues for + dense matrices. + \addtogroup DenseMatVec + @{ + */ namespace Dune { -namespace DynamicMatrixHelp { + namespace DynamicMatrixHelp { #if HAVE_LAPACK -using Dune::FMatrixHelp::eigenValuesNonsymLapackCall; + using Dune::FMatrixHelp::eigenValuesNonsymLapackCall; #endif -/** \brief calculates the eigenvalues of a symmetric field matrix -\param[in] matrix matrix eigenvalues are calculated for -\param[out] eigenValues FieldVector that contains eigenvalues in -ascending order -\param[out] eigenVectors (optional) list of right eigenvectors - -\note LAPACK::dgeev is used to calculate the eigen values -*/ -template <typename K, class C> -static void eigenValuesNonSym(const DynamicMatrix<K>& matrix, -DynamicVector<C>& eigenValues, -std::vector<DynamicVector<K>>* eigenVectors = nullptr -) -{ + /** \brief calculates the eigenvalues of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + \param[out] eigenVectors (optional) list of right eigenvectors + + \note LAPACK::dgeev is used to calculate the eigen values + */ + template <typename K, class C> + static void eigenValuesNonSym(const DynamicMatrix<K>& matrix, + DynamicVector<C>& eigenValues, + std::vector<DynamicVector<K>>* eigenVectors = nullptr + ) + { #if HAVE_LAPACK -{ -const long int N = matrix.rows(); -const char jobvl = 'n'; -const char jobvr = eigenVectors ? 'v' : 'n'; - - -// matrix to put into dgeev -auto matrixVector = std::make_unique<double[]>(N*N); - -// copy matrix -int row = 0; -for(int i=0; i<N; ++i) -{ -for(int j=0; j<N; ++j, ++row) -{ -matrixVector[ row ] = matrix[ i ][ j ]; -} -} - -// working memory -auto eigenR = std::make_unique<double[]>(N); -auto eigenI = std::make_unique<double[]>(N); - -const long int lwork = eigenVectors ? 4*N : 3*N; -auto work = std::make_unique<double[]>(lwork); -auto vr = eigenVectors ? std::make_unique<double[]>(N*N) : std::unique_ptr<double[]>{}; - -// return value information -long int info = 0; - -// call LAPACK routine (see fmatrixev_ext.cc) -eigenValuesNonsymLapackCall(&jobvl, &jobvr, &N, matrixVector.get(), &N, -eigenR.get(), eigenI.get(), nullptr, &N, vr.get(), &N, work.get(), -&lwork, &info); - -if( info != 0 ) -{ -std::cerr << "For matrix " << matrix << " eigenvalue calculation failed! " << std::endl; -DUNE_THROW(InvalidStateException,"eigenValues: Eigenvalue calculation failed!"); -} - -eigenValues.resize(N); -for (int i=0; i<N; ++i) -eigenValues[i] = std::complex<double>(eigenR[i], eigenI[i]); - -if (eigenVectors) { -eigenVectors->resize(N); -for (int i = 0; i < N; ++i) { -auto& v = (*eigenVectors)[i]; -v.resize(N); -std::copy(vr.get() + N*i, vr.get() + N*(i+1), &v[0]); -} -} -} + { + const long int N = matrix.rows(); + const char jobvl = 'n'; + const char jobvr = eigenVectors ? 'v' : 'n'; + + + // matrix to put into dgeev + auto matrixVector = std::make_unique<double[]>(N*N); + + // copy matrix + int row = 0; + for(int i=0; i<N; ++i) + { + for(int j=0; j<N; ++j, ++row) + { + matrixVector[ row ] = matrix[ i ][ j ]; + } + } + + // working memory + auto eigenR = std::make_unique<double[]>(N); + auto eigenI = std::make_unique<double[]>(N); + + const long int lwork = eigenVectors ? 4*N : 3*N; + auto work = std::make_unique<double[]>(lwork); + auto vr = eigenVectors ? std::make_unique<double[]>(N*N) : std::unique_ptr<double[]>{}; + + // return value information + long int info = 0; + + // call LAPACK routine (see fmatrixev_ext.cc) + eigenValuesNonsymLapackCall(&jobvl, &jobvr, &N, matrixVector.get(), &N, + eigenR.get(), eigenI.get(), nullptr, &N, vr.get(), &N, work.get(), + &lwork, &info); + + if( info != 0 ) + { + std::cerr << "For matrix " << matrix << " eigenvalue calculation failed! " << std::endl; + DUNE_THROW(InvalidStateException,"eigenValues: Eigenvalue calculation failed!"); + } + + eigenValues.resize(N); + for (int i=0; i<N; ++i) + eigenValues[i] = std::complex<double>(eigenR[i], eigenI[i]); + + if (eigenVectors) { + eigenVectors->resize(N); + for (int i = 0; i < N; ++i) { + auto& v = (*eigenVectors)[i]; + v.resize(N); + std::copy(vr.get() + N*i, vr.get() + N*(i+1), &v[0]); + } + } + } #else // #if HAVE_LAPACK -DUNE_THROW(NotImplemented,"LAPACK not found!"); + DUNE_THROW(NotImplemented,"LAPACK not found!"); #endif -} -} + } + } } /** @} */ diff --git a/dune/common/dynvector.hh b/dune/common/dynvector.hh index 34119a7ca727c608d9575bba4ab74e7fea510724..7a8283d33fada380c208765c397d61191f2cde51 100644 --- a/dune/common/dynvector.hh +++ b/dune/common/dynvector.hh @@ -22,181 +22,181 @@ namespace Dune { -/** @addtogroup DenseMatVec -@{ -*/ - -/*! \file -* \brief This file implements a dense vector with a dynamic size. -*/ - -template< class K, class Allocator > class DynamicVector; -template< class K, class Allocator > -struct DenseMatVecTraits< DynamicVector< K, Allocator > > -{ -typedef DynamicVector< K, Allocator > derived_type; -typedef std::vector< K, Allocator > container_type; -typedef K value_type; -typedef typename container_type::size_type size_type; -}; - -template< class K, class Allocator > -struct FieldTraits< DynamicVector< K, Allocator > > -{ -typedef typename FieldTraits< K >::field_type field_type; -typedef typename FieldTraits< K >::real_type real_type; -}; - -/** \brief Construct a vector with a dynamic size. -* -* \tparam K is the field type (use float, double, complex, etc) -* \tparam Allocator type of allocator object used to define the storage allocation model, -* default Allocator = std::allocator< K >. -*/ -template< class K, class Allocator = std::allocator< K > > -class DynamicVector : public DenseVector< DynamicVector< K, Allocator > > -{ -std::vector< K, Allocator > _data; - -typedef DenseVector< DynamicVector< K, Allocator > > Base; -public: -typedef typename Base::size_type size_type; -typedef typename Base::value_type value_type; - -typedef std::vector< K, Allocator > container_type; - -typedef Allocator allocator_type; - -//! Constructor making uninitialized vector -explicit DynamicVector(const allocator_type &a = allocator_type() ) : -_data( a ) -{} - -explicit DynamicVector(size_type n, const allocator_type &a = allocator_type() ) : -_data( n, value_type(), a ) -{} - -//! Constructor making vector with identical coordinates -DynamicVector( size_type n, value_type c, const allocator_type &a = allocator_type() ) : -_data( n, c, a ) -{} - -/** \brief Construct from a std::initializer_list */ -DynamicVector (std::initializer_list<K> const &l) : -_data(l) -{} - -//! Constructor making vector with identical coordinates -DynamicVector(const DynamicVector & x) : -Base(), _data(x._data) -{} - -//! Move constructor -DynamicVector(DynamicVector && x) : -_data(std::move(x._data)) -{} - -template< class T > -DynamicVector(const DynamicVector< T, Allocator > & x) : -_data(x.begin(), x.end(), x.get_allocator()) -{} - -//! Copy constructor from another DenseVector -template< class X > -DynamicVector(const DenseVector< X > & x, const allocator_type &a = allocator_type() ) : -_data(a) -{ -const size_type n = x.size(); -_data.reserve(n); -for( size_type i =0; i<n ;++i) -_data.push_back( x[ i ] ); -} - -using Base::operator=; - -//! Copy assignment operator -DynamicVector &operator=(const DynamicVector &other) -{ -_data = other._data; -return *this; -} - -//! Move assignment operator -DynamicVector &operator=(DynamicVector &&other) -{ -_data = std::move(other._data); -return *this; -} - -//==== forward some methods of std::vector -/** \brief Number of elements for which memory has been allocated. - -capacity() is always greater than or equal to size(). -*/ -size_type capacity() const -{ -return _data.capacity(); -} -void resize (size_type n, value_type c = value_type() ) -{ -_data.resize(n,c); -} -void reserve (size_type n) -{ -_data.reserve(n); -} - -//==== make this thing a vector -size_type size() const { return _data.size(); } -K & operator[](size_type i) { -DUNE_ASSERT_BOUNDS(i < size()); -return _data[i]; -} -const K & operator[](size_type i) const { -DUNE_ASSERT_BOUNDS(i < size()); -return _data[i]; -} - -//! return pointer to underlying array -K* data() noexcept -{ -return _data.data(); -} - -//! return pointer to underlying array -const K* data() const noexcept -{ -return _data.data(); -} - -const container_type &container () const { return _data; } -container_type &container () { return _data; } -}; - -/** \brief Read a DynamicVector from an input stream -* \relates DynamicVector -* -* \note This operator is STL compilant, i.e., the content of v is only -* changed if the read operation is successful. -* -* \param[in] in std :: istream to read from -* \param[out] v DynamicVector to be read -* -* \returns the input stream (in) -*/ -template< class K, class Allocator > -inline std::istream &operator>> ( std::istream &in, -DynamicVector< K, Allocator > &v ) -{ -DynamicVector< K, Allocator > w(v); -for( typename DynamicVector< K, Allocator >::size_type i = 0; i < w.size(); ++i ) -in >> w[ i ]; -if(in) -v = std::move(w); -return in; -} - -/** @} end documentation */ + /** @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief This file implements a dense vector with a dynamic size. + */ + + template< class K, class Allocator > class DynamicVector; + template< class K, class Allocator > + struct DenseMatVecTraits< DynamicVector< K, Allocator > > + { + typedef DynamicVector< K, Allocator > derived_type; + typedef std::vector< K, Allocator > container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K, class Allocator > + struct FieldTraits< DynamicVector< K, Allocator > > + { + typedef typename FieldTraits< K >::field_type field_type; + typedef typename FieldTraits< K >::real_type real_type; + }; + + /** \brief Construct a vector with a dynamic size. + * + * \tparam K is the field type (use float, double, complex, etc) + * \tparam Allocator type of allocator object used to define the storage allocation model, + * default Allocator = std::allocator< K >. + */ + template< class K, class Allocator = std::allocator< K > > + class DynamicVector : public DenseVector< DynamicVector< K, Allocator > > + { + std::vector< K, Allocator > _data; + + typedef DenseVector< DynamicVector< K, Allocator > > Base; + public: + typedef typename Base::size_type size_type; + typedef typename Base::value_type value_type; + + typedef std::vector< K, Allocator > container_type; + + typedef Allocator allocator_type; + + //! Constructor making uninitialized vector + explicit DynamicVector(const allocator_type &a = allocator_type() ) : + _data( a ) + {} + + explicit DynamicVector(size_type n, const allocator_type &a = allocator_type() ) : + _data( n, value_type(), a ) + {} + + //! Constructor making vector with identical coordinates + DynamicVector( size_type n, value_type c, const allocator_type &a = allocator_type() ) : + _data( n, c, a ) + {} + + /** \brief Construct from a std::initializer_list */ + DynamicVector (std::initializer_list<K> const &l) : + _data(l) + {} + + //! Constructor making vector with identical coordinates + DynamicVector(const DynamicVector & x) : + Base(), _data(x._data) + {} + + //! Move constructor + DynamicVector(DynamicVector && x) : + _data(std::move(x._data)) + {} + + template< class T > + DynamicVector(const DynamicVector< T, Allocator > & x) : + _data(x.begin(), x.end(), x.get_allocator()) + {} + + //! Copy constructor from another DenseVector + template< class X > + DynamicVector(const DenseVector< X > & x, const allocator_type &a = allocator_type() ) : + _data(a) + { + const size_type n = x.size(); + _data.reserve(n); + for( size_type i =0; i<n ;++i) + _data.push_back( x[ i ] ); + } + + using Base::operator=; + + //! Copy assignment operator + DynamicVector &operator=(const DynamicVector &other) + { + _data = other._data; + return *this; + } + + //! Move assignment operator + DynamicVector &operator=(DynamicVector &&other) + { + _data = std::move(other._data); + return *this; + } + + //==== forward some methods of std::vector + /** \brief Number of elements for which memory has been allocated. + + capacity() is always greater than or equal to size(). + */ + size_type capacity() const + { + return _data.capacity(); + } + void resize (size_type n, value_type c = value_type() ) + { + _data.resize(n,c); + } + void reserve (size_type n) + { + _data.reserve(n); + } + + //==== make this thing a vector + size_type size() const { return _data.size(); } + K & operator[](size_type i) { + DUNE_ASSERT_BOUNDS(i < size()); + return _data[i]; + } + const K & operator[](size_type i) const { + DUNE_ASSERT_BOUNDS(i < size()); + return _data[i]; + } + + //! return pointer to underlying array + K* data() noexcept + { + return _data.data(); + } + + //! return pointer to underlying array + const K* data() const noexcept + { + return _data.data(); + } + + const container_type &container () const { return _data; } + container_type &container () { return _data; } + }; + + /** \brief Read a DynamicVector from an input stream + * \relates DynamicVector + * + * \note This operator is STL compilant, i.e., the content of v is only + * changed if the read operation is successful. + * + * \param[in] in std :: istream to read from + * \param[out] v DynamicVector to be read + * + * \returns the input stream (in) + */ + template< class K, class Allocator > + inline std::istream &operator>> ( std::istream &in, + DynamicVector< K, Allocator > &v ) + { + DynamicVector< K, Allocator > w(v); + for( typename DynamicVector< K, Allocator >::size_type i = 0; i < w.size(); ++i ) + in >> w[ i ]; + if(in) + v = std::move(w); + return in; + } + + /** @} end documentation */ } // end namespace diff --git a/dune/common/enumset.hh b/dune/common/enumset.hh index db4c8a4ad482600f7de4fbc375520591636f90c5..2c3ed918148d68e8c0fe545db866872d43188a40 100644 --- a/dune/common/enumset.hh +++ b/dune/common/enumset.hh @@ -9,169 +9,169 @@ namespace Dune { -/** -* @file -* @brief Classes for building sets out of enumeration values. -* @author Markus Blatt -*/ -/** @addtogroup Common -* -* @{ -*/ - -/** -* @brief An empty set. -*/ -template<typename TA> -class EmptySet -{ -public: -/** -* @brief The POD type the set holds. -*/ -typedef TA Type; -/** -* @brief Always returns false. -*/ -static bool contains(const Type& attribute); -}; - -/** -* @brief A set containing everything. -*/ -template<typename TA> -class AllSet -{ -public: -/** -* @brief The POD type the set holds. -*/ -typedef TA Type; -/** -* @brief Always returns true. -*/ -static bool contains(const Type& attribute); -}; - -/** -* @brief A set consisting only of one item. -*/ -template<typename TA, int item> -class EnumItem -{ -public: -/** -* @brief The type the set holds. -*/ -typedef TA Type; - -/** -* @brief Tests whether an item is in the set. -* @return True if item==Type. -*/ -static bool contains(const Type& attribute); -}; - -/** -* @brief A set representing a range including the borders. -*/ -template<typename TA,int from, int end> -class EnumRange //: public PODSet<EnumRange<T,from,end>,T> -{ -public: -/** -* @brief The type the set holds. -*/ -typedef TA Type; -static bool contains(const Type& item); -}; - -/** -* @brief The negation of a set. -* An item is contained in the set if and only if it is not -* contained in the negated set. -*/ -template<typename S> -class NegateSet -{ -public: -typedef typename S::Type Type; - -static bool contains(const Type& item) -{ -return !S::contains(item); -} -}; - -/** -* @brief A set combining two other sets. -*/ -template<class TI1, class TI2, typename TA=typename TI1::Type> -class Combine -{ -public: -static bool contains(const TA& item); -}; - -template<typename TA> -inline bool EmptySet<TA>::contains(const Type& attribute) -{ -DUNE_UNUSED_PARAMETER(attribute); -return false; -} - -template<typename TA> -inline bool AllSet<TA>::contains(const Type& attribute) -{ -DUNE_UNUSED_PARAMETER(attribute); -return true; -} - -template<typename TA,int i> -inline bool EnumItem<TA,i>::contains(const Type& item) -{ -return item==i; -} - -template<typename TA,int i> -inline std::ostream& operator<<(std::ostream& os, const EnumItem<TA,i>&) -{ -return os<<i; -} - -template<typename TA, int from, int to> -inline bool EnumRange<TA,from,to>::contains(const Type& item) -{ -return from<=item && item<=to; -} - -template<typename TA, int from, int to> -inline std::ostream& operator<<(std::ostream& os, const EnumRange<TA,from,to>&) -{ -return os<<"["<<from<<" - "<<to<<"]"; -} - -template<class TI1, class TI2, typename TA> -inline bool Combine<TI1,TI2,TA>::contains(const TA& item) -{ -return TI1::contains(item) || -TI2::contains(item); -} - -template<class TI1, class TI2> -inline Combine<TI1,TI2,typename TI1::Type> combine(const TI1& set1, const TI2& set2) -{ -DUNE_UNUSED_PARAMETER(set1); -DUNE_UNUSED_PARAMETER(set2); -return Combine<TI1,TI2,typename TI1::Type>(); -} - -template<class TI1, class TI2, class T> -inline std::ostream& operator<<(std::ostream& os, const Combine<TI1,TI2,T>&) -{ -return os << TI1()<<" "<<TI2(); -} -/** @} */ + /** + * @file + * @brief Classes for building sets out of enumeration values. + * @author Markus Blatt + */ + /** @addtogroup Common + * + * @{ + */ + + /** + * @brief An empty set. + */ + template<typename TA> + class EmptySet + { + public: + /** + * @brief The POD type the set holds. + */ + typedef TA Type; + /** + * @brief Always returns false. + */ + static bool contains(const Type& attribute); + }; + + /** + * @brief A set containing everything. + */ + template<typename TA> + class AllSet + { + public: + /** + * @brief The POD type the set holds. + */ + typedef TA Type; + /** + * @brief Always returns true. + */ + static bool contains(const Type& attribute); + }; + + /** + * @brief A set consisting only of one item. + */ + template<typename TA, int item> + class EnumItem + { + public: + /** + * @brief The type the set holds. + */ + typedef TA Type; + + /** + * @brief Tests whether an item is in the set. + * @return True if item==Type. + */ + static bool contains(const Type& attribute); + }; + + /** + * @brief A set representing a range including the borders. + */ + template<typename TA,int from, int end> + class EnumRange //: public PODSet<EnumRange<T,from,end>,T> + { + public: + /** + * @brief The type the set holds. + */ + typedef TA Type; + static bool contains(const Type& item); + }; + + /** + * @brief The negation of a set. + * An item is contained in the set if and only if it is not + * contained in the negated set. + */ + template<typename S> + class NegateSet + { + public: + typedef typename S::Type Type; + + static bool contains(const Type& item) + { + return !S::contains(item); + } + }; + + /** + * @brief A set combining two other sets. + */ + template<class TI1, class TI2, typename TA=typename TI1::Type> + class Combine + { + public: + static bool contains(const TA& item); + }; + + template<typename TA> + inline bool EmptySet<TA>::contains(const Type& attribute) + { + DUNE_UNUSED_PARAMETER(attribute); + return false; + } + + template<typename TA> + inline bool AllSet<TA>::contains(const Type& attribute) + { + DUNE_UNUSED_PARAMETER(attribute); + return true; + } + + template<typename TA,int i> + inline bool EnumItem<TA,i>::contains(const Type& item) + { + return item==i; + } + + template<typename TA,int i> + inline std::ostream& operator<<(std::ostream& os, const EnumItem<TA,i>&) + { + return os<<i; + } + + template<typename TA, int from, int to> + inline bool EnumRange<TA,from,to>::contains(const Type& item) + { + return from<=item && item<=to; + } + + template<typename TA, int from, int to> + inline std::ostream& operator<<(std::ostream& os, const EnumRange<TA,from,to>&) + { + return os<<"["<<from<<" - "<<to<<"]"; + } + + template<class TI1, class TI2, typename TA> + inline bool Combine<TI1,TI2,TA>::contains(const TA& item) + { + return TI1::contains(item) || + TI2::contains(item); + } + + template<class TI1, class TI2> + inline Combine<TI1,TI2,typename TI1::Type> combine(const TI1& set1, const TI2& set2) + { + DUNE_UNUSED_PARAMETER(set1); + DUNE_UNUSED_PARAMETER(set2); + return Combine<TI1,TI2,typename TI1::Type>(); + } + + template<class TI1, class TI2, class T> + inline std::ostream& operator<<(std::ostream& os, const Combine<TI1,TI2,T>&) + { + return os << TI1()<<" "<<TI2(); + } + /** @} */ } #endif diff --git a/dune/common/exceptions.hh b/dune/common/exceptions.hh index fa35f4555c9b634a7de163dcd0e51341a80ab5f6..0dbaf7b297def5995ccdd0a4dd1054de361ce1c1 100644 --- a/dune/common/exceptions.hh +++ b/dune/common/exceptions.hh @@ -11,279 +11,279 @@ namespace Dune { -/*! \defgroup Exceptions Exception handling -\ingroup Common -\{ + /*! \defgroup Exceptions Exception handling + \ingroup Common + \{ -The Dune-exceptions are designed to allow a simple derivation of subclasses -and to accept a text written in the '<<' syntax. + The Dune-exceptions are designed to allow a simple derivation of subclasses + and to accept a text written in the '<<' syntax. -Example of usage: + Example of usage: -\code -#include <dune/common/exceptions.hh> + \code + #include <dune/common/exceptions.hh> -... + ... -class FileNotFoundError : public Dune::IOError {}; + class FileNotFoundError : public Dune::IOError {}; -... + ... -void fileopen (std::string name) { -std::ifstream file; - -file.open(name.c_str()); - -if (file == 0) -DUNE_THROW(FileNotFoundError, "File " << name << " not found!"); - -... - -file.close(); -} - -... - -int main () { -try { -... -} catch (Dune::IOError &e) { -std::cerr << "I/O error: " << e << std::endl; -return 1; -} catch (Dune::Exception &e) { -std::cerr << "Generic Dune error: " << e << std::endl; -return 2; -} -} -\endcode - -\see exceptions.hh for detailed info - -*/ - -/*! \file -\brief A few common exception classes - -This file defines a common framework for generating exception -subclasses and to throw them in a simple manner - -*/ - -/* forward declarations */ -class Exception; -struct ExceptionHook; - -/*! \class Exception -\brief Base class for Dune-Exceptions - -all Dune exceptions are derived from this class via trivial subclassing: - -\code -class MyException : public Dune::Exception {}; -\endcode - -You should not \c throw a Dune::Exception directly but use the macro -DUNE_THROW() instead which fills the message-buffer of the exception -in a standard way and features a way to pass the result in the -operator<<-style - -\see DUNE_THROW, IOError, MathError - -*/ -class Exception -: public std::exception -{ -public: -Exception (); -void message(const std::string &msg); //!< store string in internal message buffer -const char* what() const noexcept override; //!< output internal message buffer -static void registerHook (ExceptionHook * hook); //!< add a functor which is called before a Dune::Exception is emitted (see Dune::ExceptionHook) \see Dune::ExceptionHook -static void clearHook (); //!< remove all hooks -private: -std::string _message; -static ExceptionHook * _hook; -}; - -/*! \brief Base class to add a hook to the Dune::Exception - -The user can add a functor which should be called before a Dune::Exception is emitted. - - -Example: attach a debugger to the process, if an exception is thrown -\code -struct ExceptionHookDebugger : public Dune::ExceptionHook -{ -char * process_; -char * debugger_; -ExceptionHookDebugger (int argc, char ** argv, std::string debugger) -{ -process_ = strdup(argv[0]); -debugger_ = strdup(debugger.c_str()); -} -virtual void operator () () -{ -pid_t pid = getpid(); -pid_t cpid; -cpid = fork(); -if (cpid == 0) // child -{ -char * argv[4]; -argv[0] = debugger_; -argv[1] = process_; -argv[2] = new char[12]; -snprintf(argv[2], 12, "%i", int(pid)); -argv[3] = 0; -// execute debugger -std::cout << process_ << "\n"; -std::cout << argv[0] << " " -<< argv[1] << " " -<< argv[2] << std::endl; -execv(argv[0], argv); -} -else // parent -{ -// send application to sleep -kill(pid, SIGSTOP); -} -} -}; -\endcode - -This hook is registered via a static method of Dune::Exception: -\code -int main(int argc, char** argv) { -Dune::MPIHelper & mpihelper = Dune::MPIHelper::instance(argc,argv); -ExceptionHookDebugger debugger(argc, argv, "/usr/bin/ddd"); -Dune::Exception::registerHook(& debugger); -try -{ -... -} -catch (std::string & s) { -std::cout << mpihelper.rank() << ": ERROR: " << s << std::endl; -} -catch (Dune::Exception & e) { -std::cout << mpihelper.rank() << ": DUNE ERROR: " << e.what() << std::endl; -} -} -\endcode - -*/ -struct ExceptionHook -{ -virtual ~ExceptionHook() {} -virtual void operator () () = 0; -}; - -inline std::ostream& operator<<(std::ostream &stream, const Exception &e) -{ -return stream << e.what(); -} + void fileopen (std::string name) { + std::ifstream file; + + file.open(name.c_str()); + + if (file == 0) + DUNE_THROW(FileNotFoundError, "File " << name << " not found!"); + + ... + + file.close(); + } + + ... + + int main () { + try { + ... + } catch (Dune::IOError &e) { + std::cerr << "I/O error: " << e << std::endl; + return 1; + } catch (Dune::Exception &e) { + std::cerr << "Generic Dune error: " << e << std::endl; + return 2; + } + } + \endcode + + \see exceptions.hh for detailed info + + */ + + /*! \file + \brief A few common exception classes + + This file defines a common framework for generating exception + subclasses and to throw them in a simple manner + + */ + + /* forward declarations */ + class Exception; + struct ExceptionHook; + + /*! \class Exception + \brief Base class for Dune-Exceptions + + all Dune exceptions are derived from this class via trivial subclassing: + + \code + class MyException : public Dune::Exception {}; + \endcode + + You should not \c throw a Dune::Exception directly but use the macro + DUNE_THROW() instead which fills the message-buffer of the exception + in a standard way and features a way to pass the result in the + operator<<-style + + \see DUNE_THROW, IOError, MathError + + */ + class Exception + : public std::exception + { + public: + Exception (); + void message(const std::string &msg); //!< store string in internal message buffer + const char* what() const noexcept override; //!< output internal message buffer + static void registerHook (ExceptionHook * hook); //!< add a functor which is called before a Dune::Exception is emitted (see Dune::ExceptionHook) \see Dune::ExceptionHook + static void clearHook (); //!< remove all hooks + private: + std::string _message; + static ExceptionHook * _hook; + }; + + /*! \brief Base class to add a hook to the Dune::Exception + + The user can add a functor which should be called before a Dune::Exception is emitted. + + + Example: attach a debugger to the process, if an exception is thrown + \code + struct ExceptionHookDebugger : public Dune::ExceptionHook + { + char * process_; + char * debugger_; + ExceptionHookDebugger (int argc, char ** argv, std::string debugger) + { + process_ = strdup(argv[0]); + debugger_ = strdup(debugger.c_str()); + } + virtual void operator () () + { + pid_t pid = getpid(); + pid_t cpid; + cpid = fork(); + if (cpid == 0) // child + { + char * argv[4]; + argv[0] = debugger_; + argv[1] = process_; + argv[2] = new char[12]; + snprintf(argv[2], 12, "%i", int(pid)); + argv[3] = 0; + // execute debugger + std::cout << process_ << "\n"; + std::cout << argv[0] << " " + << argv[1] << " " + << argv[2] << std::endl; + execv(argv[0], argv); + } + else // parent + { + // send application to sleep + kill(pid, SIGSTOP); + } + } + }; + \endcode + + This hook is registered via a static method of Dune::Exception: + \code + int main(int argc, char** argv) { + Dune::MPIHelper & mpihelper = Dune::MPIHelper::instance(argc,argv); + ExceptionHookDebugger debugger(argc, argv, "/usr/bin/ddd"); + Dune::Exception::registerHook(& debugger); + try + { + ... + } + catch (std::string & s) { + std::cout << mpihelper.rank() << ": ERROR: " << s << std::endl; + } + catch (Dune::Exception & e) { + std::cout << mpihelper.rank() << ": DUNE ERROR: " << e.what() << std::endl; + } + } + \endcode + + */ + struct ExceptionHook + { + virtual ~ExceptionHook() {} + virtual void operator () () = 0; + }; + + inline std::ostream& operator<<(std::ostream &stream, const Exception &e) + { + return stream << e.what(); + } #ifndef DOXYGEN -// the "format" the exception-type gets printed. __FILE__ and -// __LINE__ are standard C-defines, the GNU cpp-infofile claims that -// C99 defines __func__ as well. __FUNCTION__ is a GNU-extension + // the "format" the exception-type gets printed. __FILE__ and + // __LINE__ are standard C-defines, the GNU cpp-infofile claims that + // C99 defines __func__ as well. __FUNCTION__ is a GNU-extension #define THROWSPEC(E) # E << " [" << __func__ << ":" << __FILE__ << ":" << __LINE__ << "]: " #endif // DOXYGEN -/*! Macro to throw an exception + /*! Macro to throw an exception -\code -#include <dune/common/exceptions.hh> -\endcode + \code + #include <dune/common/exceptions.hh> + \endcode -\param E exception class derived from Dune::Exception -\param m reason for this exception in ostream-notation + \param E exception class derived from Dune::Exception + \param m reason for this exception in ostream-notation -Example: + Example: -\code -if (filehandle == 0) -DUNE_THROW(FileError, "Could not open " << filename << " for reading!"); -\endcode + \code + if (filehandle == 0) + DUNE_THROW(FileError, "Could not open " << filename << " for reading!"); + \endcode -DUNE_THROW automatically adds information about the exception thrown -to the text. + DUNE_THROW automatically adds information about the exception thrown + to the text. -\note -you can add a hook to be called before a Dune::Exception is emitted, -e.g. to add additional information to the exception, -or to invoke a debugger during parallel debugging. (see Dune::ExceptionHook) + \note + you can add a hook to be called before a Dune::Exception is emitted, + e.g. to add additional information to the exception, + or to invoke a debugger during parallel debugging. (see Dune::ExceptionHook) -*/ -// this is the magic: use the usual do { ... } while (0) trick, create -// the full message via a string stream and throw the created object + */ + // this is the magic: use the usual do { ... } while (0) trick, create + // the full message via a string stream and throw the created object #define DUNE_THROW(E, m) do { E th__ex; std::ostringstream th__out; \ -th__out << THROWSPEC(E) << m; th__ex.message(th__out.str()); throw th__ex; \ + th__out << THROWSPEC(E) << m; th__ex.message(th__out.str()); throw th__ex; \ } while (0) -/*! \brief Default exception class for I/O errors + /*! \brief Default exception class for I/O errors -This is a superclass for any errors dealing with file/socket I/O problems -like + This is a superclass for any errors dealing with file/socket I/O problems + like -- file not found -- could not write file -- could not connect to remote socket -*/ -class IOError : public Exception {}; + - file not found + - could not write file + - could not connect to remote socket + */ + class IOError : public Exception {}; -/*! \brief Default exception class for mathematical errors + /*! \brief Default exception class for mathematical errors -This is the superclass for all errors which are caused by -mathematical problems like + This is the superclass for all errors which are caused by + mathematical problems like -- matrix not invertible -- not convergent -*/ -class MathError : public Exception {}; + - matrix not invertible + - not convergent + */ + class MathError : public Exception {}; -/*! \brief Default exception class for range errors + /*! \brief Default exception class for range errors -This is the superclass for all errors which are caused because -the user tries to access data that was not allocated before. -These can be problems like + This is the superclass for all errors which are caused because + the user tries to access data that was not allocated before. + These can be problems like -- accessing array entries behind the last entry -- adding the fourth non zero entry in a sparse matrix -with only three non zero entries per row + - accessing array entries behind the last entry + - adding the fourth non zero entry in a sparse matrix + with only three non zero entries per row -*/ -class RangeError : public Exception {}; + */ + class RangeError : public Exception {}; -/*! \brief Default exception for dummy implementations + /*! \brief Default exception for dummy implementations -This exception can be used for functions/methods + This exception can be used for functions/methods -- that have to be implemented but should never be called -- that are missing -*/ -class NotImplemented : public Exception {}; + - that have to be implemented but should never be called + - that are missing + */ + class NotImplemented : public Exception {}; -/*! \brief Default exception class for OS errors + /*! \brief Default exception class for OS errors -This class is thrown when a system-call is used and returns an -error. + This class is thrown when a system-call is used and returns an + error. -*/ -class SystemError : public Exception {}; + */ + class SystemError : public Exception {}; -/*! \brief Default exception if memory allocation fails + /*! \brief Default exception if memory allocation fails -*/ -class OutOfMemoryError : public SystemError {}; + */ + class OutOfMemoryError : public SystemError {}; -/*! \brief Default exception if a function was called while -the object is not in a valid state for that function. -*/ -class InvalidStateException : public Exception {}; + /*! \brief Default exception if a function was called while + the object is not in a valid state for that function. + */ + class InvalidStateException : public Exception {}; -/*! \brief Default exception if an error in the parallel -communication of the program occurred -\ingroup ParallelCommunication -*/ -class ParallelError : public Exception {}; + /*! \brief Default exception if an error in the parallel + communication of the program occurred + \ingroup ParallelCommunication + */ + class ParallelError : public Exception {}; } // end namespace diff --git a/dune/common/filledarray.hh b/dune/common/filledarray.hh index eef102793a059fbf92542c490106660737b13eeb..5e683a74883f7330b1948157593e5a3289152ec1 100644 --- a/dune/common/filledarray.hh +++ b/dune/common/filledarray.hh @@ -6,39 +6,39 @@ #include <dune/internal/dune-common.hh> /** \file -\brief Utility to generate an array with a certain value -*/ + \brief Utility to generate an array with a certain value + */ #include <array> #include <cstddef> namespace Dune { -/** @addtogroup Common - -@{ -*/ - -//! Return an array filled with the provided value. -/** -* \note This function is `constexpr` only in C++17, or, more precisely, -* when `std::array::begin()` and `std::array::end()` are `constexpr`. -* -* \tparam n Size of the returned array. -* \tparam T Value type of the returned array. This is usually deduced -* from `t`. -*/ -template<std::size_t n, class T> -constexpr std::array<T, n> filledArray(const T& t) -{ -std::array<T, n> arr{}; -// this is constexpr in c++17, `arr.fill(t)` is not -for(auto &el : arr) -el = t; -return arr; -} - -/** @} */ + /** @addtogroup Common + + @{ + */ + + //! Return an array filled with the provided value. + /** + * \note This function is `constexpr` only in C++17, or, more precisely, + * when `std::array::begin()` and `std::array::end()` are `constexpr`. + * + * \tparam n Size of the returned array. + * \tparam T Value type of the returned array. This is usually deduced + * from `t`. + */ + template<std::size_t n, class T> + constexpr std::array<T, n> filledArray(const T& t) + { + std::array<T, n> arr{}; + // this is constexpr in c++17, `arr.fill(t)` is not + for(auto &el : arr) + el = t; + return arr; + } + + /** @} */ } // end namespace Dune diff --git a/dune/common/float_cmp.hh b/dune/common/float_cmp.hh index d648e199fb899ed362f613d5e44f8b3071f1b05e..cf288d9c074da14793ee3c77d284377cd417cd33 100644 --- a/dune/common/float_cmp.hh +++ b/dune/common/float_cmp.hh @@ -5,379 +5,379 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Various ways to compare floating-point numbers -*/ + * \brief Various ways to compare floating-point numbers + */ /** -@addtogroup FloatCmp - -@section How_to_compare How to compare floats - -When comparing floating point numbers for equality, one often faces the -problem that floating point operations are not always exact. For example on -i386 the expression -@code -0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 == 2.0 -@endcode -evaluates to -@code -1.99999999999999977796 == 2.00000000000000000000 -@endcode -which is false. One solution is to compare approximately, using an epsilon -which says how much deviation to accept. - -The most straightforward way of comparing is using an @em absolute epsilon. -This means comparison for equality is replaced by -@code -abs(first-second) <= epsilon -@endcode -This has a severe disadvantage: if you have an epsilon like 1e-10 but first -and second are of the magnitude 1e-15 everything will compare equal which is -certainly not what you want. This can be overcome by selecting an -appropriate epsilon. Nevertheless this method of comparing is not -recommended in general, and we will present a more robus method in the -next paragraph. - -There is another way of comparing approximately, using a @em relative -epsilon which is then scaled with first: -@code -abs(first-second) <= epsilon * abs(first) -@endcode -Of cource the comparison should be symmetric in first and second so we -cannot arbitrarily select either first or second to scale epsilon. The are -two symmetric variants, @em relative_weak -@code -abs(first-second) <= epsilon * max(abs(first), abs(second)) -@endcode -and @em relative_strong -@code -abs(first-second) <= epsilon * min(abs(first), abs(second)) -@endcode -Both variants are good, but in practice the relative_weak variant is -preferred. This is also the default variant. - -\note Although using a relative epsilon is better than using an absolute -epsilon, using a relative epsilon leads to problems if either first or -second equals 0. In principle the relative method can be combined -with an absolute method using an epsilon near the minimum -representable positive value, but this is not implemented here. - -There is a completely different way of comparing floats. Instead of giving -an epsilon, the programmer states how many representable value are allowed -between first and second. See the "Comparing using integers" section in -http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm -for more about that. - -@section Interface Interface - -To do the comparison, you can use the free functions @link -Dune::FloatCmp::eq eq()@endlink, @link Dune::FloatCmp::ne ne()@endlink, -@link Dune::FloatCmp::gt gt()@endlink, @link Dune::FloatCmp::lt -lt()@endlink, @link Dune::FloatCmp::ge ge()@endlink and @link -Dune::FloatCmp::le le()@endlink from the namespace Dune::FloatCmp. They -take the values to compare and optionally an epsilon, which defaults to 8 -times the machine epsilon (the difference between 1.0 and the smallest -representable value > 1.0) for relative comparisons, or simply 1e-6 for -absolute comparisons. The compare style can be given as an optional second -template parameter and defaults to relative_weak. - -You can also use the class Dune::FloatCmpOps which has @link -Dune::FloatCmpOps::eq eq()@endlink, @link Dune::FloatCmpOps::ne -ne()@endlink, @link Dune::FloatCmpOps::gt gt()@endlink, @link -Dune::FloatCmpOps::lt lt()@endlink, @link Dune::FloatCmpOps::ge ge()@endlink -and @link Dune::FloatCmpOps::le le()@endlink as member functions. In this -case the class encapsulates the epsilon and the comparison style (again the -defaults from the previous paragraph apply). This may be more convenient if -you write your own class utilizing floating point comparisons, and you want -the user of you class to specify epsilon and compare style. -*/ + @addtogroup FloatCmp + + @section How_to_compare How to compare floats + + When comparing floating point numbers for equality, one often faces the + problem that floating point operations are not always exact. For example on + i386 the expression + @code + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 + 0.2 == 2.0 + @endcode + evaluates to + @code + 1.99999999999999977796 == 2.00000000000000000000 + @endcode + which is false. One solution is to compare approximately, using an epsilon + which says how much deviation to accept. + + The most straightforward way of comparing is using an @em absolute epsilon. + This means comparison for equality is replaced by + @code + abs(first-second) <= epsilon + @endcode + This has a severe disadvantage: if you have an epsilon like 1e-10 but first + and second are of the magnitude 1e-15 everything will compare equal which is + certainly not what you want. This can be overcome by selecting an + appropriate epsilon. Nevertheless this method of comparing is not + recommended in general, and we will present a more robus method in the + next paragraph. + + There is another way of comparing approximately, using a @em relative + epsilon which is then scaled with first: + @code + abs(first-second) <= epsilon * abs(first) + @endcode + Of cource the comparison should be symmetric in first and second so we + cannot arbitrarily select either first or second to scale epsilon. The are + two symmetric variants, @em relative_weak + @code + abs(first-second) <= epsilon * max(abs(first), abs(second)) + @endcode + and @em relative_strong + @code + abs(first-second) <= epsilon * min(abs(first), abs(second)) + @endcode + Both variants are good, but in practice the relative_weak variant is + preferred. This is also the default variant. + + \note Although using a relative epsilon is better than using an absolute + epsilon, using a relative epsilon leads to problems if either first or + second equals 0. In principle the relative method can be combined + with an absolute method using an epsilon near the minimum + representable positive value, but this is not implemented here. + + There is a completely different way of comparing floats. Instead of giving + an epsilon, the programmer states how many representable value are allowed + between first and second. See the "Comparing using integers" section in + http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + for more about that. + + @section Interface Interface + + To do the comparison, you can use the free functions @link + Dune::FloatCmp::eq eq()@endlink, @link Dune::FloatCmp::ne ne()@endlink, + @link Dune::FloatCmp::gt gt()@endlink, @link Dune::FloatCmp::lt + lt()@endlink, @link Dune::FloatCmp::ge ge()@endlink and @link + Dune::FloatCmp::le le()@endlink from the namespace Dune::FloatCmp. They + take the values to compare and optionally an epsilon, which defaults to 8 + times the machine epsilon (the difference between 1.0 and the smallest + representable value > 1.0) for relative comparisons, or simply 1e-6 for + absolute comparisons. The compare style can be given as an optional second + template parameter and defaults to relative_weak. + + You can also use the class Dune::FloatCmpOps which has @link + Dune::FloatCmpOps::eq eq()@endlink, @link Dune::FloatCmpOps::ne + ne()@endlink, @link Dune::FloatCmpOps::gt gt()@endlink, @link + Dune::FloatCmpOps::lt lt()@endlink, @link Dune::FloatCmpOps::ge ge()@endlink + and @link Dune::FloatCmpOps::le le()@endlink as member functions. In this + case the class encapsulates the epsilon and the comparison style (again the + defaults from the previous paragraph apply). This may be more convenient if + you write your own class utilizing floating point comparisons, and you want + the user of you class to specify epsilon and compare style. + */ //! Dune namespace namespace Dune { -//! FloatCmp namespace -//! @ingroup FloatCmp -namespace FloatCmp { -// basic constants -//! How to compare -//! @ingroup FloatCmp -enum CmpStyle { -//! |a-b|/|a| <= epsilon || |a-b|/|b| <= epsilon -relativeWeak, -//! |a-b|/|a| <= epsilon && |a-b|/|b| <= epsilon -relativeStrong, -//! |a-b| <= epsilon -absolute, -//! the global default compare style (relative_weak) -defaultCmpStyle = relativeWeak -}; -//! How to round or truncate -//! @ingroup FloatCmp -enum RoundingStyle { -//! always round toward 0 -towardZero, -//! always round away from 0 -towardInf, -//! round toward \f$-\infty\f$ -downward, -//! round toward \f$+\infty\f$ -upward, -//! the global default rounding style (toward_zero) -defaultRoundingStyle = towardZero -}; - -template<class T> struct EpsilonType; - -//! mapping from a value type and a compare style to a default epsilon -/** -* @ingroup FloatCmp -* @tparam T The value type to map from -* @tparam style The compare style to map from -*/ -template<class T, CmpStyle style = defaultCmpStyle> -struct DefaultEpsilon { -//! Returns the default epsilon for the given value type and compare style -static typename EpsilonType<T>::Type value(); -}; - -// operations in functional style - -//! @addtogroup FloatCmp -//! @{ - -//! test for equality using epsilon -/** -* @tparam T Type of the values to compare -* @tparam style How to compare. This defaults to defaultCmpStyle. -* @param first left operand of equals operation -* @param second right operand of equals operation -* @param epsilon The epsilon to use in the comparison -*/ -template <class T, CmpStyle style /*= defaultCmpStyle*/> -bool eq(const T &first, -const T &second, -typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); -//! test for inequality using epsilon -/** -* @tparam T Type of the values to compare -* @tparam style How to compare. This defaults to defaultCmpStyle. -* @param first left operand of not-equal operation -* @param second right operand of not-equal operation -* @param epsilon The epsilon to use in the comparison -* @return !eq(first, second, epsilon) -*/ -template <class T, CmpStyle style /*= defaultCmpStyle*/> -bool ne(const T &first, -const T &second, -typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); -//! test if first greater than second -/** -* @tparam T Type of the values to compare -* @tparam style How to compare. This defaults to defaultCmpStyle. -* @param first left operand of greater-than operation -* @param second right operand of greater-than operation -* @param epsilon The epsilon to use in the comparison -* @return ne(first, second, epsilon) && first > second -* -* this is like first > second but the region that compares equal with an -* epsilon is excluded -*/ -template <class T, CmpStyle style /*= defaultCmpStyle*/> -bool gt(const T &first, -const T &second, -typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); -//! test if first lesser than second -/** -* @tparam T Type of the values to compare -* @tparam style How to compare. This defaults to defaultCmpStyle. -* @param first left operand of less-than operation -* @param second right operand of less-than operation -* @param epsilon The epsilon to use in the comparison -* @return ne(first, second, epsilon) && first < second -* -* this is like first < second, but the region that compares equal with an -* epsilon is excluded -*/ -template <class T, CmpStyle style /*= defaultCmpStyle*/> -bool lt(const T &first, -const T &second, -typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); -//! test if first greater or equal second -/** -* @tparam T Type of the values to compare -* @tparam style How to compare. This defaults to defaultCmpStyle. -* @param first left operand of greater-or-equals operation -* @param second right operand of greater-or-equals operation -* @param epsilon The epsilon to use in the comparison -* @return eq(first, second, epsilon) || first > second -* -* this is like first > second, but the region that compares equal with an -* epsilon is also included -*/ -template <class T, CmpStyle style /*= defaultCmpStyle*/> -bool ge(const T &first, -const T &second, -typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); -//! test if first lesser or equal second -/** -* @tparam T Type of the values to compare -* @tparam style How to compare. This defaults to defaultCmpStyle. -* @param first left operand of less-or-equals operation -* @param second right operand of less-or-equals operation -* @param epsilon The epsilon to use in the comparison -* @return eq(first, second) || first < second -* -* this is like first < second, but the region that compares equal with an -* epsilon is also included -*/ -template <class T, CmpStyle style /*= defaultCmpStyle*/> -bool le(const T &first, -const T &second, -typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); - -// rounding operations -//! round using epsilon -/** -* @tparam I The integral type to round to -* @tparam T Type of the value to round -* @tparam cstyle How to compare. This defaults to defaultCmpStyle. -* @tparam rstyle How to round. This defaults to defaultRoundingStyle. -* @param val The value to round -* @param epsilon The epsilon to use in comparisons -* @return The rounded value -* -* Round according to rstyle. If val is already near the mean of two -* adjacent integers in terms of epsilon, the result will be the rounded -* mean. -*/ -template<class I, class T, CmpStyle cstyle /*= defaultCmpStyle*/, RoundingStyle rstyle /*= defaultRoundingStyle*/> -I round(const T &val, typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, cstyle>::value()); -// truncation -//! truncate using epsilon -/** -* @tparam I The integral type to truncate to -* @tparam T Type of the value to truncate -* @tparam cstyle How to compare. This defaults to defaultCmpStyle. -* @tparam rstyle How to truncate. This defaults to defaultRoundingStyle. -* @param val The value to truncate -* @param epsilon The epsilon to use in comparisons -* @return The truncated value -* -* Truncate according to rstyle. If val is already near an integer in -* terms of epsilon, the result will be that integer instead of the real -* truncated value. -*/ -template<class I, class T, CmpStyle cstyle /*= defaultCmpStyle*/, RoundingStyle rstyle /*= defaultRoundingStyle*/> -I trunc(const T &val, typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, cstyle>::value()); - -//! @} -// group FloatCmp -} //namespace FloatCmp - - -// oo interface -//! Class encapsulating a default epsilon -/** -* @ingroup FloatCmp -* @tparam T Type of the values to compare -* @tparam cstyle_ How to compare -* @tparam rstyle_ How to round -*/ -template<class T, FloatCmp::CmpStyle cstyle_ = FloatCmp::defaultCmpStyle, -FloatCmp::RoundingStyle rstyle_ = FloatCmp::defaultRoundingStyle> -class FloatCmpOps { -typedef FloatCmp::CmpStyle CmpStyle; -typedef FloatCmp::RoundingStyle RoundingStyle; - -public: -// record template parameters -//! How comparisons are done -static const CmpStyle cstyle = cstyle_; -//! How rounding is done -static const RoundingStyle rstyle = rstyle_; -//! Type of the values to compare -typedef T ValueType; -//! Type of the epsilon. -/** -* May be different from the value type, for example for complex<double> -*/ -typedef typename FloatCmp::EpsilonType<T>::Type EpsilonType; - -private: -EpsilonType epsilon_; - -typedef FloatCmp::DefaultEpsilon<EpsilonType, cstyle> DefaultEpsilon; - -public: -//! construct an operations object -/** -* @param epsilon Use the specified epsilon for comparing -*/ -FloatCmpOps(EpsilonType epsilon = DefaultEpsilon::value()); - -//! return the current epsilon -EpsilonType epsilon() const; -//! set new epsilon -void epsilon(EpsilonType epsilon__); - -//! test for equality using epsilon -bool eq(const ValueType &first, const ValueType &second) const; -//! test for inequality using epsilon -/** -* this is exactly !eq(first, second) -*/ -bool ne(const ValueType &first, const ValueType &second) const; -//! test if first greater than second -/** -* this is exactly ne(first, second) && first > second, i.e. greater but -* the region that compares equal with an epsilon is excluded -*/ -bool gt(const ValueType &first, const ValueType &second) const; -//! test if first lesser than second -/** -* this is exactly ne(first, second) && first < second, i.e. lesser but -* the region that compares equal with an epsilon is excluded -*/ -bool lt(const ValueType &first, const ValueType &second) const; -//! test if first greater or equal second -/** -* this is exactly eq(first, second) || first > second, i.e. greater but -* the region that compares equal with an epsilon is also included -*/ -bool ge(const ValueType &first, const ValueType &second) const; -//! test if first lesser or equal second -/** -* this is exactly eq(first, second) || first > second, i.e. lesser but -* the region that compares equal with an epsilon is also included -*/ -bool le(const ValueType &first, const ValueType &second) const; - -//! round using epsilon -/** -* @tparam I The integral type to round to -* -* @param val The value to round -* -* Round according to rstyle. If val is already near the mean of two -* adjacent integers in terms of epsilon, the result will be the rounded -* mean. -*/ -template<class I> -I round(const ValueType &val) const; - -//! truncate using epsilon -/** -* @tparam I The integral type to truncate to -* -* @param val The value to truncate -* -* Truncate according to rstyle. If val is already near an integer in -* terms of epsilon, the result will be that integer instead of the real -* truncated value. -*/ -template<class I> -I trunc(const ValueType &val) const; - -}; + //! FloatCmp namespace + //! @ingroup FloatCmp + namespace FloatCmp { + // basic constants + //! How to compare + //! @ingroup FloatCmp + enum CmpStyle { + //! |a-b|/|a| <= epsilon || |a-b|/|b| <= epsilon + relativeWeak, + //! |a-b|/|a| <= epsilon && |a-b|/|b| <= epsilon + relativeStrong, + //! |a-b| <= epsilon + absolute, + //! the global default compare style (relative_weak) + defaultCmpStyle = relativeWeak + }; + //! How to round or truncate + //! @ingroup FloatCmp + enum RoundingStyle { + //! always round toward 0 + towardZero, + //! always round away from 0 + towardInf, + //! round toward \f$-\infty\f$ + downward, + //! round toward \f$+\infty\f$ + upward, + //! the global default rounding style (toward_zero) + defaultRoundingStyle = towardZero + }; + + template<class T> struct EpsilonType; + + //! mapping from a value type and a compare style to a default epsilon + /** + * @ingroup FloatCmp + * @tparam T The value type to map from + * @tparam style The compare style to map from + */ + template<class T, CmpStyle style = defaultCmpStyle> + struct DefaultEpsilon { + //! Returns the default epsilon for the given value type and compare style + static typename EpsilonType<T>::Type value(); + }; + + // operations in functional style + + //! @addtogroup FloatCmp + //! @{ + + //! test for equality using epsilon + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of equals operation + * @param second right operand of equals operation + * @param epsilon The epsilon to use in the comparison + */ + template <class T, CmpStyle style /*= defaultCmpStyle*/> + bool eq(const T &first, + const T &second, + typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); + //! test for inequality using epsilon + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of not-equal operation + * @param second right operand of not-equal operation + * @param epsilon The epsilon to use in the comparison + * @return !eq(first, second, epsilon) + */ + template <class T, CmpStyle style /*= defaultCmpStyle*/> + bool ne(const T &first, + const T &second, + typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); + //! test if first greater than second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of greater-than operation + * @param second right operand of greater-than operation + * @param epsilon The epsilon to use in the comparison + * @return ne(first, second, epsilon) && first > second + * + * this is like first > second but the region that compares equal with an + * epsilon is excluded + */ + template <class T, CmpStyle style /*= defaultCmpStyle*/> + bool gt(const T &first, + const T &second, + typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); + //! test if first lesser than second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of less-than operation + * @param second right operand of less-than operation + * @param epsilon The epsilon to use in the comparison + * @return ne(first, second, epsilon) && first < second + * + * this is like first < second, but the region that compares equal with an + * epsilon is excluded + */ + template <class T, CmpStyle style /*= defaultCmpStyle*/> + bool lt(const T &first, + const T &second, + typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); + //! test if first greater or equal second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of greater-or-equals operation + * @param second right operand of greater-or-equals operation + * @param epsilon The epsilon to use in the comparison + * @return eq(first, second, epsilon) || first > second + * + * this is like first > second, but the region that compares equal with an + * epsilon is also included + */ + template <class T, CmpStyle style /*= defaultCmpStyle*/> + bool ge(const T &first, + const T &second, + typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); + //! test if first lesser or equal second + /** + * @tparam T Type of the values to compare + * @tparam style How to compare. This defaults to defaultCmpStyle. + * @param first left operand of less-or-equals operation + * @param second right operand of less-or-equals operation + * @param epsilon The epsilon to use in the comparison + * @return eq(first, second) || first < second + * + * this is like first < second, but the region that compares equal with an + * epsilon is also included + */ + template <class T, CmpStyle style /*= defaultCmpStyle*/> + bool le(const T &first, + const T &second, + typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, style>::value()); + + // rounding operations + //! round using epsilon + /** + * @tparam I The integral type to round to + * @tparam T Type of the value to round + * @tparam cstyle How to compare. This defaults to defaultCmpStyle. + * @tparam rstyle How to round. This defaults to defaultRoundingStyle. + * @param val The value to round + * @param epsilon The epsilon to use in comparisons + * @return The rounded value + * + * Round according to rstyle. If val is already near the mean of two + * adjacent integers in terms of epsilon, the result will be the rounded + * mean. + */ + template<class I, class T, CmpStyle cstyle /*= defaultCmpStyle*/, RoundingStyle rstyle /*= defaultRoundingStyle*/> + I round(const T &val, typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, cstyle>::value()); + // truncation + //! truncate using epsilon + /** + * @tparam I The integral type to truncate to + * @tparam T Type of the value to truncate + * @tparam cstyle How to compare. This defaults to defaultCmpStyle. + * @tparam rstyle How to truncate. This defaults to defaultRoundingStyle. + * @param val The value to truncate + * @param epsilon The epsilon to use in comparisons + * @return The truncated value + * + * Truncate according to rstyle. If val is already near an integer in + * terms of epsilon, the result will be that integer instead of the real + * truncated value. + */ + template<class I, class T, CmpStyle cstyle /*= defaultCmpStyle*/, RoundingStyle rstyle /*= defaultRoundingStyle*/> + I trunc(const T &val, typename EpsilonType<T>::Type epsilon = DefaultEpsilon<T, cstyle>::value()); + + //! @} + // group FloatCmp + } //namespace FloatCmp + + + // oo interface + //! Class encapsulating a default epsilon + /** + * @ingroup FloatCmp + * @tparam T Type of the values to compare + * @tparam cstyle_ How to compare + * @tparam rstyle_ How to round + */ + template<class T, FloatCmp::CmpStyle cstyle_ = FloatCmp::defaultCmpStyle, + FloatCmp::RoundingStyle rstyle_ = FloatCmp::defaultRoundingStyle> + class FloatCmpOps { + typedef FloatCmp::CmpStyle CmpStyle; + typedef FloatCmp::RoundingStyle RoundingStyle; + + public: + // record template parameters + //! How comparisons are done + static const CmpStyle cstyle = cstyle_; + //! How rounding is done + static const RoundingStyle rstyle = rstyle_; + //! Type of the values to compare + typedef T ValueType; + //! Type of the epsilon. + /** + * May be different from the value type, for example for complex<double> + */ + typedef typename FloatCmp::EpsilonType<T>::Type EpsilonType; + + private: + EpsilonType epsilon_; + + typedef FloatCmp::DefaultEpsilon<EpsilonType, cstyle> DefaultEpsilon; + + public: + //! construct an operations object + /** + * @param epsilon Use the specified epsilon for comparing + */ + FloatCmpOps(EpsilonType epsilon = DefaultEpsilon::value()); + + //! return the current epsilon + EpsilonType epsilon() const; + //! set new epsilon + void epsilon(EpsilonType epsilon__); + + //! test for equality using epsilon + bool eq(const ValueType &first, const ValueType &second) const; + //! test for inequality using epsilon + /** + * this is exactly !eq(first, second) + */ + bool ne(const ValueType &first, const ValueType &second) const; + //! test if first greater than second + /** + * this is exactly ne(first, second) && first > second, i.e. greater but + * the region that compares equal with an epsilon is excluded + */ + bool gt(const ValueType &first, const ValueType &second) const; + //! test if first lesser than second + /** + * this is exactly ne(first, second) && first < second, i.e. lesser but + * the region that compares equal with an epsilon is excluded + */ + bool lt(const ValueType &first, const ValueType &second) const; + //! test if first greater or equal second + /** + * this is exactly eq(first, second) || first > second, i.e. greater but + * the region that compares equal with an epsilon is also included + */ + bool ge(const ValueType &first, const ValueType &second) const; + //! test if first lesser or equal second + /** + * this is exactly eq(first, second) || first > second, i.e. lesser but + * the region that compares equal with an epsilon is also included + */ + bool le(const ValueType &first, const ValueType &second) const; + + //! round using epsilon + /** + * @tparam I The integral type to round to + * + * @param val The value to round + * + * Round according to rstyle. If val is already near the mean of two + * adjacent integers in terms of epsilon, the result will be the rounded + * mean. + */ + template<class I> + I round(const ValueType &val) const; + + //! truncate using epsilon + /** + * @tparam I The integral type to truncate to + * + * @param val The value to truncate + * + * Truncate according to rstyle. If val is already near an integer in + * terms of epsilon, the result will be that integer instead of the real + * truncated value. + */ + template<class I> + I trunc(const ValueType &val) const; + + }; } //namespace Dune diff --git a/dune/common/fmatrix.hh b/dune/common/fmatrix.hh index dbdb82857bbba172f66ea2eeb863276d5750e072..fcd793f4f8c0cc9c27560382c70468bd0802233b 100644 --- a/dune/common/fmatrix.hh +++ b/dune/common/fmatrix.hh @@ -21,688 +21,688 @@ namespace Dune { -/** -@addtogroup DenseMatVec -@{ -*/ - -/*! \file - -\brief Implements a matrix constructed from a given type -representing a field and compile-time given number of rows and columns. -*/ - -template< class K, int ROWS, int COLS = ROWS > class FieldMatrix; - -template< class K, int ROWS, int COLS > -struct DenseMatVecTraits< FieldMatrix<K,ROWS,COLS> > -{ -typedef FieldMatrix<K,ROWS,COLS> derived_type; - -// each row is implemented by a field vector -typedef FieldVector<K,COLS> row_type; - -typedef row_type &row_reference; -typedef const row_type &const_row_reference; - -typedef std::array<row_type,ROWS> container_type; -typedef K value_type; -typedef typename container_type::size_type size_type; -}; - -template< class K, int ROWS, int COLS > -struct FieldTraits< FieldMatrix<K,ROWS,COLS> > -{ -typedef typename FieldTraits<K>::field_type field_type; -typedef typename FieldTraits<K>::real_type real_type; -}; - -/** -@brief A dense n x m matrix. - -Matrices represent linear maps from a vector space V to a vector space W. -This class represents such a linear map by storing a two-dimensional -%array of numbers of a given field type K. The number of rows and -columns is given at compile time. -*/ -template<class K, int ROWS, int COLS> -class FieldMatrix : public DenseMatrix< FieldMatrix<K,ROWS,COLS> > -{ -std::array< FieldVector<K,COLS>, ROWS > _data; -typedef DenseMatrix< FieldMatrix<K,ROWS,COLS> > Base; -public: - -//! export size -enum { -//! The number of rows. -rows = ROWS, -//! The number of columns. -cols = COLS -}; - -typedef typename Base::size_type size_type; -typedef typename Base::row_type row_type; - -typedef typename Base::row_reference row_reference; -typedef typename Base::const_row_reference const_row_reference; - -//===== constructors -/** \brief Default constructor -*/ -constexpr FieldMatrix() = default; - -/** \brief Constructor initializing the matrix from a list of vector -*/ -FieldMatrix(std::initializer_list<Dune::FieldVector<K, cols> > const &l) { -assert(l.size() == rows); // Actually, this is not needed any more! -std::copy_n(l.begin(), std::min(static_cast<std::size_t>(ROWS), -l.size()), -_data.begin()); -} - -template <class T, -typename = std::enable_if_t<HasDenseMatrixAssigner<FieldMatrix, T>::value>> -FieldMatrix(T const& rhs) -{ -*this = rhs; -} - -using Base::operator=; - -//! copy assignment operator -FieldMatrix& operator=(const FieldMatrix&) = default; - -//! copy assignment from FieldMatrix over a different field -template<typename T> -FieldMatrix& operator=(const FieldMatrix<T, ROWS, COLS>& x) -{ -_data = x._data; -return *this; -} - -//! no copy assignment from FieldMatrix of different size -template <typename T, int rows, int cols> -FieldMatrix& operator=(FieldMatrix<T,rows,cols> const&) = delete; - -//! vector space addition -- two-argument version -template <class OtherScalar> -friend auto operator+ ( const FieldMatrix& matrixA, -const FieldMatrix<OtherScalar,ROWS,COLS>& matrixB) -{ -FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,ROWS,COLS> result; - -for (size_type i = 0; i < ROWS; ++i) -for (size_type j = 0; j < COLS; ++j) -result[i][j] = matrixA[i][j] + matrixB[i][j]; - -return result; -} - -//! vector space subtraction -- two-argument version -template <class OtherScalar> -friend auto operator- ( const FieldMatrix& matrixA, -const FieldMatrix<OtherScalar,ROWS,COLS>& matrixB) -{ -FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,ROWS,COLS> result; - -for (size_type i = 0; i < ROWS; ++i) -for (size_type j = 0; j < COLS; ++j) -result[i][j] = matrixA[i][j] - matrixB[i][j]; - -return result; -} - -//! vector space multiplication with scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator* ( const FieldMatrix& matrix, Scalar scalar) -{ -FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,ROWS,COLS> result; - -for (size_type i = 0; i < ROWS; ++i) -for (size_type j = 0; j < COLS; ++j) -result[i][j] = matrix[i][j] * scalar; - -return result; -} - -//! vector space multiplication with scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator* ( Scalar scalar, const FieldMatrix& matrix) -{ -FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,ROWS,COLS> result; - -for (size_type i = 0; i < ROWS; ++i) -for (size_type j = 0; j < COLS; ++j) -result[i][j] = scalar * matrix[i][j]; - -return result; -} - -//! vector space division by scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator/ ( const FieldMatrix& matrix, Scalar scalar) -{ -FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,ROWS,COLS> result; - -for (size_type i = 0; i < ROWS; ++i) -for (size_type j = 0; j < COLS; ++j) -result[i][j] = matrix[i][j] / scalar; - -return result; -} - -/** \brief Matrix-matrix multiplication -*/ -template <class OtherScalar, int otherCols> -friend auto operator* ( const FieldMatrix& matrixA, -const FieldMatrix<OtherScalar, COLS, otherCols>& matrixB) -{ -FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,ROWS,otherCols> result; - -for (size_type i = 0; i < matrixA.mat_rows(); ++i) -for (size_type j = 0; j < matrixB.mat_cols(); ++j) -{ -result[i][j] = 0; -for (size_type k = 0; k < matrixA.mat_cols(); ++k) -result[i][j] += matrixA[i][k] * matrixB[k][j]; -} - -return result; -} - -//! Multiplies M from the left to this matrix, this matrix is not modified -template<int l> -FieldMatrix<K,l,cols> leftmultiplyany (const FieldMatrix<K,l,rows>& M) const -{ -FieldMatrix<K,l,cols> C; - -for (size_type i=0; i<l; i++) { -for (size_type j=0; j<cols; j++) { -C[i][j] = 0; -for (size_type k=0; k<rows; k++) -C[i][j] += M[i][k]*(*this)[k][j]; -} -} -return C; -} - -using Base::rightmultiply; - -//! Multiplies M from the right to this matrix -template <int r, int c> -FieldMatrix& rightmultiply (const FieldMatrix<K,r,c>& M) -{ -static_assert(r == c, "Cannot rightmultiply with non-square matrix"); -static_assert(r == cols, "Size mismatch"); -FieldMatrix<K,rows,cols> C(*this); - -for (size_type i=0; i<rows; i++) -for (size_type j=0; j<cols; j++) { -(*this)[i][j] = 0; -for (size_type k=0; k<cols; k++) -(*this)[i][j] += C[i][k]*M[k][j]; -} -return *this; -} - -//! Multiplies M from the right to this matrix, this matrix is not modified -template<int l> -FieldMatrix<K,rows,l> rightmultiplyany (const FieldMatrix<K,cols,l>& M) const -{ -FieldMatrix<K,rows,l> C; - -for (size_type i=0; i<rows; i++) { -for (size_type j=0; j<l; j++) { -C[i][j] = 0; -for (size_type k=0; k<cols; k++) -C[i][j] += (*this)[i][k]*M[k][j]; -} -} -return C; -} - -// make this thing a matrix -static constexpr size_type mat_rows() { return ROWS; } -static constexpr size_type mat_cols() { return COLS; } - -row_reference mat_access ( size_type i ) -{ -DUNE_ASSERT_BOUNDS(i < ROWS); -return _data[i]; -} - -const_row_reference mat_access ( size_type i ) const -{ -DUNE_ASSERT_BOUNDS(i < ROWS); -return _data[i]; -} -}; + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + + \brief Implements a matrix constructed from a given type + representing a field and compile-time given number of rows and columns. + */ + + template< class K, int ROWS, int COLS = ROWS > class FieldMatrix; + + template< class K, int ROWS, int COLS > + struct DenseMatVecTraits< FieldMatrix<K,ROWS,COLS> > + { + typedef FieldMatrix<K,ROWS,COLS> derived_type; + + // each row is implemented by a field vector + typedef FieldVector<K,COLS> row_type; + + typedef row_type &row_reference; + typedef const row_type &const_row_reference; + + typedef std::array<row_type,ROWS> container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K, int ROWS, int COLS > + struct FieldTraits< FieldMatrix<K,ROWS,COLS> > + { + typedef typename FieldTraits<K>::field_type field_type; + typedef typename FieldTraits<K>::real_type real_type; + }; + + /** + @brief A dense n x m matrix. + + Matrices represent linear maps from a vector space V to a vector space W. + This class represents such a linear map by storing a two-dimensional + %array of numbers of a given field type K. The number of rows and + columns is given at compile time. + */ + template<class K, int ROWS, int COLS> + class FieldMatrix : public DenseMatrix< FieldMatrix<K,ROWS,COLS> > + { + std::array< FieldVector<K,COLS>, ROWS > _data; + typedef DenseMatrix< FieldMatrix<K,ROWS,COLS> > Base; + public: + + //! export size + enum { + //! The number of rows. + rows = ROWS, + //! The number of columns. + cols = COLS + }; + + typedef typename Base::size_type size_type; + typedef typename Base::row_type row_type; + + typedef typename Base::row_reference row_reference; + typedef typename Base::const_row_reference const_row_reference; + + //===== constructors + /** \brief Default constructor + */ + constexpr FieldMatrix() = default; + + /** \brief Constructor initializing the matrix from a list of vector + */ + FieldMatrix(std::initializer_list<Dune::FieldVector<K, cols> > const &l) { + assert(l.size() == rows); // Actually, this is not needed any more! + std::copy_n(l.begin(), std::min(static_cast<std::size_t>(ROWS), + l.size()), + _data.begin()); + } + + template <class T, + typename = std::enable_if_t<HasDenseMatrixAssigner<FieldMatrix, T>::value>> + FieldMatrix(T const& rhs) + { + *this = rhs; + } + + using Base::operator=; + + //! copy assignment operator + FieldMatrix& operator=(const FieldMatrix&) = default; + + //! copy assignment from FieldMatrix over a different field + template<typename T> + FieldMatrix& operator=(const FieldMatrix<T, ROWS, COLS>& x) + { + _data = x._data; + return *this; + } + + //! no copy assignment from FieldMatrix of different size + template <typename T, int rows, int cols> + FieldMatrix& operator=(FieldMatrix<T,rows,cols> const&) = delete; + + //! vector space addition -- two-argument version + template <class OtherScalar> + friend auto operator+ ( const FieldMatrix& matrixA, + const FieldMatrix<OtherScalar,ROWS,COLS>& matrixB) + { + FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrixA[i][j] + matrixB[i][j]; + + return result; + } + + //! vector space subtraction -- two-argument version + template <class OtherScalar> + friend auto operator- ( const FieldMatrix& matrixA, + const FieldMatrix<OtherScalar,ROWS,COLS>& matrixB) + { + FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrixA[i][j] - matrixB[i][j]; + + return result; + } + + //! vector space multiplication with scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator* ( const FieldMatrix& matrix, Scalar scalar) + { + FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrix[i][j] * scalar; + + return result; + } + + //! vector space multiplication with scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator* ( Scalar scalar, const FieldMatrix& matrix) + { + FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = scalar * matrix[i][j]; + + return result; + } + + //! vector space division by scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator/ ( const FieldMatrix& matrix, Scalar scalar) + { + FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,ROWS,COLS> result; + + for (size_type i = 0; i < ROWS; ++i) + for (size_type j = 0; j < COLS; ++j) + result[i][j] = matrix[i][j] / scalar; + + return result; + } + + /** \brief Matrix-matrix multiplication + */ + template <class OtherScalar, int otherCols> + friend auto operator* ( const FieldMatrix& matrixA, + const FieldMatrix<OtherScalar, COLS, otherCols>& matrixB) + { + FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,ROWS,otherCols> result; + + for (size_type i = 0; i < matrixA.mat_rows(); ++i) + for (size_type j = 0; j < matrixB.mat_cols(); ++j) + { + result[i][j] = 0; + for (size_type k = 0; k < matrixA.mat_cols(); ++k) + result[i][j] += matrixA[i][k] * matrixB[k][j]; + } + + return result; + } + + //! Multiplies M from the left to this matrix, this matrix is not modified + template<int l> + FieldMatrix<K,l,cols> leftmultiplyany (const FieldMatrix<K,l,rows>& M) const + { + FieldMatrix<K,l,cols> C; + + for (size_type i=0; i<l; i++) { + for (size_type j=0; j<cols; j++) { + C[i][j] = 0; + for (size_type k=0; k<rows; k++) + C[i][j] += M[i][k]*(*this)[k][j]; + } + } + return C; + } + + using Base::rightmultiply; + + //! Multiplies M from the right to this matrix + template <int r, int c> + FieldMatrix& rightmultiply (const FieldMatrix<K,r,c>& M) + { + static_assert(r == c, "Cannot rightmultiply with non-square matrix"); + static_assert(r == cols, "Size mismatch"); + FieldMatrix<K,rows,cols> C(*this); + + for (size_type i=0; i<rows; i++) + for (size_type j=0; j<cols; j++) { + (*this)[i][j] = 0; + for (size_type k=0; k<cols; k++) + (*this)[i][j] += C[i][k]*M[k][j]; + } + return *this; + } + + //! Multiplies M from the right to this matrix, this matrix is not modified + template<int l> + FieldMatrix<K,rows,l> rightmultiplyany (const FieldMatrix<K,cols,l>& M) const + { + FieldMatrix<K,rows,l> C; + + for (size_type i=0; i<rows; i++) { + for (size_type j=0; j<l; j++) { + C[i][j] = 0; + for (size_type k=0; k<cols; k++) + C[i][j] += (*this)[i][k]*M[k][j]; + } + } + return C; + } + + // make this thing a matrix + static constexpr size_type mat_rows() { return ROWS; } + static constexpr size_type mat_cols() { return COLS; } + + row_reference mat_access ( size_type i ) + { + DUNE_ASSERT_BOUNDS(i < ROWS); + return _data[i]; + } + + const_row_reference mat_access ( size_type i ) const + { + DUNE_ASSERT_BOUNDS(i < ROWS); + return _data[i]; + } + }; #ifndef DOXYGEN // hide specialization -/** \brief Special type for 1x1 matrices -*/ -template<class K> -class FieldMatrix<K,1,1> : public DenseMatrix< FieldMatrix<K,1,1> > -{ -FieldVector<K,1> _data; -typedef DenseMatrix< FieldMatrix<K,1,1> > Base; -public: -// standard constructor and everything is sufficient ... - -//===== type definitions and constants - -//! The type used for index access and size operations -typedef typename Base::size_type size_type; - -//! We are at the leaf of the block recursion -enum { -//! The number of block levels we contain. -//! This is always one for this type. -blocklevel = 1 -}; - -typedef typename Base::row_type row_type; - -typedef typename Base::row_reference row_reference; -typedef typename Base::const_row_reference const_row_reference; - -//! export size -enum { -//! \brief The number of rows. -//! This is always one for this type. -rows = 1, -//! \brief The number of columns. -//! This is always one for this type. -cols = 1 -}; - -//===== constructors -/** \brief Default constructor -*/ -constexpr FieldMatrix() = default; - -/** \brief Constructor initializing the matrix from a list of vector -*/ -FieldMatrix(std::initializer_list<Dune::FieldVector<K, 1>> const &l) -{ -std::copy_n(l.begin(), std::min(static_cast< std::size_t >( 1 ), l.size()), &_data); -} - -template <class T, -typename = std::enable_if_t<HasDenseMatrixAssigner<FieldMatrix, T>::value>> -FieldMatrix(T const& rhs) -{ -*this = rhs; -} - -using Base::operator=; - -//! vector space addition -- two-argument version -template <class OtherScalar> -friend auto operator+ ( const FieldMatrix& matrixA, -const FieldMatrix<OtherScalar,1,1>& matrixB) -{ -return FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,1,1>{matrixA[0][0] + matrixB[0][0]}; -} - -//! Binary addition when treating FieldMatrix<K,1,1> like K -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator+ ( const FieldMatrix& matrix, -const Scalar& scalar) -{ -return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1>{matrix[0][0] + scalar}; -} - -//! Binary addition when treating FieldMatrix<K,1,1> like K -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator+ ( const Scalar& scalar, -const FieldMatrix& matrix) -{ -return FieldMatrix<typename PromotionTraits<Scalar,K>::PromotedType,1,1>{scalar + matrix[0][0]}; -} - -//! vector space subtraction -- two-argument version -template <class OtherScalar> -friend auto operator- ( const FieldMatrix& matrixA, -const FieldMatrix<OtherScalar,1,1>& matrixB) -{ -return FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,1,1>{matrixA[0][0] - matrixB[0][0]}; -} - -//! Binary subtraction when treating FieldMatrix<K,1,1> like K -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator- ( const FieldMatrix& matrix, -const Scalar& scalar) -{ -return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1>{matrix[0][0] - scalar}; -} - -//! Binary subtraction when treating FieldMatrix<K,1,1> like K -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator- ( const Scalar& scalar, -const FieldMatrix& matrix) -{ -return FieldMatrix<typename PromotionTraits<Scalar,K>::PromotedType,1,1>{scalar - matrix[0][0]}; -} - -//! vector space multiplication with scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator* ( const FieldMatrix& matrix, Scalar scalar) -{ -return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1> {matrix[0][0] * scalar}; -} - -//! vector space multiplication with scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator* ( Scalar scalar, const FieldMatrix& matrix) -{ -return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1> {scalar * matrix[0][0]}; -} - -//! vector space division by scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator/ ( const FieldMatrix& matrix, Scalar scalar) -{ -return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1> {matrix[0][0] / scalar}; -} - -//===== solve - -/** \brief Matrix-matrix multiplication -*/ -template <class OtherScalar, int otherCols> -friend auto operator* ( const FieldMatrix& matrixA, -const FieldMatrix<OtherScalar, 1, otherCols>& matrixB) -{ -FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,1,otherCols> result; - -for (size_type j = 0; j < matrixB.mat_cols(); ++j) -result[0][j] = matrixA[0][0] * matrixB[0][j]; - -return result; -} - -//! Multiplies M from the left to this matrix, this matrix is not modified -template<int l> -FieldMatrix<K,l,1> leftmultiplyany (const FieldMatrix<K,l,1>& M) const -{ -FieldMatrix<K,l,1> C; -for (size_type j=0; j<l; j++) -C[j][0] = M[j][0]*(*this)[0][0]; -return C; -} - -//! left multiplication -FieldMatrix& rightmultiply (const FieldMatrix& M) -{ -_data[0] *= M[0][0]; -return *this; -} - -//! Multiplies M from the right to this matrix, this matrix is not modified -template<int l> -FieldMatrix<K,1,l> rightmultiplyany (const FieldMatrix<K,1,l>& M) const -{ -FieldMatrix<K,1,l> C; - -for (size_type j=0; j<l; j++) -C[0][j] = M[0][j]*_data[0]; -return C; -} - -// make this thing a matrix -static constexpr size_type mat_rows() { return 1; } -static constexpr size_type mat_cols() { return 1; } - -row_reference mat_access ( size_type i ) -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return _data; -} - -const_row_reference mat_access ( size_type i ) const -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return _data; -} - -//! add scalar -FieldMatrix& operator+= (const K& k) -{ -_data[0] += k; -return (*this); -} - -//! subtract scalar -FieldMatrix& operator-= (const K& k) -{ -_data[0] -= k; -return (*this); -} - -//! multiplication with scalar -FieldMatrix& operator*= (const K& k) -{ -_data[0] *= k; -return (*this); -} - -//! division by scalar -FieldMatrix& operator/= (const K& k) -{ -_data[0] /= k; -return (*this); -} - -//===== conversion operator - -operator const K& () const { return _data[0]; } - -}; - -/** \brief Sends the matrix to an output stream */ -template<typename K> -std::ostream& operator<< (std::ostream& s, const FieldMatrix<K,1,1>& a) -{ -s << a[0][0]; -return s; -} + /** \brief Special type for 1x1 matrices + */ + template<class K> + class FieldMatrix<K,1,1> : public DenseMatrix< FieldMatrix<K,1,1> > + { + FieldVector<K,1> _data; + typedef DenseMatrix< FieldMatrix<K,1,1> > Base; + public: + // standard constructor and everything is sufficient ... + + //===== type definitions and constants + + //! The type used for index access and size operations + typedef typename Base::size_type size_type; + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. + //! This is always one for this type. + blocklevel = 1 + }; + + typedef typename Base::row_type row_type; + + typedef typename Base::row_reference row_reference; + typedef typename Base::const_row_reference const_row_reference; + + //! export size + enum { + //! \brief The number of rows. + //! This is always one for this type. + rows = 1, + //! \brief The number of columns. + //! This is always one for this type. + cols = 1 + }; + + //===== constructors + /** \brief Default constructor + */ + constexpr FieldMatrix() = default; + + /** \brief Constructor initializing the matrix from a list of vector + */ + FieldMatrix(std::initializer_list<Dune::FieldVector<K, 1>> const &l) + { + std::copy_n(l.begin(), std::min(static_cast< std::size_t >( 1 ), l.size()), &_data); + } + + template <class T, + typename = std::enable_if_t<HasDenseMatrixAssigner<FieldMatrix, T>::value>> + FieldMatrix(T const& rhs) + { + *this = rhs; + } + + using Base::operator=; + + //! vector space addition -- two-argument version + template <class OtherScalar> + friend auto operator+ ( const FieldMatrix& matrixA, + const FieldMatrix<OtherScalar,1,1>& matrixB) + { + return FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,1,1>{matrixA[0][0] + matrixB[0][0]}; + } + + //! Binary addition when treating FieldMatrix<K,1,1> like K + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator+ ( const FieldMatrix& matrix, + const Scalar& scalar) + { + return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1>{matrix[0][0] + scalar}; + } + + //! Binary addition when treating FieldMatrix<K,1,1> like K + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator+ ( const Scalar& scalar, + const FieldMatrix& matrix) + { + return FieldMatrix<typename PromotionTraits<Scalar,K>::PromotedType,1,1>{scalar + matrix[0][0]}; + } + + //! vector space subtraction -- two-argument version + template <class OtherScalar> + friend auto operator- ( const FieldMatrix& matrixA, + const FieldMatrix<OtherScalar,1,1>& matrixB) + { + return FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,1,1>{matrixA[0][0] - matrixB[0][0]}; + } + + //! Binary subtraction when treating FieldMatrix<K,1,1> like K + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator- ( const FieldMatrix& matrix, + const Scalar& scalar) + { + return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1>{matrix[0][0] - scalar}; + } + + //! Binary subtraction when treating FieldMatrix<K,1,1> like K + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator- ( const Scalar& scalar, + const FieldMatrix& matrix) + { + return FieldMatrix<typename PromotionTraits<Scalar,K>::PromotedType,1,1>{scalar - matrix[0][0]}; + } + + //! vector space multiplication with scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator* ( const FieldMatrix& matrix, Scalar scalar) + { + return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1> {matrix[0][0] * scalar}; + } + + //! vector space multiplication with scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator* ( Scalar scalar, const FieldMatrix& matrix) + { + return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1> {scalar * matrix[0][0]}; + } + + //! vector space division by scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator/ ( const FieldMatrix& matrix, Scalar scalar) + { + return FieldMatrix<typename PromotionTraits<K,Scalar>::PromotedType,1,1> {matrix[0][0] / scalar}; + } + + //===== solve + + /** \brief Matrix-matrix multiplication + */ + template <class OtherScalar, int otherCols> + friend auto operator* ( const FieldMatrix& matrixA, + const FieldMatrix<OtherScalar, 1, otherCols>& matrixB) + { + FieldMatrix<typename PromotionTraits<K,OtherScalar>::PromotedType,1,otherCols> result; + + for (size_type j = 0; j < matrixB.mat_cols(); ++j) + result[0][j] = matrixA[0][0] * matrixB[0][j]; + + return result; + } + + //! Multiplies M from the left to this matrix, this matrix is not modified + template<int l> + FieldMatrix<K,l,1> leftmultiplyany (const FieldMatrix<K,l,1>& M) const + { + FieldMatrix<K,l,1> C; + for (size_type j=0; j<l; j++) + C[j][0] = M[j][0]*(*this)[0][0]; + return C; + } + + //! left multiplication + FieldMatrix& rightmultiply (const FieldMatrix& M) + { + _data[0] *= M[0][0]; + return *this; + } + + //! Multiplies M from the right to this matrix, this matrix is not modified + template<int l> + FieldMatrix<K,1,l> rightmultiplyany (const FieldMatrix<K,1,l>& M) const + { + FieldMatrix<K,1,l> C; + + for (size_type j=0; j<l; j++) + C[0][j] = M[0][j]*_data[0]; + return C; + } + + // make this thing a matrix + static constexpr size_type mat_rows() { return 1; } + static constexpr size_type mat_cols() { return 1; } + + row_reference mat_access ( size_type i ) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return _data; + } + + const_row_reference mat_access ( size_type i ) const + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return _data; + } + + //! add scalar + FieldMatrix& operator+= (const K& k) + { + _data[0] += k; + return (*this); + } + + //! subtract scalar + FieldMatrix& operator-= (const K& k) + { + _data[0] -= k; + return (*this); + } + + //! multiplication with scalar + FieldMatrix& operator*= (const K& k) + { + _data[0] *= k; + return (*this); + } + + //! division by scalar + FieldMatrix& operator/= (const K& k) + { + _data[0] /= k; + return (*this); + } + + //===== conversion operator + + operator const K& () const { return _data[0]; } + + }; + + /** \brief Sends the matrix to an output stream */ + template<typename K> + std::ostream& operator<< (std::ostream& s, const FieldMatrix<K,1,1>& a) + { + s << a[0][0]; + return s; + } #endif // DOXYGEN -namespace FMatrixHelp { - -//! invert scalar without changing the original matrix -template <typename K> -static inline K invertMatrix (const FieldMatrix<K,1,1> &matrix, FieldMatrix<K,1,1> &inverse) -{ -using real_type = typename FieldTraits<K>::real_type; -inverse[0][0] = real_type(1.0)/matrix[0][0]; -return matrix[0][0]; -} - -//! invert scalar without changing the original matrix -template <typename K> -static inline K invertMatrix_retTransposed (const FieldMatrix<K,1,1> &matrix, FieldMatrix<K,1,1> &inverse) -{ -return invertMatrix(matrix,inverse); -} - - -//! invert 2x2 Matrix without changing the original matrix -template <typename K> -static inline K invertMatrix (const FieldMatrix<K,2,2> &matrix, FieldMatrix<K,2,2> &inverse) -{ -using real_type = typename FieldTraits<K>::real_type; -// code generated by maple -K det = (matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]); -K det_1 = real_type(1.0)/det; -inverse[0][0] = matrix[1][1] * det_1; -inverse[0][1] = - matrix[0][1] * det_1; -inverse[1][0] = - matrix[1][0] * det_1; -inverse[1][1] = matrix[0][0] * det_1; -return det; -} - -//! invert 2x2 Matrix without changing the original matrix -//! return transposed matrix -template <typename K> -static inline K invertMatrix_retTransposed (const FieldMatrix<K,2,2> &matrix, FieldMatrix<K,2,2> &inverse) -{ -using real_type = typename FieldTraits<K>::real_type; -// code generated by maple -K det = (matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]); -K det_1 = real_type(1.0)/det; -inverse[0][0] = matrix[1][1] * det_1; -inverse[1][0] = - matrix[0][1] * det_1; -inverse[0][1] = - matrix[1][0] * det_1; -inverse[1][1] = matrix[0][0] * det_1; -return det; -} - -//! invert 3x3 Matrix without changing the original matrix -template <typename K> -static inline K invertMatrix (const FieldMatrix<K,3,3> &matrix, FieldMatrix<K,3,3> &inverse) -{ -using real_type = typename FieldTraits<K>::real_type; -// code generated by maple -K t4 = matrix[0][0] * matrix[1][1]; -K t6 = matrix[0][0] * matrix[1][2]; -K t8 = matrix[0][1] * matrix[1][0]; -K t10 = matrix[0][2] * matrix[1][0]; -K t12 = matrix[0][1] * matrix[2][0]; -K t14 = matrix[0][2] * matrix[2][0]; - -K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+ -t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]); -K t17 = real_type(1.0)/det; - -inverse[0][0] = (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])*t17; -inverse[0][1] = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1])*t17; -inverse[0][2] = (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])*t17; -inverse[1][0] = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])*t17; -inverse[1][1] = (matrix[0][0] * matrix[2][2] - t14) * t17; -inverse[1][2] = -(t6-t10) * t17; -inverse[2][0] = (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]) * t17; -inverse[2][1] = -(matrix[0][0] * matrix[2][1] - t12) * t17; -inverse[2][2] = (t4-t8) * t17; - -return det; -} - -//! invert 3x3 Matrix without changing the original matrix -template <typename K> -static inline K invertMatrix_retTransposed (const FieldMatrix<K,3,3> &matrix, FieldMatrix<K,3,3> &inverse) -{ -using real_type = typename FieldTraits<K>::real_type; -// code generated by maple -K t4 = matrix[0][0] * matrix[1][1]; -K t6 = matrix[0][0] * matrix[1][2]; -K t8 = matrix[0][1] * matrix[1][0]; -K t10 = matrix[0][2] * matrix[1][0]; -K t12 = matrix[0][1] * matrix[2][0]; -K t14 = matrix[0][2] * matrix[2][0]; - -K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+ -t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]); -K t17 = real_type(1.0)/det; - -inverse[0][0] = (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])*t17; -inverse[1][0] = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1])*t17; -inverse[2][0] = (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])*t17; -inverse[0][1] = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])*t17; -inverse[1][1] = (matrix[0][0] * matrix[2][2] - t14) * t17; -inverse[2][1] = -(t6-t10) * t17; -inverse[0][2] = (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]) * t17; -inverse[1][2] = -(matrix[0][0] * matrix[2][1] - t12) * t17; -inverse[2][2] = (t4-t8) * t17; - -return det; -} - -//! calculates ret = A * B -template< class K, int m, int n, int p > -static inline void multMatrix ( const FieldMatrix< K, m, n > &A, -const FieldMatrix< K, n, p > &B, -FieldMatrix< K, m, p > &ret ) -{ -typedef typename FieldMatrix< K, m, p > :: size_type size_type; - -for( size_type i = 0; i < m; ++i ) -{ -for( size_type j = 0; j < p; ++j ) -{ -ret[ i ][ j ] = K( 0 ); -for( size_type k = 0; k < n; ++k ) -ret[ i ][ j ] += A[ i ][ k ] * B[ k ][ j ]; -} -} -} - -//! calculates ret= A_t*A -template <typename K, int rows, int cols> -static inline void multTransposedMatrix(const FieldMatrix<K,rows,cols> &matrix, FieldMatrix<K,cols,cols>& ret) -{ -typedef typename FieldMatrix<K,rows,cols>::size_type size_type; - -for(size_type i=0; i<cols; i++) -for(size_type j=0; j<cols; j++) -{ -ret[i][j]=0.0; -for(size_type k=0; k<rows; k++) -ret[i][j]+=matrix[k][i]*matrix[k][j]; -} -} - -using Dune::DenseMatrixHelp::multAssign; - -//! calculates ret = matrix^T * x -template <typename K, int rows, int cols> -static inline void multAssignTransposed( const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x, FieldVector<K,cols> & ret) -{ -typedef typename FieldMatrix<K,rows,cols>::size_type size_type; - -for(size_type i=0; i<cols; ++i) -{ -ret[i] = 0.0; -for(size_type j=0; j<rows; ++j) -ret[i] += matrix[j][i]*x[j]; -} -} - -//! calculates ret = matrix * x -template <typename K, int rows, int cols> -static inline FieldVector<K,rows> mult(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,cols> & x) -{ -FieldVector<K,rows> ret; -multAssign(matrix,x,ret); -return ret; -} - -//! calculates ret = matrix^T * x -template <typename K, int rows, int cols> -static inline FieldVector<K,cols> multTransposed(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x) -{ -FieldVector<K,cols> ret; -multAssignTransposed( matrix, x, ret ); -return ret; -} - -} // end namespace FMatrixHelp - -/** @} end documentation */ + namespace FMatrixHelp { + + //! invert scalar without changing the original matrix + template <typename K> + static inline K invertMatrix (const FieldMatrix<K,1,1> &matrix, FieldMatrix<K,1,1> &inverse) + { + using real_type = typename FieldTraits<K>::real_type; + inverse[0][0] = real_type(1.0)/matrix[0][0]; + return matrix[0][0]; + } + + //! invert scalar without changing the original matrix + template <typename K> + static inline K invertMatrix_retTransposed (const FieldMatrix<K,1,1> &matrix, FieldMatrix<K,1,1> &inverse) + { + return invertMatrix(matrix,inverse); + } + + + //! invert 2x2 Matrix without changing the original matrix + template <typename K> + static inline K invertMatrix (const FieldMatrix<K,2,2> &matrix, FieldMatrix<K,2,2> &inverse) + { + using real_type = typename FieldTraits<K>::real_type; + // code generated by maple + K det = (matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]); + K det_1 = real_type(1.0)/det; + inverse[0][0] = matrix[1][1] * det_1; + inverse[0][1] = - matrix[0][1] * det_1; + inverse[1][0] = - matrix[1][0] * det_1; + inverse[1][1] = matrix[0][0] * det_1; + return det; + } + + //! invert 2x2 Matrix without changing the original matrix + //! return transposed matrix + template <typename K> + static inline K invertMatrix_retTransposed (const FieldMatrix<K,2,2> &matrix, FieldMatrix<K,2,2> &inverse) + { + using real_type = typename FieldTraits<K>::real_type; + // code generated by maple + K det = (matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]); + K det_1 = real_type(1.0)/det; + inverse[0][0] = matrix[1][1] * det_1; + inverse[1][0] = - matrix[0][1] * det_1; + inverse[0][1] = - matrix[1][0] * det_1; + inverse[1][1] = matrix[0][0] * det_1; + return det; + } + + //! invert 3x3 Matrix without changing the original matrix + template <typename K> + static inline K invertMatrix (const FieldMatrix<K,3,3> &matrix, FieldMatrix<K,3,3> &inverse) + { + using real_type = typename FieldTraits<K>::real_type; + // code generated by maple + K t4 = matrix[0][0] * matrix[1][1]; + K t6 = matrix[0][0] * matrix[1][2]; + K t8 = matrix[0][1] * matrix[1][0]; + K t10 = matrix[0][2] * matrix[1][0]; + K t12 = matrix[0][1] * matrix[2][0]; + K t14 = matrix[0][2] * matrix[2][0]; + + K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+ + t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]); + K t17 = real_type(1.0)/det; + + inverse[0][0] = (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])*t17; + inverse[0][1] = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1])*t17; + inverse[0][2] = (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])*t17; + inverse[1][0] = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])*t17; + inverse[1][1] = (matrix[0][0] * matrix[2][2] - t14) * t17; + inverse[1][2] = -(t6-t10) * t17; + inverse[2][0] = (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]) * t17; + inverse[2][1] = -(matrix[0][0] * matrix[2][1] - t12) * t17; + inverse[2][2] = (t4-t8) * t17; + + return det; + } + + //! invert 3x3 Matrix without changing the original matrix + template <typename K> + static inline K invertMatrix_retTransposed (const FieldMatrix<K,3,3> &matrix, FieldMatrix<K,3,3> &inverse) + { + using real_type = typename FieldTraits<K>::real_type; + // code generated by maple + K t4 = matrix[0][0] * matrix[1][1]; + K t6 = matrix[0][0] * matrix[1][2]; + K t8 = matrix[0][1] * matrix[1][0]; + K t10 = matrix[0][2] * matrix[1][0]; + K t12 = matrix[0][1] * matrix[2][0]; + K t14 = matrix[0][2] * matrix[2][0]; + + K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+ + t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]); + K t17 = real_type(1.0)/det; + + inverse[0][0] = (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])*t17; + inverse[1][0] = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1])*t17; + inverse[2][0] = (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])*t17; + inverse[0][1] = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])*t17; + inverse[1][1] = (matrix[0][0] * matrix[2][2] - t14) * t17; + inverse[2][1] = -(t6-t10) * t17; + inverse[0][2] = (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]) * t17; + inverse[1][2] = -(matrix[0][0] * matrix[2][1] - t12) * t17; + inverse[2][2] = (t4-t8) * t17; + + return det; + } + + //! calculates ret = A * B + template< class K, int m, int n, int p > + static inline void multMatrix ( const FieldMatrix< K, m, n > &A, + const FieldMatrix< K, n, p > &B, + FieldMatrix< K, m, p > &ret ) + { + typedef typename FieldMatrix< K, m, p > :: size_type size_type; + + for( size_type i = 0; i < m; ++i ) + { + for( size_type j = 0; j < p; ++j ) + { + ret[ i ][ j ] = K( 0 ); + for( size_type k = 0; k < n; ++k ) + ret[ i ][ j ] += A[ i ][ k ] * B[ k ][ j ]; + } + } + } + + //! calculates ret= A_t*A + template <typename K, int rows, int cols> + static inline void multTransposedMatrix(const FieldMatrix<K,rows,cols> &matrix, FieldMatrix<K,cols,cols>& ret) + { + typedef typename FieldMatrix<K,rows,cols>::size_type size_type; + + for(size_type i=0; i<cols; i++) + for(size_type j=0; j<cols; j++) + { + ret[i][j]=0.0; + for(size_type k=0; k<rows; k++) + ret[i][j]+=matrix[k][i]*matrix[k][j]; + } + } + + using Dune::DenseMatrixHelp::multAssign; + + //! calculates ret = matrix^T * x + template <typename K, int rows, int cols> + static inline void multAssignTransposed( const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x, FieldVector<K,cols> & ret) + { + typedef typename FieldMatrix<K,rows,cols>::size_type size_type; + + for(size_type i=0; i<cols; ++i) + { + ret[i] = 0.0; + for(size_type j=0; j<rows; ++j) + ret[i] += matrix[j][i]*x[j]; + } + } + + //! calculates ret = matrix * x + template <typename K, int rows, int cols> + static inline FieldVector<K,rows> mult(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,cols> & x) + { + FieldVector<K,rows> ret; + multAssign(matrix,x,ret); + return ret; + } + + //! calculates ret = matrix^T * x + template <typename K, int rows, int cols> + static inline FieldVector<K,cols> multTransposed(const FieldMatrix<K,rows,cols> &matrix, const FieldVector<K,rows> & x) + { + FieldVector<K,cols> ret; + multAssignTransposed( matrix, x, ret ); + return ret; + } + + } // end namespace FMatrixHelp + + /** @} end documentation */ } // end namespace diff --git a/dune/common/fmatrixev.hh b/dune/common/fmatrixev.hh index 3ca829783c5a3bced26c6dae034c2860b9643a6e..c3120d214777d8a35d1ce7bf5ae193f7a8a5ed3d 100644 --- a/dune/common/fmatrixev.hh +++ b/dune/common/fmatrixev.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Eigenvalue computations for the FieldMatrix class -*/ + * \brief Eigenvalue computations for the FieldMatrix class + */ #include <algorithm> #include <iostream> @@ -20,620 +20,620 @@ namespace Dune { -/** -@addtogroup DenseMatVec -@{ -*/ + /** + @addtogroup DenseMatVec + @{ + */ -namespace FMatrixHelp { + namespace FMatrixHelp { #if HAVE_LAPACK -// defined in fmatrixev.cc -extern void eigenValuesLapackCall( -const char* jobz, const char* uplo, const long -int* n, double* a, const long int* lda, double* w, -double* work, const long int* lwork, long int* info); - -extern void eigenValuesNonsymLapackCall( -const char* jobvl, const char* jobvr, const long -int* n, double* a, const long int* lda, double* wr, double* wi, double* vl, -const long int* ldvl, double* vr, const long int* ldvr, double* work, -const long int* lwork, long int* info); - -extern void eigenValuesLapackCall( -const char* jobz, const char* uplo, const long -int* n, float* a, const long int* lda, float* w, -float* work, const long int* lwork, long int* info); - -extern void eigenValuesNonsymLapackCall( -const char* jobvl, const char* jobvr, const long -int* n, float* a, const long int* lda, float* wr, float* wi, float* vl, -const long int* ldvl, float* vr, const long int* ldvr, float* work, -const long int* lwork, long int* info); + // defined in fmatrixev.cc + extern void eigenValuesLapackCall( + const char* jobz, const char* uplo, const long + int* n, double* a, const long int* lda, double* w, + double* work, const long int* lwork, long int* info); + + extern void eigenValuesNonsymLapackCall( + const char* jobvl, const char* jobvr, const long + int* n, double* a, const long int* lda, double* wr, double* wi, double* vl, + const long int* ldvl, double* vr, const long int* ldvr, double* work, + const long int* lwork, long int* info); + + extern void eigenValuesLapackCall( + const char* jobz, const char* uplo, const long + int* n, float* a, const long int* lda, float* w, + float* work, const long int* lwork, long int* info); + + extern void eigenValuesNonsymLapackCall( + const char* jobvl, const char* jobvr, const long + int* n, float* a, const long int* lda, float* wr, float* wi, float* vl, + const long int* ldvl, float* vr, const long int* ldvr, float* work, + const long int* lwork, long int* info); #endif -namespace Impl { -//internal tag to activate/disable code for eigenvector calculation at compile time -enum Jobs { OnlyEigenvalues=0, EigenvaluesEigenvectors=1 }; - -//internal dummy used if only eigenvalues are to be calculated -template<typename K, int dim> -using EVDummy = FieldMatrix<K, dim, dim>; - -//compute the cross-product of two vectors -template<typename K> -inline FieldVector<K,3> crossProduct(const FieldVector<K,3>& vec0, const FieldVector<K,3>& vec1) { -return {vec0[1]*vec1[2] - vec0[2]*vec1[1], vec0[2]*vec1[0] - vec0[0]*vec1[2], vec0[0]*vec1[1] - vec0[1]*vec1[0]}; -} - -template <typename K> -static void eigenValues2dImpl(const FieldMatrix<K, 2, 2>& matrix, -FieldVector<K, 2>& eigenvalues) -{ -using std::sqrt; -const K detM = matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1]; -const K p = 0.5 * (matrix[0][0] + matrix [1][1]); -K q = p * p - detM; -if( q < 0 && q > -1e-14 ) q = 0; -if (q < 0) -{ -std::cout << matrix << std::endl; -// Complex eigenvalues are either caused by non-symmetric matrices or by round-off errors -DUNE_THROW(MathError, "Complex eigenvalue detected (which this implementation cannot handle)."); -} - -// get square root -q = sqrt(q); - -// store eigenvalues in ascending order -eigenvalues[0] = p - q; -eigenvalues[1] = p + q; -} - -/* -This implementation was adapted from the pseudo-code (Python?) implementation found on -http://en.wikipedia.org/wiki/Eigenvalue_algorithm (retrieved late August 2014). -Wikipedia claims to have taken it from -Smith, Oliver K. (April 1961), Eigenvalues of a symmetric 3 × 3 matrix., -Communications of the ACM 4 (4): 168, doi:10.1145/355578.366316 -*/ -template <typename K> -static K eigenValues3dImpl(const FieldMatrix<K, 3, 3>& matrix, -FieldVector<K, 3>& eigenvalues) -{ -using std::sqrt; -using std::acos; -using real_type = typename FieldTraits<K>::real_type; -const K pi = MathematicalConstants<K>::pi(); -K p1 = matrix[0][1]*matrix[0][1] + matrix[0][2]*matrix[0][2] + matrix[1][2]*matrix[1][2]; - -if (p1 <= std::numeric_limits<K>::epsilon()) { -// A is diagonal. -eigenvalues[0] = matrix[0][0]; -eigenvalues[1] = matrix[1][1]; -eigenvalues[2] = matrix[2][2]; -std::sort(eigenvalues.begin(), eigenvalues.end()); - -return 0.0; -} -else -{ -// q = trace(A)/3 -K q = 0; -for (int i=0; i<3; i++) -q += matrix[i][i] / 3.0; - -K p2 = (matrix[0][0] - q)*(matrix[0][0] - q) + (matrix[1][1] - q)*(matrix[1][1] - q) + (matrix[2][2] - q)*(matrix[2][2] - q) + 2.0 * p1; -K p = sqrt(p2 / 6); -// B = (1 / p) * (A - q * I); // I is the identity matrix -FieldMatrix<K,3,3> B; -for (int i=0; i<3; i++) -for (int j=0; j<3; j++) -B[i][j] = (real_type(1.0)/p) * (matrix[i][j] - q*(i==j)); - -K r = B.determinant() / 2.0; - -/*In exact arithmetic for a symmetric matrix -1 <= r <= 1 -but computation error can leave it slightly outside this range. -acos(z) function requires |z| <= 1, but will fail silently -and return NaN if the input is larger than 1 in magnitude. -Thus r is clamped to [-1,1].*/ -r = std::min<K>(std::max<K>(r, -1.0), 1.0); -K phi = acos(r) / 3.0; - -// the eigenvalues satisfy eig[2] <= eig[1] <= eig[0] -eigenvalues[2] = q + 2 * p * cos(phi); -eigenvalues[0] = q + 2 * p * cos(phi + (2*pi/3)); -eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2]; // since trace(matrix) = eig1 + eig2 + eig3 - -return r; -} -} - -//see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf -//Robustly compute a right-handed orthonormal set {u, v, evec0}. -template<typename K> -void orthoComp(const FieldVector<K,3>& evec0, FieldVector<K,3>& u, FieldVector<K,3>& v) { -if(std::abs(evec0[0]) > std::abs(evec0[1])) { -//The component of maximum absolute value is either evec0[0] or evec0[2]. -FieldVector<K,2> temp = {evec0[0], evec0[2]}; -auto L = 1.0 / temp.two_norm(); -u = L * FieldVector<K,3>({-evec0[2], 0.0, evec0[0]}); -} -else { -//The component of maximum absolute value is either evec0[1] or evec0[2]. -FieldVector<K,2> temp = {evec0[1], evec0[2]}; -auto L = 1.0 / temp.two_norm(); -u = L * FieldVector<K,3>({0.0, evec0[2], -evec0[1]}); -} -v = crossProduct(evec0, u); -} - -//see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf -template<typename K> -void eig0(const FieldMatrix<K,3,3>& matrix, K eval0, FieldVector<K,3>& evec0) { -/* Compute a unit-length eigenvector for eigenvalue[i0]. The -matrix is rank 2, so two of the rows are linearly independent. -For a robust computation of the eigenvector, select the two -rows whose cross product has largest length of all pairs of -rows. */ -using Vector = FieldVector<K,3>; -Vector row0 = {matrix[0][0]-eval0, matrix[0][1], matrix[0][2]}; -Vector row1 = {matrix[1][0], matrix[1][1]-eval0, matrix[1][2]}; -Vector row2 = {matrix[2][0], matrix[2][1], matrix[2][2]-eval0}; - -Vector r0xr1 = crossProduct(row0, row1); -Vector r0xr2 = crossProduct(row0, row2); -Vector r1xr2 = crossProduct(row1, row2); -auto d0 = r0xr1.two_norm(); -auto d1 = r0xr2.two_norm(); -auto d2 = r1xr2.two_norm(); - -auto dmax = d0 ; -int imax = 0; -if(d1>dmax) { -dmax = d1; -imax = 1; -} -if(d2>dmax) -imax = 2; - -if(imax == 0) -evec0 = r0xr1 / d0; -else if(imax == 1) -evec0 = r0xr2 / d1; -else -evec0 = r1xr2 / d2; -} - -//see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf -template<typename K> -void eig1(const FieldMatrix<K,3,3>& matrix, const FieldVector<K,3>& evec0, FieldVector<K,3>& evec1, K eval1) { -using Vector = FieldVector<K,3>; - -//Robustly compute a right-handed orthonormal set {u, v, evec0}. -Vector u,v; -orthoComp(evec0, u, v); - -/* Let e be eval1 and let E be a corresponding eigenvector which -is a solution to the linear system (A - e*I)*E = 0. The matrix -(A - e*I) is 3x3, not invertible (so infinitely many -solutions), and has rank 2 when eval1 and eval are different. -It has rank 1 when eval1 and eval2 are equal. Numerically, it -is difficult to compute robustly the rank of a matrix. Instead, -the 3x3 linear system is reduced to a 2x2 system as follows. -Define the 3x2 matrix J = [u,v] whose columns are the u and v -computed previously. Define the 2x1 vector X = J*E. The 2x2 -system is 0 = M * X = (J^T * (A - e*I) * J) * X where J^T is -the transpose of J and M = J^T * (A - e*I) * J is a 2x2 matrix. -The system may be written as -+- -++- -+ +- -+ -| U^T*A*U - e U^T*A*V || x0 | = e * | x0 | -| V^T*A*U V^T*A*V - e || x1 | | x1 | -+- -++ -+ +- -+ -where X has row entries x0 and x1. */ - -Vector Au, Av; -matrix.mv(u, Au); -matrix.mv(v, Av); - -auto m00 = u.dot(Au) - eval1; -auto m01 = u.dot(Av); -auto m11 = v.dot(Av) - eval1; - -/* For robustness, choose the largest-length row of M to compute -the eigenvector. The 2-tuple of coefficients of U and V in the -assignments to eigenvector[1] lies on a circle, and U and V are -unit length and perpendicular, so eigenvector[1] is unit length -(within numerical tolerance). */ -auto absM00 = std::abs(m00); -auto absM01 = std::abs(m01); -auto absM11 = std::abs(m11); -if(absM00 >= absM11) { -auto maxAbsComp = std::max(absM00, absM01); -if(maxAbsComp > 0.0) { -if(absM00 >= absM01) { -m01 /= m00; -m00 = 1.0 / std::sqrt(1.0 + m01*m01); -m01 *= m00; -} -else { -m00 /= m01; -m01 = 1.0 / std::sqrt(1.0 + m00*m00); -m00 *= m01; -} -evec1 = m01*u - m00*v; -} -else -evec1 = u; -} -else { -auto maxAbsComp = std::max(absM11, absM01); -if(maxAbsComp > 0.0) { -if(absM11 >= absM01) { -m01 /= m11; -m11 = 1.0 / std::sqrt(1.0 + m01*m01); -m01 *= m11; -} -else { -m11 /= m01; -m01 = 1.0 / std::sqrt(1.0 + m11*m11); -m11 *= m01; -} -evec1 = m11*u - m01*v; -} -else -evec1 = u; -} -} - -// 1d specialization -template<Jobs Tag, typename K> -static void eigenValuesVectorsImpl(const FieldMatrix<K, 1, 1>& matrix, -FieldVector<K, 1>& eigenValues, -FieldMatrix<K, 1, 1>& eigenVectors) -{ -eigenValues[0] = matrix[0][0]; -if constexpr(Tag==EigenvaluesEigenvectors) -eigenVectors[0] = {1.0}; -} - - -// 2d specialization -template <Jobs Tag, typename K> -static void eigenValuesVectorsImpl(const FieldMatrix<K, 2, 2>& matrix, -FieldVector<K, 2>& eigenValues, -FieldMatrix<K, 2, 2>& eigenVectors) -{ -// Compute eigen values -Impl::eigenValues2dImpl(matrix, eigenValues); - -// Compute eigenvectors by exploiting the Cayley–Hamilton theorem. -// If λ_1, λ_2 are the eigenvalues, then (A - λ_1I )(A - λ_2I ) = (A - λ_2I )(A - λ_1I ) = 0, -// so the columns of (A - λ_2I ) are annihilated by (A - λ_1I ) and vice versa. -// Assuming neither matrix is zero, the columns of each must include eigenvectors -// for the other eigenvalue. (If either matrix is zero, then A is a multiple of the -// identity and any non-zero vector is an eigenvector.) -// From: https://en.wikipedia.org/wiki/Eigenvalue_algorithm#2x2_matrices -if constexpr(Tag==EigenvaluesEigenvectors) { - -// Special casing for multiples of the identity -FieldMatrix<K,2,2> temp = matrix; -temp[0][0] -= eigenValues[0]; -temp[1][1] -= eigenValues[0]; -if(temp.infinity_norm() <= 1e-14) { -eigenVectors[0] = {1.0, 0.0}; -eigenVectors[1] = {0.0, 1.0}; -} -else { -// The columns of A - λ_2I are eigenvectors for λ_1, or zero. -// Take the column with the larger norm to avoid zero columns. -FieldVector<K,2> ev0 = {matrix[0][0]-eigenValues[1], matrix[1][0]}; -FieldVector<K,2> ev1 = {matrix[0][1], matrix[1][1]-eigenValues[1]}; -eigenVectors[0] = (ev0.two_norm2() >= ev1.two_norm2()) ? ev0/ev0.two_norm() : ev1/ev1.two_norm(); - -// The columns of A - λ_1I are eigenvectors for λ_2, or zero. -// Take the column with the larger norm to avoid zero columns. -ev0 = {matrix[0][0]-eigenValues[0], matrix[1][0]}; -ev1 = {matrix[0][1], matrix[1][1]-eigenValues[0]}; -eigenVectors[1] = (ev0.two_norm2() >= ev1.two_norm2()) ? ev0/ev0.two_norm() : ev1/ev1.two_norm(); -} -} -} - -// 3d specialization -template <Jobs Tag, typename K> -static void eigenValuesVectorsImpl(const FieldMatrix<K, 3, 3>& matrix, -FieldVector<K, 3>& eigenValues, -FieldMatrix<K, 3, 3>& eigenVectors) -{ -using Vector = FieldVector<K,3>; -using Matrix = FieldMatrix<K,3,3>; - -//compute eigenvalues -/* Precondition the matrix by factoring out the maximum absolute -value of the components. This guards against floating-point -overflow when computing the eigenvalues.*/ -using std::isnormal; -K maxAbsElement = (isnormal(matrix.infinity_norm())) ? matrix.infinity_norm() : K(1.0); -Matrix scaledMatrix = matrix / maxAbsElement; -K r = Impl::eigenValues3dImpl(scaledMatrix, eigenValues); - -if constexpr(Tag==EigenvaluesEigenvectors) { -K offDiagNorm = Vector{scaledMatrix[0][1],scaledMatrix[0][2],scaledMatrix[1][2]}.two_norm2(); -if (offDiagNorm <= std::numeric_limits<K>::epsilon()) -{ -eigenValues = {scaledMatrix[0][0], scaledMatrix[1][1], scaledMatrix[2][2]}; -eigenVectors = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}; - -// Use bubble sort to jointly sort eigenvalues and eigenvectors -// such that eigenvalues are ascending -if (eigenValues[0] > eigenValues[1]) -{ -std::swap(eigenValues[0], eigenValues[1]); -std::swap(eigenVectors[0], eigenVectors[1]); -} -if (eigenValues[1] > eigenValues[2]) -{ -std::swap(eigenValues[1], eigenValues[2]); -std::swap(eigenVectors[1], eigenVectors[2]); -} -if (eigenValues[0] > eigenValues[1]) -{ -std::swap(eigenValues[0], eigenValues[1]); -std::swap(eigenVectors[0], eigenVectors[1]); -} -} -else { -/*Compute the eigenvectors so that the set -[evec[0], evec[1], evec[2]] is right handed and -orthonormal. */ - -Matrix evec(0.0); -Vector eval(eigenValues); -if(r >= 0) { -Impl::eig0(scaledMatrix, eval[2], evec[2]); -Impl::eig1(scaledMatrix, evec[2], evec[1], eval[1]); -evec[0] = Impl::crossProduct(evec[1], evec[2]); -} -else { -Impl::eig0(scaledMatrix, eval[0], evec[0]); -Impl::eig1(scaledMatrix, evec[0], evec[1], eval[1]); -evec[2] = Impl::crossProduct(evec[0], evec[1]); -} -//sort eval/evec-pairs in ascending order -using EVPair = std::pair<K, Vector>; -std::vector<EVPair> pairs; -for(std::size_t i=0; i<=2; ++i) -pairs.push_back(EVPair(eval[i], evec[i])); -auto comp = [](EVPair x, EVPair y){ return x.first < y.first; }; -std::sort(pairs.begin(), pairs.end(), comp); -for(std::size_t i=0; i<=2; ++i){ -eigenValues[i] = pairs[i].first; -eigenVectors[i] = pairs[i].second; -} -} -} -//The preconditioning scaled the matrix, which scales the eigenvalues. Revert the scaling. -eigenValues *= maxAbsElement; -} - -// forwarding to LAPACK with corresponding tag -template <Jobs Tag, int dim, typename K> -static void eigenValuesVectorsLapackImpl(const FieldMatrix<K, dim, dim>& matrix, -FieldVector<K, dim>& eigenValues, -FieldMatrix<K, dim, dim>& eigenVectors) -{ -{ + namespace Impl { + //internal tag to activate/disable code for eigenvector calculation at compile time + enum Jobs { OnlyEigenvalues=0, EigenvaluesEigenvectors=1 }; + + //internal dummy used if only eigenvalues are to be calculated + template<typename K, int dim> + using EVDummy = FieldMatrix<K, dim, dim>; + + //compute the cross-product of two vectors + template<typename K> + inline FieldVector<K,3> crossProduct(const FieldVector<K,3>& vec0, const FieldVector<K,3>& vec1) { + return {vec0[1]*vec1[2] - vec0[2]*vec1[1], vec0[2]*vec1[0] - vec0[0]*vec1[2], vec0[0]*vec1[1] - vec0[1]*vec1[0]}; + } + + template <typename K> + static void eigenValues2dImpl(const FieldMatrix<K, 2, 2>& matrix, + FieldVector<K, 2>& eigenvalues) + { + using std::sqrt; + const K detM = matrix[0][0] * matrix[1][1] - matrix[1][0] * matrix[0][1]; + const K p = 0.5 * (matrix[0][0] + matrix [1][1]); + K q = p * p - detM; + if( q < 0 && q > -1e-14 ) q = 0; + if (q < 0) + { + std::cout << matrix << std::endl; + // Complex eigenvalues are either caused by non-symmetric matrices or by round-off errors + DUNE_THROW(MathError, "Complex eigenvalue detected (which this implementation cannot handle)."); + } + + // get square root + q = sqrt(q); + + // store eigenvalues in ascending order + eigenvalues[0] = p - q; + eigenvalues[1] = p + q; + } + + /* + This implementation was adapted from the pseudo-code (Python?) implementation found on + http://en.wikipedia.org/wiki/Eigenvalue_algorithm (retrieved late August 2014). + Wikipedia claims to have taken it from + Smith, Oliver K. (April 1961), Eigenvalues of a symmetric 3 × 3 matrix., + Communications of the ACM 4 (4): 168, doi:10.1145/355578.366316 + */ + template <typename K> + static K eigenValues3dImpl(const FieldMatrix<K, 3, 3>& matrix, + FieldVector<K, 3>& eigenvalues) + { + using std::sqrt; + using std::acos; + using real_type = typename FieldTraits<K>::real_type; + const K pi = MathematicalConstants<K>::pi(); + K p1 = matrix[0][1]*matrix[0][1] + matrix[0][2]*matrix[0][2] + matrix[1][2]*matrix[1][2]; + + if (p1 <= std::numeric_limits<K>::epsilon()) { + // A is diagonal. + eigenvalues[0] = matrix[0][0]; + eigenvalues[1] = matrix[1][1]; + eigenvalues[2] = matrix[2][2]; + std::sort(eigenvalues.begin(), eigenvalues.end()); + + return 0.0; + } + else + { + // q = trace(A)/3 + K q = 0; + for (int i=0; i<3; i++) + q += matrix[i][i] / 3.0; + + K p2 = (matrix[0][0] - q)*(matrix[0][0] - q) + (matrix[1][1] - q)*(matrix[1][1] - q) + (matrix[2][2] - q)*(matrix[2][2] - q) + 2.0 * p1; + K p = sqrt(p2 / 6); + // B = (1 / p) * (A - q * I); // I is the identity matrix + FieldMatrix<K,3,3> B; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + B[i][j] = (real_type(1.0)/p) * (matrix[i][j] - q*(i==j)); + + K r = B.determinant() / 2.0; + + /*In exact arithmetic for a symmetric matrix -1 <= r <= 1 + but computation error can leave it slightly outside this range. + acos(z) function requires |z| <= 1, but will fail silently + and return NaN if the input is larger than 1 in magnitude. + Thus r is clamped to [-1,1].*/ + r = std::min<K>(std::max<K>(r, -1.0), 1.0); + K phi = acos(r) / 3.0; + + // the eigenvalues satisfy eig[2] <= eig[1] <= eig[0] + eigenvalues[2] = q + 2 * p * cos(phi); + eigenvalues[0] = q + 2 * p * cos(phi + (2*pi/3)); + eigenvalues[1] = 3 * q - eigenvalues[0] - eigenvalues[2]; // since trace(matrix) = eig1 + eig2 + eig3 + + return r; + } + } + + //see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf + //Robustly compute a right-handed orthonormal set {u, v, evec0}. + template<typename K> + void orthoComp(const FieldVector<K,3>& evec0, FieldVector<K,3>& u, FieldVector<K,3>& v) { + if(std::abs(evec0[0]) > std::abs(evec0[1])) { + //The component of maximum absolute value is either evec0[0] or evec0[2]. + FieldVector<K,2> temp = {evec0[0], evec0[2]}; + auto L = 1.0 / temp.two_norm(); + u = L * FieldVector<K,3>({-evec0[2], 0.0, evec0[0]}); + } + else { + //The component of maximum absolute value is either evec0[1] or evec0[2]. + FieldVector<K,2> temp = {evec0[1], evec0[2]}; + auto L = 1.0 / temp.two_norm(); + u = L * FieldVector<K,3>({0.0, evec0[2], -evec0[1]}); + } + v = crossProduct(evec0, u); + } + + //see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf + template<typename K> + void eig0(const FieldMatrix<K,3,3>& matrix, K eval0, FieldVector<K,3>& evec0) { + /* Compute a unit-length eigenvector for eigenvalue[i0]. The + matrix is rank 2, so two of the rows are linearly independent. + For a robust computation of the eigenvector, select the two + rows whose cross product has largest length of all pairs of + rows. */ + using Vector = FieldVector<K,3>; + Vector row0 = {matrix[0][0]-eval0, matrix[0][1], matrix[0][2]}; + Vector row1 = {matrix[1][0], matrix[1][1]-eval0, matrix[1][2]}; + Vector row2 = {matrix[2][0], matrix[2][1], matrix[2][2]-eval0}; + + Vector r0xr1 = crossProduct(row0, row1); + Vector r0xr2 = crossProduct(row0, row2); + Vector r1xr2 = crossProduct(row1, row2); + auto d0 = r0xr1.two_norm(); + auto d1 = r0xr2.two_norm(); + auto d2 = r1xr2.two_norm(); + + auto dmax = d0 ; + int imax = 0; + if(d1>dmax) { + dmax = d1; + imax = 1; + } + if(d2>dmax) + imax = 2; + + if(imax == 0) + evec0 = r0xr1 / d0; + else if(imax == 1) + evec0 = r0xr2 / d1; + else + evec0 = r1xr2 / d2; + } + + //see https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf + template<typename K> + void eig1(const FieldMatrix<K,3,3>& matrix, const FieldVector<K,3>& evec0, FieldVector<K,3>& evec1, K eval1) { + using Vector = FieldVector<K,3>; + + //Robustly compute a right-handed orthonormal set {u, v, evec0}. + Vector u,v; + orthoComp(evec0, u, v); + + /* Let e be eval1 and let E be a corresponding eigenvector which + is a solution to the linear system (A - e*I)*E = 0. The matrix + (A - e*I) is 3x3, not invertible (so infinitely many + solutions), and has rank 2 when eval1 and eval are different. + It has rank 1 when eval1 and eval2 are equal. Numerically, it + is difficult to compute robustly the rank of a matrix. Instead, + the 3x3 linear system is reduced to a 2x2 system as follows. + Define the 3x2 matrix J = [u,v] whose columns are the u and v + computed previously. Define the 2x1 vector X = J*E. The 2x2 + system is 0 = M * X = (J^T * (A - e*I) * J) * X where J^T is + the transpose of J and M = J^T * (A - e*I) * J is a 2x2 matrix. + The system may be written as + +- -++- -+ +- -+ + | U^T*A*U - e U^T*A*V || x0 | = e * | x0 | + | V^T*A*U V^T*A*V - e || x1 | | x1 | + +- -++ -+ +- -+ + where X has row entries x0 and x1. */ + + Vector Au, Av; + matrix.mv(u, Au); + matrix.mv(v, Av); + + auto m00 = u.dot(Au) - eval1; + auto m01 = u.dot(Av); + auto m11 = v.dot(Av) - eval1; + + /* For robustness, choose the largest-length row of M to compute + the eigenvector. The 2-tuple of coefficients of U and V in the + assignments to eigenvector[1] lies on a circle, and U and V are + unit length and perpendicular, so eigenvector[1] is unit length + (within numerical tolerance). */ + auto absM00 = std::abs(m00); + auto absM01 = std::abs(m01); + auto absM11 = std::abs(m11); + if(absM00 >= absM11) { + auto maxAbsComp = std::max(absM00, absM01); + if(maxAbsComp > 0.0) { + if(absM00 >= absM01) { + m01 /= m00; + m00 = 1.0 / std::sqrt(1.0 + m01*m01); + m01 *= m00; + } + else { + m00 /= m01; + m01 = 1.0 / std::sqrt(1.0 + m00*m00); + m00 *= m01; + } + evec1 = m01*u - m00*v; + } + else + evec1 = u; + } + else { + auto maxAbsComp = std::max(absM11, absM01); + if(maxAbsComp > 0.0) { + if(absM11 >= absM01) { + m01 /= m11; + m11 = 1.0 / std::sqrt(1.0 + m01*m01); + m01 *= m11; + } + else { + m11 /= m01; + m01 = 1.0 / std::sqrt(1.0 + m11*m11); + m11 *= m01; + } + evec1 = m11*u - m01*v; + } + else + evec1 = u; + } + } + + // 1d specialization + template<Jobs Tag, typename K> + static void eigenValuesVectorsImpl(const FieldMatrix<K, 1, 1>& matrix, + FieldVector<K, 1>& eigenValues, + FieldMatrix<K, 1, 1>& eigenVectors) + { + eigenValues[0] = matrix[0][0]; + if constexpr(Tag==EigenvaluesEigenvectors) + eigenVectors[0] = {1.0}; + } + + + // 2d specialization + template <Jobs Tag, typename K> + static void eigenValuesVectorsImpl(const FieldMatrix<K, 2, 2>& matrix, + FieldVector<K, 2>& eigenValues, + FieldMatrix<K, 2, 2>& eigenVectors) + { + // Compute eigen values + Impl::eigenValues2dImpl(matrix, eigenValues); + + // Compute eigenvectors by exploiting the Cayley–Hamilton theorem. + // If λ_1, λ_2 are the eigenvalues, then (A - λ_1I )(A - λ_2I ) = (A - λ_2I )(A - λ_1I ) = 0, + // so the columns of (A - λ_2I ) are annihilated by (A - λ_1I ) and vice versa. + // Assuming neither matrix is zero, the columns of each must include eigenvectors + // for the other eigenvalue. (If either matrix is zero, then A is a multiple of the + // identity and any non-zero vector is an eigenvector.) + // From: https://en.wikipedia.org/wiki/Eigenvalue_algorithm#2x2_matrices + if constexpr(Tag==EigenvaluesEigenvectors) { + + // Special casing for multiples of the identity + FieldMatrix<K,2,2> temp = matrix; + temp[0][0] -= eigenValues[0]; + temp[1][1] -= eigenValues[0]; + if(temp.infinity_norm() <= 1e-14) { + eigenVectors[0] = {1.0, 0.0}; + eigenVectors[1] = {0.0, 1.0}; + } + else { + // The columns of A - λ_2I are eigenvectors for λ_1, or zero. + // Take the column with the larger norm to avoid zero columns. + FieldVector<K,2> ev0 = {matrix[0][0]-eigenValues[1], matrix[1][0]}; + FieldVector<K,2> ev1 = {matrix[0][1], matrix[1][1]-eigenValues[1]}; + eigenVectors[0] = (ev0.two_norm2() >= ev1.two_norm2()) ? ev0/ev0.two_norm() : ev1/ev1.two_norm(); + + // The columns of A - λ_1I are eigenvectors for λ_2, or zero. + // Take the column with the larger norm to avoid zero columns. + ev0 = {matrix[0][0]-eigenValues[0], matrix[1][0]}; + ev1 = {matrix[0][1], matrix[1][1]-eigenValues[0]}; + eigenVectors[1] = (ev0.two_norm2() >= ev1.two_norm2()) ? ev0/ev0.two_norm() : ev1/ev1.two_norm(); + } + } + } + + // 3d specialization + template <Jobs Tag, typename K> + static void eigenValuesVectorsImpl(const FieldMatrix<K, 3, 3>& matrix, + FieldVector<K, 3>& eigenValues, + FieldMatrix<K, 3, 3>& eigenVectors) + { + using Vector = FieldVector<K,3>; + using Matrix = FieldMatrix<K,3,3>; + + //compute eigenvalues + /* Precondition the matrix by factoring out the maximum absolute + value of the components. This guards against floating-point + overflow when computing the eigenvalues.*/ + using std::isnormal; + K maxAbsElement = (isnormal(matrix.infinity_norm())) ? matrix.infinity_norm() : K(1.0); + Matrix scaledMatrix = matrix / maxAbsElement; + K r = Impl::eigenValues3dImpl(scaledMatrix, eigenValues); + + if constexpr(Tag==EigenvaluesEigenvectors) { + K offDiagNorm = Vector{scaledMatrix[0][1],scaledMatrix[0][2],scaledMatrix[1][2]}.two_norm2(); + if (offDiagNorm <= std::numeric_limits<K>::epsilon()) + { + eigenValues = {scaledMatrix[0][0], scaledMatrix[1][1], scaledMatrix[2][2]}; + eigenVectors = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}; + + // Use bubble sort to jointly sort eigenvalues and eigenvectors + // such that eigenvalues are ascending + if (eigenValues[0] > eigenValues[1]) + { + std::swap(eigenValues[0], eigenValues[1]); + std::swap(eigenVectors[0], eigenVectors[1]); + } + if (eigenValues[1] > eigenValues[2]) + { + std::swap(eigenValues[1], eigenValues[2]); + std::swap(eigenVectors[1], eigenVectors[2]); + } + if (eigenValues[0] > eigenValues[1]) + { + std::swap(eigenValues[0], eigenValues[1]); + std::swap(eigenVectors[0], eigenVectors[1]); + } + } + else { + /*Compute the eigenvectors so that the set + [evec[0], evec[1], evec[2]] is right handed and + orthonormal. */ + + Matrix evec(0.0); + Vector eval(eigenValues); + if(r >= 0) { + Impl::eig0(scaledMatrix, eval[2], evec[2]); + Impl::eig1(scaledMatrix, evec[2], evec[1], eval[1]); + evec[0] = Impl::crossProduct(evec[1], evec[2]); + } + else { + Impl::eig0(scaledMatrix, eval[0], evec[0]); + Impl::eig1(scaledMatrix, evec[0], evec[1], eval[1]); + evec[2] = Impl::crossProduct(evec[0], evec[1]); + } + //sort eval/evec-pairs in ascending order + using EVPair = std::pair<K, Vector>; + std::vector<EVPair> pairs; + for(std::size_t i=0; i<=2; ++i) + pairs.push_back(EVPair(eval[i], evec[i])); + auto comp = [](EVPair x, EVPair y){ return x.first < y.first; }; + std::sort(pairs.begin(), pairs.end(), comp); + for(std::size_t i=0; i<=2; ++i){ + eigenValues[i] = pairs[i].first; + eigenVectors[i] = pairs[i].second; + } + } + } + //The preconditioning scaled the matrix, which scales the eigenvalues. Revert the scaling. + eigenValues *= maxAbsElement; + } + + // forwarding to LAPACK with corresponding tag + template <Jobs Tag, int dim, typename K> + static void eigenValuesVectorsLapackImpl(const FieldMatrix<K, dim, dim>& matrix, + FieldVector<K, dim>& eigenValues, + FieldMatrix<K, dim, dim>& eigenVectors) + { + { #if HAVE_LAPACK -/*Lapack uses a proprietary tag to determine whether both eigenvalues and --vectors ('v') or only eigenvalues ('n') should be calculated */ -const char jobz = "nv"[Tag]; - -const long int N = dim ; -const char uplo = 'u'; // use upper triangular matrix - -// length of matrix vector, LWORK >= max(1,3*N-1) -const long int lwork = 3*N -1 ; - -constexpr bool isKLapackType = std::is_same_v<K,double> || std::is_same_v<K,float>; -using LapackNumType = std::conditional_t<isKLapackType, K, double>; - -// matrix to put into dsyev -LapackNumType matrixVector[dim * dim]; - -// copy matrix -int row = 0; -for(int i=0; i<dim; ++i) -{ -for(int j=0; j<dim; ++j, ++row) -{ -matrixVector[ row ] = matrix[ i ][ j ]; -} -} - -// working memory -LapackNumType workSpace[lwork]; - -// return value information -long int info = 0; -LapackNumType* ev; -if constexpr (isKLapackType){ -ev = &eigenValues[0]; -}else{ -ev = new LapackNumType[dim]; -} - -// call LAPACK routine (see fmatrixev.cc) -eigenValuesLapackCall(&jobz, &uplo, &N, &matrixVector[0], &N, -ev, &workSpace[0], &lwork, &info); - -if constexpr (!isKLapackType){ -for(size_t i=0;i<dim;++i) -eigenValues[i] = ev[i]; -delete[] ev; -} - -// restore eigenvectors matrix -if (Tag==Jobs::EigenvaluesEigenvectors){ -row = 0; -for(int i=0; i<dim; ++i) -{ -for(int j=0; j<dim; ++j, ++row) -{ -eigenVectors[ i ][ j ] = matrixVector[ row ]; -} -} -} - -if( info != 0 ) -{ -std::cerr << "For matrix " << matrix << " eigenvalue calculation failed! " << std::endl; -DUNE_THROW(InvalidStateException,"eigenValues: Eigenvalue calculation failed!"); -} + /*Lapack uses a proprietary tag to determine whether both eigenvalues and + -vectors ('v') or only eigenvalues ('n') should be calculated */ + const char jobz = "nv"[Tag]; + + const long int N = dim ; + const char uplo = 'u'; // use upper triangular matrix + + // length of matrix vector, LWORK >= max(1,3*N-1) + const long int lwork = 3*N -1 ; + + constexpr bool isKLapackType = std::is_same_v<K,double> || std::is_same_v<K,float>; + using LapackNumType = std::conditional_t<isKLapackType, K, double>; + + // matrix to put into dsyev + LapackNumType matrixVector[dim * dim]; + + // copy matrix + int row = 0; + for(int i=0; i<dim; ++i) + { + for(int j=0; j<dim; ++j, ++row) + { + matrixVector[ row ] = matrix[ i ][ j ]; + } + } + + // working memory + LapackNumType workSpace[lwork]; + + // return value information + long int info = 0; + LapackNumType* ev; + if constexpr (isKLapackType){ + ev = &eigenValues[0]; + }else{ + ev = new LapackNumType[dim]; + } + + // call LAPACK routine (see fmatrixev.cc) + eigenValuesLapackCall(&jobz, &uplo, &N, &matrixVector[0], &N, + ev, &workSpace[0], &lwork, &info); + + if constexpr (!isKLapackType){ + for(size_t i=0;i<dim;++i) + eigenValues[i] = ev[i]; + delete[] ev; + } + + // restore eigenvectors matrix + if (Tag==Jobs::EigenvaluesEigenvectors){ + row = 0; + for(int i=0; i<dim; ++i) + { + for(int j=0; j<dim; ++j, ++row) + { + eigenVectors[ i ][ j ] = matrixVector[ row ]; + } + } + } + + if( info != 0 ) + { + std::cerr << "For matrix " << matrix << " eigenvalue calculation failed! " << std::endl; + DUNE_THROW(InvalidStateException,"eigenValues: Eigenvalue calculation failed!"); + } #else -DUNE_THROW(NotImplemented,"LAPACK not found!"); + DUNE_THROW(NotImplemented,"LAPACK not found!"); #endif -} -} - -// generic specialization -template <Jobs Tag, int dim, typename K> -static void eigenValuesVectorsImpl(const FieldMatrix<K, dim, dim>& matrix, -FieldVector<K, dim>& eigenValues, -FieldMatrix<K, dim, dim>& eigenVectors) -{ -eigenValuesVectorsLapackImpl<Tag>(matrix,eigenValues,eigenVectors); -} -} //namespace Impl - -/** \brief calculates the eigenvalues of a symmetric field matrix -\param[in] matrix matrix eigenvalues are calculated for -\param[out] eigenValues FieldVector that contains eigenvalues in -ascending order - -\note specializations for dim=1,2,3 exist, for dim>3 LAPACK::dsyev is used -*/ -template <int dim, typename K> -static void eigenValues(const FieldMatrix<K, dim, dim>& matrix, -FieldVector<K ,dim>& eigenValues) -{ -Impl::EVDummy<K,dim> dummy; -Impl::eigenValuesVectorsImpl<Impl::Jobs::OnlyEigenvalues>(matrix, eigenValues, dummy); -} - -/** \brief calculates the eigenvalues and eigenvectors of a symmetric field matrix -\param[in] matrix matrix eigenvalues are calculated for -\param[out] eigenValues FieldVector that contains eigenvalues in -ascending order -\param[out] eigenVectors FieldMatrix that contains the eigenvectors - -\note specializations for dim=1,2,3 exist, for dim>3 LAPACK::dsyev is used -*/ -template <int dim, typename K> -static void eigenValuesVectors(const FieldMatrix<K, dim, dim>& matrix, -FieldVector<K ,dim>& eigenValues, -FieldMatrix<K, dim, dim>& eigenVectors) -{ -Impl::eigenValuesVectorsImpl<Impl::Jobs::EigenvaluesEigenvectors>(matrix, eigenValues, eigenVectors); -} - -/** \brief calculates the eigenvalues of a symmetric field matrix -\param[in] matrix matrix eigenvalues are calculated for -\param[out] eigenValues FieldVector that contains eigenvalues in -ascending order - -\note LAPACK::dsyev is used to calculate the eigenvalues -*/ -template <int dim, typename K> -static void eigenValuesLapack(const FieldMatrix<K, dim, dim>& matrix, -FieldVector<K, dim>& eigenValues) -{ -Impl::EVDummy<K,dim> dummy; -Impl::eigenValuesVectorsLapackImpl<Impl::Jobs::EigenvaluesEigenvectors>(matrix, eigenValues, dummy); -} - -/** \brief calculates the eigenvalues and -vectors of a symmetric field matrix -\param[in] matrix matrix eigenvalues are calculated for -\param[out] eigenValues FieldVector that contains eigenvalues in -ascending order -\param[out] eigenVectors FieldMatrix that contains the eigenvectors - -\note LAPACK::dsyev is used to calculate the eigenvalues and -vectors -*/ -template <int dim, typename K> -static void eigenValuesVectorsLapack(const FieldMatrix<K, dim, dim>& matrix, -FieldVector<K, dim>& eigenValues, -FieldMatrix<K, dim, dim>& eigenVectors) -{ -Impl::eigenValuesVectorsLapackImpl<Impl::Jobs::EigenvaluesEigenvectors>(matrix, eigenValues, eigenVectors); -} - -/** \brief calculates the eigenvalues of a non-symmetric field matrix -\param[in] matrix matrix eigenvalues are calculated for -\param[out] eigenValues FieldVector that contains eigenvalues in -ascending order - -\note LAPACK::dgeev is used to calculate the eigenvalues -*/ -template <int dim, typename K, class C> -static void eigenValuesNonSym(const FieldMatrix<K, dim, dim>& matrix, -FieldVector<C, dim>& eigenValues) -{ + } + } + + // generic specialization + template <Jobs Tag, int dim, typename K> + static void eigenValuesVectorsImpl(const FieldMatrix<K, dim, dim>& matrix, + FieldVector<K, dim>& eigenValues, + FieldMatrix<K, dim, dim>& eigenVectors) + { + eigenValuesVectorsLapackImpl<Tag>(matrix,eigenValues,eigenVectors); + } + } //namespace Impl + + /** \brief calculates the eigenvalues of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + + \note specializations for dim=1,2,3 exist, for dim>3 LAPACK::dsyev is used + */ + template <int dim, typename K> + static void eigenValues(const FieldMatrix<K, dim, dim>& matrix, + FieldVector<K ,dim>& eigenValues) + { + Impl::EVDummy<K,dim> dummy; + Impl::eigenValuesVectorsImpl<Impl::Jobs::OnlyEigenvalues>(matrix, eigenValues, dummy); + } + + /** \brief calculates the eigenvalues and eigenvectors of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + \param[out] eigenVectors FieldMatrix that contains the eigenvectors + + \note specializations for dim=1,2,3 exist, for dim>3 LAPACK::dsyev is used + */ + template <int dim, typename K> + static void eigenValuesVectors(const FieldMatrix<K, dim, dim>& matrix, + FieldVector<K ,dim>& eigenValues, + FieldMatrix<K, dim, dim>& eigenVectors) + { + Impl::eigenValuesVectorsImpl<Impl::Jobs::EigenvaluesEigenvectors>(matrix, eigenValues, eigenVectors); + } + + /** \brief calculates the eigenvalues of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + + \note LAPACK::dsyev is used to calculate the eigenvalues + */ + template <int dim, typename K> + static void eigenValuesLapack(const FieldMatrix<K, dim, dim>& matrix, + FieldVector<K, dim>& eigenValues) + { + Impl::EVDummy<K,dim> dummy; + Impl::eigenValuesVectorsLapackImpl<Impl::Jobs::EigenvaluesEigenvectors>(matrix, eigenValues, dummy); + } + + /** \brief calculates the eigenvalues and -vectors of a symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + \param[out] eigenVectors FieldMatrix that contains the eigenvectors + + \note LAPACK::dsyev is used to calculate the eigenvalues and -vectors + */ + template <int dim, typename K> + static void eigenValuesVectorsLapack(const FieldMatrix<K, dim, dim>& matrix, + FieldVector<K, dim>& eigenValues, + FieldMatrix<K, dim, dim>& eigenVectors) + { + Impl::eigenValuesVectorsLapackImpl<Impl::Jobs::EigenvaluesEigenvectors>(matrix, eigenValues, eigenVectors); + } + + /** \brief calculates the eigenvalues of a non-symmetric field matrix + \param[in] matrix matrix eigenvalues are calculated for + \param[out] eigenValues FieldVector that contains eigenvalues in + ascending order + + \note LAPACK::dgeev is used to calculate the eigenvalues + */ + template <int dim, typename K, class C> + static void eigenValuesNonSym(const FieldMatrix<K, dim, dim>& matrix, + FieldVector<C, dim>& eigenValues) + { #if HAVE_LAPACK -{ -const long int N = dim ; -const char jobvl = 'n'; -const char jobvr = 'n'; - -constexpr bool isKLapackType = std::is_same_v<K,double> || std::is_same_v<K,float>; -using LapackNumType = std::conditional_t<isKLapackType, K, double>; - -// matrix to put into dgeev -LapackNumType matrixVector[dim * dim]; - -// copy matrix -int row = 0; -for(int i=0; i<dim; ++i) -{ -for(int j=0; j<dim; ++j, ++row) -{ -matrixVector[ row ] = matrix[ i ][ j ]; -} -} - -// working memory -LapackNumType eigenR[dim]; -LapackNumType eigenI[dim]; -LapackNumType work[3*dim]; - -// return value information -long int info = 0; -const long int lwork = 3*dim; - -// call LAPACK routine (see fmatrixev_ext.cc) -eigenValuesNonsymLapackCall(&jobvl, &jobvr, &N, &matrixVector[0], &N, -&eigenR[0], &eigenI[0], nullptr, &N, nullptr, &N, &work[0], -&lwork, &info); - -if( info != 0 ) -{ -std::cerr << "For matrix " << matrix << " eigenvalue calculation failed! " << std::endl; -DUNE_THROW(InvalidStateException,"eigenValues: Eigenvalue calculation failed!"); -} -for (int i=0; i<N; ++i) { -eigenValues[i].real = eigenR[i]; -eigenValues[i].imag = eigenI[i]; -} -} + { + const long int N = dim ; + const char jobvl = 'n'; + const char jobvr = 'n'; + + constexpr bool isKLapackType = std::is_same_v<K,double> || std::is_same_v<K,float>; + using LapackNumType = std::conditional_t<isKLapackType, K, double>; + + // matrix to put into dgeev + LapackNumType matrixVector[dim * dim]; + + // copy matrix + int row = 0; + for(int i=0; i<dim; ++i) + { + for(int j=0; j<dim; ++j, ++row) + { + matrixVector[ row ] = matrix[ i ][ j ]; + } + } + + // working memory + LapackNumType eigenR[dim]; + LapackNumType eigenI[dim]; + LapackNumType work[3*dim]; + + // return value information + long int info = 0; + const long int lwork = 3*dim; + + // call LAPACK routine (see fmatrixev_ext.cc) + eigenValuesNonsymLapackCall(&jobvl, &jobvr, &N, &matrixVector[0], &N, + &eigenR[0], &eigenI[0], nullptr, &N, nullptr, &N, &work[0], + &lwork, &info); + + if( info != 0 ) + { + std::cerr << "For matrix " << matrix << " eigenvalue calculation failed! " << std::endl; + DUNE_THROW(InvalidStateException,"eigenValues: Eigenvalue calculation failed!"); + } + for (int i=0; i<N; ++i) { + eigenValues[i].real = eigenR[i]; + eigenValues[i].imag = eigenI[i]; + } + } #else -DUNE_THROW(NotImplemented,"LAPACK not found!"); + DUNE_THROW(NotImplemented,"LAPACK not found!"); #endif -} -} // end namespace FMatrixHelp + } + } // end namespace FMatrixHelp -/** @} end documentation */ + /** @} end documentation */ } // end namespace Dune #endif diff --git a/dune/common/ftraits.hh b/dune/common/ftraits.hh index a8458757bbdb4cd063bd133d3872f92f099388e1..c0f43f93cd20357b6f44c34860f227c253d4f527 100644 --- a/dune/common/ftraits.hh +++ b/dune/common/ftraits.hh @@ -5,57 +5,57 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Type traits to determine the type of reals (when working with complex numbers) -*/ + * \brief Type traits to determine the type of reals (when working with complex numbers) + */ #include <complex> #include <vector> namespace Dune { -/** -@addtogroup DenseMatVec -\brief Type traits to retrieve the field and the real type of classes - -Type traits to retrieve the field and the real type of classes -e.g. that of FieldVector or FieldMatrix -*/ -template<class T> -struct FieldTraits -{ -//! export the type representing the field -typedef T field_type; -//! export the type representing the real type of the field -typedef T real_type; -}; - -template<class T> -struct FieldTraits<const T> -{ -typedef typename FieldTraits<T>::field_type field_type; -typedef typename FieldTraits<T>::real_type real_type; -}; - -template<class T> -struct FieldTraits< std::complex<T> > -{ -typedef std::complex<T> field_type; -typedef T real_type; -}; - -template<class T, unsigned int N> -struct FieldTraits< T[N] > -{ -typedef typename FieldTraits<T>::field_type field_type; -typedef typename FieldTraits<T>::real_type real_type; -}; - -template<class T> -struct FieldTraits< std::vector<T> > -{ -typedef typename FieldTraits<T>::field_type field_type; -typedef typename FieldTraits<T>::real_type real_type; -}; + /** + @addtogroup DenseMatVec + \brief Type traits to retrieve the field and the real type of classes + + Type traits to retrieve the field and the real type of classes + e.g. that of FieldVector or FieldMatrix + */ + template<class T> + struct FieldTraits + { + //! export the type representing the field + typedef T field_type; + //! export the type representing the real type of the field + typedef T real_type; + }; + + template<class T> + struct FieldTraits<const T> + { + typedef typename FieldTraits<T>::field_type field_type; + typedef typename FieldTraits<T>::real_type real_type; + }; + + template<class T> + struct FieldTraits< std::complex<T> > + { + typedef std::complex<T> field_type; + typedef T real_type; + }; + + template<class T, unsigned int N> + struct FieldTraits< T[N] > + { + typedef typename FieldTraits<T>::field_type field_type; + typedef typename FieldTraits<T>::real_type real_type; + }; + + template<class T> + struct FieldTraits< std::vector<T> > + { + typedef typename FieldTraits<T>::field_type field_type; + typedef typename FieldTraits<T>::real_type real_type; + }; } // end namespace Dune diff --git a/dune/common/function.hh b/dune/common/function.hh index a9fc6ac6b693ae37a3d059fa6bba32bf539d4f15..9a5be6c43ba487535d7a458d860d9fa2bebff8f8 100644 --- a/dune/common/function.hh +++ b/dune/common/function.hh @@ -17,144 +17,144 @@ namespace Dune { -/** @addtogroup Common -@{ -*/ - -/*! \file -\brief Simple base class templates for functions. -*/ - -/** -* \brief Base class template for function classes -* -* \tparam Domain Type of input variable. This could be some 'const T' or 'const T&'. -* -* \tparam Range Type of output variable. This should be some non-const 'T&' to allow to return results. -*/ -template <class Domain, class Range> -class -[[deprecated("Dune::Function is deprecated after Dune 2.7. Use C++ " -"function objects instead!")]] -Function -{ -typedef typename std::remove_cv<typename std::remove_reference< Domain >::type >::type RawDomainType; -typedef typename std::remove_cv<typename std::remove_reference< Range >::type >::type RawRangeType; - -public: - -//! Raw type of input variable with removed reference and constness -typedef RawRangeType RangeType; - -//! Raw type of output variable with removed reference and constness -typedef RawDomainType DomainType; - -//! Traits class containing raw types -struct Traits -{ -typedef RawDomainType DomainType; -typedef RawRangeType RangeType; -}; - -/** -* \brief Function evaluation. -* -* \param x Argument for function evaluation. -* \param y Result of function evaluation. -*/ -void evaluate(const typename Traits::DomainType& x, typename Traits::RangeType& y) const; -}; // end of Function class - - - -DUNE_NO_DEPRECATED_BEGIN -/** -* \brief Virtual base class template for function classes. -* -* \see makeVirtualFunction for a helper to convert lambda functions to -* `VirtualFunction` objects. -* -* \tparam DomainType The type of the input variable is 'const DomainType &' -* -* \tparam RangeType The type of the output variable is 'RangeType &' -*/ -template <class DomainType, class RangeType> -class -[[deprecated("Dune::VirtualFunction is deprecated after Dune 2.7. Use C++ " -"function objects and std::function instead!")]] -VirtualFunction : public Function<const DomainType&, RangeType&> -{ -public: -typedef typename Function<const DomainType&, RangeType&>::Traits Traits; - -virtual ~VirtualFunction() {} -/** -* \brief Function evaluation. -* -* \param x Argument for function evaluation. -* \param y Result of function evaluation. -*/ -virtual void evaluate(const typename Traits::DomainType& x, typename Traits::RangeType& y) const = 0; -}; // end of VirtualFunction class -DUNE_NO_DEPRECATED_END - -namespace Impl { - -DUNE_NO_DEPRECATED_BEGIN -template<typename Domain, typename Range, typename F> -class LambdaVirtualFunction final -: public VirtualFunction<Domain, Range> -{ -public: -LambdaVirtualFunction(F&& f) -: f_(std::move(f)) -{} - -LambdaVirtualFunction(const F& f) -: f_(f) -{} - -void evaluate(const Domain& x, Range& y) const override -{ -y = f_(x); -} - -private: -const F f_; -}; -DUNE_NO_DEPRECATED_END - -} /* namespace Impl */ - -/** -* \brief make `VirtualFunction` out of a function object -* -* This helper function wraps a function object into a class implementing -* the `VirtualFunction` interface. It allows for easy use of lambda -* expressions in places that expect a `VirtualFunction`: -\code -void doSomething(const VirtualFunction<double, double>& f); - -auto f = makeVirtualFunction<double, double>( -[](double x) { return x*x; }); -doSomething(f); -\endcode -* -* \returns object of a class derived from `VirtualFunction<Domain, Range>` -* -* \tparam Domain domain of the function -* \tparam Range range of the function -*/ -template<typename Domain, typename Range, typename F> -[[deprecated("Dune::LambdaVirtualFunction is deprecated after Dune 2.7. " -"Use std::function instead!")]] -Impl::LambdaVirtualFunction< Domain, Range, std::decay_t<F> > -makeVirtualFunction(F&& f) -{ -return {std::forward<F>(f)}; -} - -/** @} end documentation */ + /** @addtogroup Common + @{ + */ + + /*! \file + \brief Simple base class templates for functions. + */ + + /** + * \brief Base class template for function classes + * + * \tparam Domain Type of input variable. This could be some 'const T' or 'const T&'. + * + * \tparam Range Type of output variable. This should be some non-const 'T&' to allow to return results. + */ + template <class Domain, class Range> + class + [[deprecated("Dune::Function is deprecated after Dune 2.7. Use C++ " + "function objects instead!")]] + Function + { + typedef typename std::remove_cv<typename std::remove_reference< Domain >::type >::type RawDomainType; + typedef typename std::remove_cv<typename std::remove_reference< Range >::type >::type RawRangeType; + + public: + + //! Raw type of input variable with removed reference and constness + typedef RawRangeType RangeType; + + //! Raw type of output variable with removed reference and constness + typedef RawDomainType DomainType; + + //! Traits class containing raw types + struct Traits + { + typedef RawDomainType DomainType; + typedef RawRangeType RangeType; + }; + + /** + * \brief Function evaluation. + * + * \param x Argument for function evaluation. + * \param y Result of function evaluation. + */ + void evaluate(const typename Traits::DomainType& x, typename Traits::RangeType& y) const; + }; // end of Function class + + + + DUNE_NO_DEPRECATED_BEGIN + /** + * \brief Virtual base class template for function classes. + * + * \see makeVirtualFunction for a helper to convert lambda functions to + * `VirtualFunction` objects. + * + * \tparam DomainType The type of the input variable is 'const DomainType &' + * + * \tparam RangeType The type of the output variable is 'RangeType &' + */ + template <class DomainType, class RangeType> + class + [[deprecated("Dune::VirtualFunction is deprecated after Dune 2.7. Use C++ " + "function objects and std::function instead!")]] + VirtualFunction : public Function<const DomainType&, RangeType&> + { + public: + typedef typename Function<const DomainType&, RangeType&>::Traits Traits; + + virtual ~VirtualFunction() {} + /** + * \brief Function evaluation. + * + * \param x Argument for function evaluation. + * \param y Result of function evaluation. + */ + virtual void evaluate(const typename Traits::DomainType& x, typename Traits::RangeType& y) const = 0; + }; // end of VirtualFunction class + DUNE_NO_DEPRECATED_END + + namespace Impl { + + DUNE_NO_DEPRECATED_BEGIN + template<typename Domain, typename Range, typename F> + class LambdaVirtualFunction final + : public VirtualFunction<Domain, Range> + { + public: + LambdaVirtualFunction(F&& f) + : f_(std::move(f)) + {} + + LambdaVirtualFunction(const F& f) + : f_(f) + {} + + void evaluate(const Domain& x, Range& y) const override + { + y = f_(x); + } + + private: + const F f_; + }; + DUNE_NO_DEPRECATED_END + + } /* namespace Impl */ + + /** + * \brief make `VirtualFunction` out of a function object + * + * This helper function wraps a function object into a class implementing + * the `VirtualFunction` interface. It allows for easy use of lambda + * expressions in places that expect a `VirtualFunction`: + \code + void doSomething(const VirtualFunction<double, double>& f); + + auto f = makeVirtualFunction<double, double>( + [](double x) { return x*x; }); + doSomething(f); + \endcode + * + * \returns object of a class derived from `VirtualFunction<Domain, Range>` + * + * \tparam Domain domain of the function + * \tparam Range range of the function + */ + template<typename Domain, typename Range, typename F> + [[deprecated("Dune::LambdaVirtualFunction is deprecated after Dune 2.7. " + "Use std::function instead!")]] + Impl::LambdaVirtualFunction< Domain, Range, std::decay_t<F> > + makeVirtualFunction(F&& f) + { + return {std::forward<F>(f)}; + } + + /** @} end documentation */ } // end namespace diff --git a/dune/common/fvector.hh b/dune/common/fvector.hh index 843ffb63e5df596512a1276043fe5e23a0422427..e862c3a7491180790467837ff38df1e42846a221 100644 --- a/dune/common/fvector.hh +++ b/dune/common/fvector.hh @@ -27,607 +27,607 @@ namespace Dune { -/** @addtogroup DenseMatVec -@{ -*/ - -/*! \file -* \brief Implements a vector constructed from a given type -representing a field and a compile-time given size. -*/ - -template< class K, int SIZE > class FieldVector; -template< class K, int SIZE > -struct DenseMatVecTraits< FieldVector<K,SIZE> > -{ -typedef FieldVector<K,SIZE> derived_type; -typedef std::array<K,SIZE> container_type; -typedef K value_type; -typedef typename container_type::size_type size_type; -}; - -template< class K, int SIZE > -struct FieldTraits< FieldVector<K,SIZE> > -{ -typedef typename FieldTraits<K>::field_type field_type; -typedef typename FieldTraits<K>::real_type real_type; -}; - -/** -* @brief TMP to check the size of a DenseVectors statically, if possible. -* -* If the implementation type of C is a FieldVector, we statically check -* whether its dimension is SIZE. -* @tparam C The implementation of the other DenseVector -* @tparam SIZE The size we need assume. -*/ -template<typename C, int SIZE> -struct IsFieldVectorSizeCorrect -{ -enum { -/** -*@param True if C is not of type FieldVector or its dimension -* is not equal SIZE. -*/ -value = true -}; -}; - -template<typename T, int SIZE> -struct IsFieldVectorSizeCorrect<FieldVector<T,SIZE>,SIZE> -{ -enum {value = true}; -}; - -template<typename T, int SIZE, int SIZE1> -struct IsFieldVectorSizeCorrect<FieldVector<T,SIZE1>,SIZE> -{ -enum {value = false}; -}; - - -/** \brief vector space out of a tensor product of fields. -* -* \tparam K the field type (use float, double, complex, etc) -* \tparam SIZE number of components. -*/ -template< class K, int SIZE > -class FieldVector : -public DenseVector< FieldVector<K,SIZE> > -{ -std::array<K,SIZE> _data; -typedef DenseVector< FieldVector<K,SIZE> > Base; -public: -//! export size -enum { -//! The size of this vector. -dimension = SIZE -}; - -typedef typename Base::size_type size_type; -typedef typename Base::value_type value_type; - -/** \brief The type used for references to the vector entry */ -typedef value_type& reference; - -/** \brief The type used for const references to the vector entry */ -typedef const value_type& const_reference; - -//! Constructor making default-initialized vector -constexpr FieldVector() -: _data{{}} -{} - -//! Constructor making vector with identical coordinates -explicit FieldVector (const K& t) -{ -std::fill(_data.begin(),_data.end(),t); -} + /** @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief Implements a vector constructed from a given type + representing a field and a compile-time given size. + */ + + template< class K, int SIZE > class FieldVector; + template< class K, int SIZE > + struct DenseMatVecTraits< FieldVector<K,SIZE> > + { + typedef FieldVector<K,SIZE> derived_type; + typedef std::array<K,SIZE> container_type; + typedef K value_type; + typedef typename container_type::size_type size_type; + }; + + template< class K, int SIZE > + struct FieldTraits< FieldVector<K,SIZE> > + { + typedef typename FieldTraits<K>::field_type field_type; + typedef typename FieldTraits<K>::real_type real_type; + }; + + /** + * @brief TMP to check the size of a DenseVectors statically, if possible. + * + * If the implementation type of C is a FieldVector, we statically check + * whether its dimension is SIZE. + * @tparam C The implementation of the other DenseVector + * @tparam SIZE The size we need assume. + */ + template<typename C, int SIZE> + struct IsFieldVectorSizeCorrect + { + enum { + /** + *@param True if C is not of type FieldVector or its dimension + * is not equal SIZE. + */ + value = true + }; + }; + + template<typename T, int SIZE> + struct IsFieldVectorSizeCorrect<FieldVector<T,SIZE>,SIZE> + { + enum {value = true}; + }; + + template<typename T, int SIZE, int SIZE1> + struct IsFieldVectorSizeCorrect<FieldVector<T,SIZE1>,SIZE> + { + enum {value = false}; + }; + + + /** \brief vector space out of a tensor product of fields. + * + * \tparam K the field type (use float, double, complex, etc) + * \tparam SIZE number of components. + */ + template< class K, int SIZE > + class FieldVector : + public DenseVector< FieldVector<K,SIZE> > + { + std::array<K,SIZE> _data; + typedef DenseVector< FieldVector<K,SIZE> > Base; + public: + //! export size + enum { + //! The size of this vector. + dimension = SIZE + }; + + typedef typename Base::size_type size_type; + typedef typename Base::value_type value_type; + + /** \brief The type used for references to the vector entry */ + typedef value_type& reference; + + /** \brief The type used for const references to the vector entry */ + typedef const value_type& const_reference; + + //! Constructor making default-initialized vector + constexpr FieldVector() + : _data{{}} + {} + + //! Constructor making vector with identical coordinates + explicit FieldVector (const K& t) + { + std::fill(_data.begin(),_data.end(),t); + } #if __GNUC__ == 5 && !defined(__clang__) -// `... = default;` causes an internal compiler error on GCC 5.4 (Ubuntu 16.04) -//! copy constructor -FieldVector(const FieldVector& x) : _data(x._data) {} + // `... = default;` causes an internal compiler error on GCC 5.4 (Ubuntu 16.04) + //! copy constructor + FieldVector(const FieldVector& x) : _data(x._data) {} #else -//! Copy constructor -FieldVector (const FieldVector&) = default; + //! Copy constructor + FieldVector (const FieldVector&) = default; #endif -/** \brief Construct from a std::initializer_list */ -FieldVector (std::initializer_list<K> const &l) -{ -assert(l.size() == dimension);// Actually, this is not needed any more! -std::copy_n(l.begin(), std::min(static_cast<std::size_t>(dimension), -l.size()), -_data.begin()); -} - -//! copy assignment operator -FieldVector& operator= (const FieldVector&) = default; - -template <typename T> -FieldVector& operator= (const FieldVector<T, SIZE>& x) -{ -std::copy_n(x.begin(), SIZE, _data.begin()); -return *this; -} - -template<typename T, int N> -FieldVector& operator=(const FieldVector<T, N>&) = delete; - -/** -* \brief Copy constructor from a second vector of possibly different type -* -* If the DenseVector type of the this constructor's argument -* is implemented by a FieldVector, it is statically checked -* if it has the correct size. If this is not the case -* the constructor is removed from the overload set using SFINAE. -* -* \param[in] x A DenseVector with correct size. -* \param[in] dummy A void* dummy argument needed by SFINAE. -*/ -template<class C> -FieldVector (const DenseVector<C> & x, typename std::enable_if<IsFieldVectorSizeCorrect<C,SIZE>::value>::type* dummy=0 ) -{ -DUNE_UNUSED_PARAMETER(dummy); -// do a run-time size check, for the case that x is not a FieldVector -assert(x.size() == SIZE); // Actually this is not needed any more! -std::copy_n(x.begin(), std::min(static_cast<std::size_t>(SIZE),x.size()), _data.begin()); -} - -//! Constructor making vector with identical coordinates -template<class K1> -explicit FieldVector (const FieldVector<K1,SIZE> & x) -{ -std::copy_n(x.begin(), SIZE, _data.begin()); -} - -template<typename T, int N> -explicit FieldVector(const FieldVector<T, N>&) = delete; - -using Base::operator=; - -// make this thing a vector -static constexpr size_type size () { return SIZE; } - -K & operator[](size_type i) { -DUNE_ASSERT_BOUNDS(i < SIZE); -return _data[i]; -} -const K & operator[](size_type i) const { -DUNE_ASSERT_BOUNDS(i < SIZE); -return _data[i]; -} - -//! return pointer to underlying array -K* data() noexcept -{ -return _data.data(); -} - -//! return pointer to underlying array -const K* data() const noexcept -{ -return _data.data(); -} - -//! vector space multiplication with scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator* ( const FieldVector& vector, Scalar scalar) -{ -FieldVector<typename PromotionTraits<value_type,Scalar>::PromotedType,SIZE> result; - -for (size_type i = 0; i < vector.size(); ++i) -result[i] = vector[i] * scalar; - -return result; -} - -//! vector space multiplication with scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator* ( Scalar scalar, const FieldVector& vector) -{ -FieldVector<typename PromotionTraits<value_type,Scalar>::PromotedType,SIZE> result; - -for (size_type i = 0; i < vector.size(); ++i) -result[i] = scalar * vector[i]; - -return result; -} - -//! vector space division by scalar -template <class Scalar, -std::enable_if_t<IsNumber<Scalar>::value, int> = 0> -friend auto operator/ ( const FieldVector& vector, Scalar scalar) -{ -FieldVector<typename PromotionTraits<value_type,Scalar>::PromotedType,SIZE> result; - -for (size_type i = 0; i < vector.size(); ++i) -result[i] = vector[i] / scalar; - -return result; -} - -}; - -/** \brief Read a FieldVector from an input stream -* \relates FieldVector -* -* \note This operator is STL compliant, i.e., the content of v is only -* changed if the read operation is successful. -* -* \param[in] in std :: istream to read from -* \param[out] v FieldVector to be read -* -* \returns the input stream (in) -*/ -template<class K, int SIZE> -inline std::istream &operator>> ( std::istream &in, -FieldVector<K, SIZE> &v ) -{ -FieldVector<K, SIZE> w; -for( typename FieldVector<K, SIZE>::size_type i = 0; i < SIZE; ++i ) -in >> w[ i ]; -if(in) -v = w; -return in; -} + /** \brief Construct from a std::initializer_list */ + FieldVector (std::initializer_list<K> const &l) + { + assert(l.size() == dimension);// Actually, this is not needed any more! + std::copy_n(l.begin(), std::min(static_cast<std::size_t>(dimension), + l.size()), + _data.begin()); + } + + //! copy assignment operator + FieldVector& operator= (const FieldVector&) = default; + + template <typename T> + FieldVector& operator= (const FieldVector<T, SIZE>& x) + { + std::copy_n(x.begin(), SIZE, _data.begin()); + return *this; + } + + template<typename T, int N> + FieldVector& operator=(const FieldVector<T, N>&) = delete; + + /** + * \brief Copy constructor from a second vector of possibly different type + * + * If the DenseVector type of the this constructor's argument + * is implemented by a FieldVector, it is statically checked + * if it has the correct size. If this is not the case + * the constructor is removed from the overload set using SFINAE. + * + * \param[in] x A DenseVector with correct size. + * \param[in] dummy A void* dummy argument needed by SFINAE. + */ + template<class C> + FieldVector (const DenseVector<C> & x, typename std::enable_if<IsFieldVectorSizeCorrect<C,SIZE>::value>::type* dummy=0 ) + { + DUNE_UNUSED_PARAMETER(dummy); + // do a run-time size check, for the case that x is not a FieldVector + assert(x.size() == SIZE); // Actually this is not needed any more! + std::copy_n(x.begin(), std::min(static_cast<std::size_t>(SIZE),x.size()), _data.begin()); + } + + //! Constructor making vector with identical coordinates + template<class K1> + explicit FieldVector (const FieldVector<K1,SIZE> & x) + { + std::copy_n(x.begin(), SIZE, _data.begin()); + } + + template<typename T, int N> + explicit FieldVector(const FieldVector<T, N>&) = delete; + + using Base::operator=; + + // make this thing a vector + static constexpr size_type size () { return SIZE; } + + K & operator[](size_type i) { + DUNE_ASSERT_BOUNDS(i < SIZE); + return _data[i]; + } + const K & operator[](size_type i) const { + DUNE_ASSERT_BOUNDS(i < SIZE); + return _data[i]; + } + + //! return pointer to underlying array + K* data() noexcept + { + return _data.data(); + } + + //! return pointer to underlying array + const K* data() const noexcept + { + return _data.data(); + } + + //! vector space multiplication with scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator* ( const FieldVector& vector, Scalar scalar) + { + FieldVector<typename PromotionTraits<value_type,Scalar>::PromotedType,SIZE> result; + + for (size_type i = 0; i < vector.size(); ++i) + result[i] = vector[i] * scalar; + + return result; + } + + //! vector space multiplication with scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator* ( Scalar scalar, const FieldVector& vector) + { + FieldVector<typename PromotionTraits<value_type,Scalar>::PromotedType,SIZE> result; + + for (size_type i = 0; i < vector.size(); ++i) + result[i] = scalar * vector[i]; + + return result; + } + + //! vector space division by scalar + template <class Scalar, + std::enable_if_t<IsNumber<Scalar>::value, int> = 0> + friend auto operator/ ( const FieldVector& vector, Scalar scalar) + { + FieldVector<typename PromotionTraits<value_type,Scalar>::PromotedType,SIZE> result; + + for (size_type i = 0; i < vector.size(); ++i) + result[i] = vector[i] / scalar; + + return result; + } + + }; + + /** \brief Read a FieldVector from an input stream + * \relates FieldVector + * + * \note This operator is STL compliant, i.e., the content of v is only + * changed if the read operation is successful. + * + * \param[in] in std :: istream to read from + * \param[out] v FieldVector to be read + * + * \returns the input stream (in) + */ + template<class K, int SIZE> + inline std::istream &operator>> ( std::istream &in, + FieldVector<K, SIZE> &v ) + { + FieldVector<K, SIZE> w; + for( typename FieldVector<K, SIZE>::size_type i = 0; i < SIZE; ++i ) + in >> w[ i ]; + if(in) + v = w; + return in; + } #ifndef DOXYGEN -template< class K > -struct DenseMatVecTraits< FieldVector<K,1> > -{ -typedef FieldVector<K,1> derived_type; -typedef K container_type; -typedef K value_type; -typedef size_t size_type; -}; - -/** \brief Vectors containing only one component -*/ -template<class K> -class FieldVector<K, 1> : -public DenseVector< FieldVector<K,1> > -{ -K _data; -typedef DenseVector< FieldVector<K,1> > Base; -public: -//! export size -enum { -//! The size of this vector. -dimension = 1 -}; - -typedef typename Base::size_type size_type; - -/** \brief The type used for references to the vector entry */ -typedef K& reference; - -/** \brief The type used for const references to the vector entry */ -typedef const K& const_reference; - -//===== construction - -/** \brief Default constructor */ -constexpr FieldVector () -: _data() -{} - -/** \brief Constructor with a given scalar */ -template<typename T, -typename EnableIf = typename std::enable_if< -std::is_convertible<T, K>::value && -! std::is_base_of<DenseVector<typename FieldTraits<T>::field_type>, K ->::value ->::type -> -FieldVector (const T& k) : _data(k) {} - -//! Constructor from static vector of different type -template<class C, -std::enable_if_t< -std::is_assignable<K&, typename DenseVector<C>::value_type>::value, int> = 0> -FieldVector (const DenseVector<C> & x) -{ -static_assert(((bool)IsFieldVectorSizeCorrect<C,1>::value), "FieldVectors do not match in dimension!"); -assert(x.size() == 1); -_data = x[0]; -} - -//! copy constructor -FieldVector(const FieldVector&) = default; - -//! copy assignment operator -FieldVector& operator=(const FieldVector&) = default; - -template <typename T> -FieldVector& operator= (const FieldVector<T, 1>& other) -{ -_data = other[0]; -return *this; -} - -template<typename T, int N> -FieldVector& operator=(const FieldVector<T, N>&) = delete; - -/** \brief Construct from a std::initializer_list */ -FieldVector (std::initializer_list<K> const &l) -{ -assert(l.size() == 1); -_data = *l.begin(); -} - -//! Assignment operator for scalar -template<typename T, -typename EnableIf = typename std::enable_if< -std::is_assignable<K&, T>::value && -! std::is_base_of<DenseVector<typename FieldTraits<T>::field_type>, K ->::value ->::type -> -inline FieldVector& operator= (const T& k) -{ -_data = k; -return *this; -} - -//===== forward methods to container -static constexpr size_type size () { return 1; } - -K & operator[](size_type i) -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return _data; -} -const K & operator[](size_type i) const -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return _data; -} - -//! return pointer to underlying array -K* data() noexcept -{ -return &_data; -} - -//! return pointer to underlying array -const K* data() const noexcept -{ -return &_data; -} - -//===== conversion operator - -/** \brief Conversion operator */ -operator K& () { return _data; } - -/** \brief Const conversion operator */ -operator const K& () const { return _data; } -}; - -/* ----- FV / FV ----- */ -/* mostly not necessary as these operations are already covered via the cast operator */ - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator> (const FieldVector<K,1>& a, const FieldVector<K,1>& b) -{ -return a[0]>b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator>= (const FieldVector<K,1>& a, const FieldVector<K,1>& b) -{ -return a[0]>=b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator< (const FieldVector<K,1>& a, const FieldVector<K,1>& b) -{ -return a[0]<b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator<= (const FieldVector<K,1>& a, const FieldVector<K,1>& b) -{ -return a[0]<=b[0]; -} - -/* ----- FV / scalar ----- */ - -//! Binary addition, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator+ (const FieldVector<K,1>& a, const K b) -{ -return a[0]+b; -} - -//! Binary subtraction, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator- (const FieldVector<K,1>& a, const K b) -{ -return a[0]-b; -} - -//! Binary multiplication, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator* (const FieldVector<K,1>& a, const K b) -{ -return a[0]*b; -} - -//! Binary division, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator/ (const FieldVector<K,1>& a, const K b) -{ -return a[0]/b; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator> (const FieldVector<K,1>& a, const K b) -{ -return a[0]>b; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator>= (const FieldVector<K,1>& a, const K b) -{ -return a[0]>=b; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator< (const FieldVector<K,1>& a, const K b) -{ -return a[0]<b; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator<= (const FieldVector<K,1>& a, const K b) -{ -return a[0]<=b; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator== (const FieldVector<K,1>& a, const K b) -{ -return a[0]==b; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator!= (const FieldVector<K,1>& a, const K b) -{ -return a[0]!=b; -} - -/* ----- scalar / FV ------ */ - -//! Binary addition, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator+ (const K a, const FieldVector<K,1>& b) -{ -return a+b[0]; -} - -//! Binary subtraction, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator- (const K a, const FieldVector<K,1>& b) -{ -return a-b[0]; -} - -//! Binary multiplication, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator* (const K a, const FieldVector<K,1>& b) -{ -return a*b[0]; -} - -//! Binary division, when using FieldVector<K,1> like K -template<class K> -inline FieldVector<K,1> operator/ (const K a, const FieldVector<K,1>& b) -{ -return a/b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator> (const K a, const FieldVector<K,1>& b) -{ -return a>b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator>= (const K a, const FieldVector<K,1>& b) -{ -return a>=b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator< (const K a, const FieldVector<K,1>& b) -{ -return a<b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator<= (const K a, const FieldVector<K,1>& b) -{ -return a<=b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator== (const K a, const FieldVector<K,1>& b) -{ -return a==b[0]; -} - -//! Binary compare, when using FieldVector<K,1> like K -template<class K> -inline bool operator!= (const K a, const FieldVector<K,1>& b) -{ -return a!=b[0]; -} + template< class K > + struct DenseMatVecTraits< FieldVector<K,1> > + { + typedef FieldVector<K,1> derived_type; + typedef K container_type; + typedef K value_type; + typedef size_t size_type; + }; + + /** \brief Vectors containing only one component + */ + template<class K> + class FieldVector<K, 1> : + public DenseVector< FieldVector<K,1> > + { + K _data; + typedef DenseVector< FieldVector<K,1> > Base; + public: + //! export size + enum { + //! The size of this vector. + dimension = 1 + }; + + typedef typename Base::size_type size_type; + + /** \brief The type used for references to the vector entry */ + typedef K& reference; + + /** \brief The type used for const references to the vector entry */ + typedef const K& const_reference; + + //===== construction + + /** \brief Default constructor */ + constexpr FieldVector () + : _data() + {} + + /** \brief Constructor with a given scalar */ + template<typename T, + typename EnableIf = typename std::enable_if< + std::is_convertible<T, K>::value && + ! std::is_base_of<DenseVector<typename FieldTraits<T>::field_type>, K + >::value + >::type + > + FieldVector (const T& k) : _data(k) {} + + //! Constructor from static vector of different type + template<class C, + std::enable_if_t< + std::is_assignable<K&, typename DenseVector<C>::value_type>::value, int> = 0> + FieldVector (const DenseVector<C> & x) + { + static_assert(((bool)IsFieldVectorSizeCorrect<C,1>::value), "FieldVectors do not match in dimension!"); + assert(x.size() == 1); + _data = x[0]; + } + + //! copy constructor + FieldVector(const FieldVector&) = default; + + //! copy assignment operator + FieldVector& operator=(const FieldVector&) = default; + + template <typename T> + FieldVector& operator= (const FieldVector<T, 1>& other) + { + _data = other[0]; + return *this; + } + + template<typename T, int N> + FieldVector& operator=(const FieldVector<T, N>&) = delete; + + /** \brief Construct from a std::initializer_list */ + FieldVector (std::initializer_list<K> const &l) + { + assert(l.size() == 1); + _data = *l.begin(); + } + + //! Assignment operator for scalar + template<typename T, + typename EnableIf = typename std::enable_if< + std::is_assignable<K&, T>::value && + ! std::is_base_of<DenseVector<typename FieldTraits<T>::field_type>, K + >::value + >::type + > + inline FieldVector& operator= (const T& k) + { + _data = k; + return *this; + } + + //===== forward methods to container + static constexpr size_type size () { return 1; } + + K & operator[](size_type i) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return _data; + } + const K & operator[](size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return _data; + } + + //! return pointer to underlying array + K* data() noexcept + { + return &_data; + } + + //! return pointer to underlying array + const K* data() const noexcept + { + return &_data; + } + + //===== conversion operator + + /** \brief Conversion operator */ + operator K& () { return _data; } + + /** \brief Const conversion operator */ + operator const K& () const { return _data; } + }; + + /* ----- FV / FV ----- */ + /* mostly not necessary as these operations are already covered via the cast operator */ + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator> (const FieldVector<K,1>& a, const FieldVector<K,1>& b) + { + return a[0]>b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator>= (const FieldVector<K,1>& a, const FieldVector<K,1>& b) + { + return a[0]>=b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator< (const FieldVector<K,1>& a, const FieldVector<K,1>& b) + { + return a[0]<b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator<= (const FieldVector<K,1>& a, const FieldVector<K,1>& b) + { + return a[0]<=b[0]; + } + + /* ----- FV / scalar ----- */ + + //! Binary addition, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator+ (const FieldVector<K,1>& a, const K b) + { + return a[0]+b; + } + + //! Binary subtraction, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator- (const FieldVector<K,1>& a, const K b) + { + return a[0]-b; + } + + //! Binary multiplication, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator* (const FieldVector<K,1>& a, const K b) + { + return a[0]*b; + } + + //! Binary division, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator/ (const FieldVector<K,1>& a, const K b) + { + return a[0]/b; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator> (const FieldVector<K,1>& a, const K b) + { + return a[0]>b; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator>= (const FieldVector<K,1>& a, const K b) + { + return a[0]>=b; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator< (const FieldVector<K,1>& a, const K b) + { + return a[0]<b; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator<= (const FieldVector<K,1>& a, const K b) + { + return a[0]<=b; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator== (const FieldVector<K,1>& a, const K b) + { + return a[0]==b; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator!= (const FieldVector<K,1>& a, const K b) + { + return a[0]!=b; + } + + /* ----- scalar / FV ------ */ + + //! Binary addition, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator+ (const K a, const FieldVector<K,1>& b) + { + return a+b[0]; + } + + //! Binary subtraction, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator- (const K a, const FieldVector<K,1>& b) + { + return a-b[0]; + } + + //! Binary multiplication, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator* (const K a, const FieldVector<K,1>& b) + { + return a*b[0]; + } + + //! Binary division, when using FieldVector<K,1> like K + template<class K> + inline FieldVector<K,1> operator/ (const K a, const FieldVector<K,1>& b) + { + return a/b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator> (const K a, const FieldVector<K,1>& b) + { + return a>b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator>= (const K a, const FieldVector<K,1>& b) + { + return a>=b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator< (const K a, const FieldVector<K,1>& b) + { + return a<b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator<= (const K a, const FieldVector<K,1>& b) + { + return a<=b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator== (const K a, const FieldVector<K,1>& b) + { + return a==b[0]; + } + + //! Binary compare, when using FieldVector<K,1> like K + template<class K> + inline bool operator!= (const K a, const FieldVector<K,1>& b) + { + return a!=b[0]; + } #endif -/* Overloads for common classification functions */ -namespace MathOverloads { - -// ! Returns whether all entries are finite -template<class K, int SIZE> -auto isFinite(const FieldVector<K,SIZE> &b, PriorityTag<2>, ADLTag) { -bool out = true; -for(int i=0; i<SIZE; i++) { -out &= Dune::isFinite(b[i]); -} -return out; -} - -// ! Returns whether any entry is infinite -template<class K, int SIZE> -bool isInf(const FieldVector<K,SIZE> &b, PriorityTag<2>, ADLTag) { -bool out = false; -for(int i=0; i<SIZE; i++) { -out |= Dune::isInf(b[i]); -} -return out; -} - -// ! Returns whether any entry is NaN -template<class K, int SIZE, typename = std::enable_if_t<HasNaN<K>::value>> -bool isNaN(const FieldVector<K,SIZE> &b, PriorityTag<2>, ADLTag) { -bool out = false; -for(int i=0; i<SIZE; i++) { -out |= Dune::isNaN(b[i]); -} -return out; -} - -// ! Returns true if either b or c is NaN -template<class K, typename = std::enable_if_t<HasNaN<K>::value>> -bool isUnordered(const FieldVector<K,1> &b, const FieldVector<K,1> &c, -PriorityTag<2>, ADLTag) { -return Dune::isUnordered(b[0],c[0]); -} -} //MathOverloads - -/** @} end documentation */ + /* Overloads for common classification functions */ + namespace MathOverloads { + + // ! Returns whether all entries are finite + template<class K, int SIZE> + auto isFinite(const FieldVector<K,SIZE> &b, PriorityTag<2>, ADLTag) { + bool out = true; + for(int i=0; i<SIZE; i++) { + out &= Dune::isFinite(b[i]); + } + return out; + } + + // ! Returns whether any entry is infinite + template<class K, int SIZE> + bool isInf(const FieldVector<K,SIZE> &b, PriorityTag<2>, ADLTag) { + bool out = false; + for(int i=0; i<SIZE; i++) { + out |= Dune::isInf(b[i]); + } + return out; + } + + // ! Returns whether any entry is NaN + template<class K, int SIZE, typename = std::enable_if_t<HasNaN<K>::value>> + bool isNaN(const FieldVector<K,SIZE> &b, PriorityTag<2>, ADLTag) { + bool out = false; + for(int i=0; i<SIZE; i++) { + out |= Dune::isNaN(b[i]); + } + return out; + } + + // ! Returns true if either b or c is NaN + template<class K, typename = std::enable_if_t<HasNaN<K>::value>> + bool isUnordered(const FieldVector<K,1> &b, const FieldVector<K,1> &c, + PriorityTag<2>, ADLTag) { + return Dune::isUnordered(b[0],c[0]); + } + } //MathOverloads + + /** @} end documentation */ } // end namespace diff --git a/dune/common/gcd.hh b/dune/common/gcd.hh index e7e603e6247149336a64e9d7f5b30a30bfa46bf4..34ea1af4bad444bcd610053b89f8587c7b4f6e6d 100644 --- a/dune/common/gcd.hh +++ b/dune/common/gcd.hh @@ -9,20 +9,20 @@ namespace Dune { -/** -* @brief Calculator of the greatest common divisor. -*/ -template<long a, long b> -struct [[deprecated("Will be removed after Dune 2.8. Use std::gcd from <numeric> instead!")]] Gcd -{ -/** -* @brief The greatest common divisior of a and b. */ -constexpr static long value = std::gcd(a,b); -}; + /** + * @brief Calculator of the greatest common divisor. + */ + template<long a, long b> + struct [[deprecated("Will be removed after Dune 2.8. Use std::gcd from <numeric> instead!")]] Gcd + { + /** + * @brief The greatest common divisior of a and b. */ + constexpr static long value = std::gcd(a,b); + }; -/** -* @} -*/ + /** + * @} + */ } #endif diff --git a/dune/common/genericiterator.hh b/dune/common/genericiterator.hh index 66c1a70e7dd69b2c6e912cd93170ee347be211c6..5f777f852c34e70385f2fbab5ff492e29d53960e 100644 --- a/dune/common/genericiterator.hh +++ b/dune/common/genericiterator.hh @@ -9,270 +9,270 @@ namespace Dune { -/*! \defgroup GenericIterator GenericIterator -\ingroup IteratorFacades - -\brief Generic Iterator class for writing stl conformant iterators -for any container class with operator[] - -Using this template class you can create an iterator and a const_iterator -for any container class. - -Imagine you have SimpleContainer and would like to have an iterator. -All you have to do is provide operator[], begin() and end() -(for const and for non-const). - -\code -template<class T> -class SimpleContainer{ -public: -typedef GenericIterator<SimpleContainer<T>,T> iterator; - -typedef GenericIterator<const SimpleContainer<T>,const T> const_iterator; - -SimpleContainer(){ -for(int i=0; i < 100; i++) -values_[i]=i; -} - -iterator begin(){ -return iterator(*this, 0); -} - -const_iterator begin() const{ -return const_iterator(*this, 0); -} - -iterator end(){ -return iterator(*this, 100); -} - -const_iterator end() const{ -return const_iterator(*this, 100); -} - -T& operator[](int i){ -return values_[i]; -} - -const T& operator[](int i) const{ -return values_[i]; -} -private: -T values_[100]; -}; -\endcode - -See dune/common/test/iteratorfacestest.hh for details or -Dune::QuadratureDefault in dune/quadrature/quadrature.hh -for a real example. -*/ - -/** -* @file -* @brief Implements a generic iterator class for writing stl conformant iterators. -* -* Using this generic iterator writing iterators for containers -* that implement operator[] is only a matter of seconds. -*/ - -/** -\brief Get the 'const' version of a reference to a mutable object - -Given a reference R=T& const_reference<R>::type gives you the typedef for const T& -*/ -template<class R> -struct const_reference -{ -typedef const R type; -}; - -template<class R> -struct const_reference<const R> -{ -typedef const R type; -}; - -template<class R> -struct const_reference<R&> -{ -typedef const R& type; -}; - -template<class R> -struct const_reference<const R&> -{ -typedef const R& type; -}; - -/** -\brief get the 'mutable' version of a reference to a const object - -given a const reference R=const T& mutable_reference<R>::type gives you the typedef for T& -*/ -template<class R> -struct mutable_reference -{ -typedef R type; -}; - -template<class R> -struct mutable_reference<const R> -{ -typedef R type; -}; - -template<class R> -struct mutable_reference<R&> -{ -typedef R& type; -}; - -template<class R> -struct mutable_reference<const R&> -{ -typedef R& type; -}; - -/** @addtogroup GenericIterator -* -* @{ -*/ - -/** -* @brief Generic class for stl-conforming iterators for container classes with operator[]. -* -* If template parameter C has a const qualifier we are a const iterator, otherwise we -* are a mutable iterator. -*/ -template<class C, class T, class R=T&, class D = std::ptrdiff_t, -template<class,class,class,class> class IteratorFacade=RandomAccessIteratorFacade> -class GenericIterator : -public IteratorFacade<GenericIterator<C,T,R,D,IteratorFacade>,T,R,D> -{ -friend class GenericIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type, D, IteratorFacade>; -friend class GenericIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type, D, IteratorFacade>; - -typedef GenericIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type, D, IteratorFacade> MutableIterator; -typedef GenericIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type, D, IteratorFacade> ConstIterator; - -public: - -/** -* @brief The type of container we are an iterator for. -* -* The container type must provide an operator[] method. -* -* If C has a const qualifier we are a const iterator, otherwise we -* are a mutable iterator. -*/ -typedef C Container; - -/** -* @brief The value type of the iterator. -* -* This is the return type when dereferencing the iterator. -*/ -typedef T Value; - -/** -* @brief The type of the difference between two positions. -*/ -typedef D DifferenceType; - -/** -* @brief The type of the reference to the values accessed. -*/ -typedef R Reference; - -// Constructors needed by the base iterators -GenericIterator() : container_(0), position_(0) -{} - -/** -* @brief Constructor -* @param cont Reference to the container we are an iterator for -* @param pos The position the iterator will be positioned to -* (e.g. 0 for an iterator returned by Container::begin() or -* the size of the container for an iterator returned by Container::end() -*/ -GenericIterator(Container& cont, DifferenceType pos) -: container_(&cont), position_(pos) -{} - -/** -* @brief Copy constructor -* -* This is somehow hard to understand, therefore play with the cases: -* 1. if we are mutable this is the only valid copy constructor, as the argument is a mutable iterator -* 2. if we are a const iterator the argument is a mutable iterator => This is the needed conversion to initialize a const iterator from a mutable one. -*/ -GenericIterator(const MutableIterator& other) : container_(other.container_), position_(other.position_) -{} - -/** -* @brief Copy constructor -* -* @warning Calling this method results in a compiler error, if this is a mutable iterator. -* -* This is somehow hard to understand, therefore play with the cases: -* 1. if we are mutable the arguments is a const iterator and therefore calling this method is mistake in the user's code and results in a (probably not understandable) compiler error -* 2. If we are a const iterator this is the default copy constructor as the argument is a const iterator too. -*/ -GenericIterator(const ConstIterator& other) : container_(other.container_), position_(other.position_) -{} - -// Methods needed by the forward iterator -bool equals(const MutableIterator & other) const -{ -return position_ == other.position_ && container_ == other.container_; -} - -bool equals(const ConstIterator & other) const -{ -return position_ == other.position_ && container_ == other.container_; -} - -Reference dereference() const { -return container_->operator[](position_); -} - -void increment(){ -++position_; -} - -// Additional function needed by BidirectionalIterator -void decrement(){ ---position_; -} - -// Additional function needed by RandomAccessIterator -Reference elementAt(DifferenceType i) const { -return container_->operator[](position_+i); -} - -void advance(DifferenceType n){ -position_=position_+n; -} - -DifferenceType distanceTo(const MutableIterator& other) const -{ -assert(other.container_==container_); -return other.position_ - position_; -} - -DifferenceType distanceTo(const ConstIterator& other) const -{ -assert(other.container_==container_); -return other.position_ - position_; -} - -private: -Container *container_; -DifferenceType position_; -}; - -/** @} */ + /*! \defgroup GenericIterator GenericIterator + \ingroup IteratorFacades + + \brief Generic Iterator class for writing stl conformant iterators + for any container class with operator[] + + Using this template class you can create an iterator and a const_iterator + for any container class. + + Imagine you have SimpleContainer and would like to have an iterator. + All you have to do is provide operator[], begin() and end() + (for const and for non-const). + + \code + template<class T> + class SimpleContainer{ + public: + typedef GenericIterator<SimpleContainer<T>,T> iterator; + + typedef GenericIterator<const SimpleContainer<T>,const T> const_iterator; + + SimpleContainer(){ + for(int i=0; i < 100; i++) + values_[i]=i; + } + + iterator begin(){ + return iterator(*this, 0); + } + + const_iterator begin() const{ + return const_iterator(*this, 0); + } + + iterator end(){ + return iterator(*this, 100); + } + + const_iterator end() const{ + return const_iterator(*this, 100); + } + + T& operator[](int i){ + return values_[i]; + } + + const T& operator[](int i) const{ + return values_[i]; + } + private: + T values_[100]; + }; + \endcode + + See dune/common/test/iteratorfacestest.hh for details or + Dune::QuadratureDefault in dune/quadrature/quadrature.hh + for a real example. + */ + + /** + * @file + * @brief Implements a generic iterator class for writing stl conformant iterators. + * + * Using this generic iterator writing iterators for containers + * that implement operator[] is only a matter of seconds. + */ + + /** + \brief Get the 'const' version of a reference to a mutable object + + Given a reference R=T& const_reference<R>::type gives you the typedef for const T& + */ + template<class R> + struct const_reference + { + typedef const R type; + }; + + template<class R> + struct const_reference<const R> + { + typedef const R type; + }; + + template<class R> + struct const_reference<R&> + { + typedef const R& type; + }; + + template<class R> + struct const_reference<const R&> + { + typedef const R& type; + }; + + /** + \brief get the 'mutable' version of a reference to a const object + + given a const reference R=const T& mutable_reference<R>::type gives you the typedef for T& + */ + template<class R> + struct mutable_reference + { + typedef R type; + }; + + template<class R> + struct mutable_reference<const R> + { + typedef R type; + }; + + template<class R> + struct mutable_reference<R&> + { + typedef R& type; + }; + + template<class R> + struct mutable_reference<const R&> + { + typedef R& type; + }; + + /** @addtogroup GenericIterator + * + * @{ + */ + + /** + * @brief Generic class for stl-conforming iterators for container classes with operator[]. + * + * If template parameter C has a const qualifier we are a const iterator, otherwise we + * are a mutable iterator. + */ + template<class C, class T, class R=T&, class D = std::ptrdiff_t, + template<class,class,class,class> class IteratorFacade=RandomAccessIteratorFacade> + class GenericIterator : + public IteratorFacade<GenericIterator<C,T,R,D,IteratorFacade>,T,R,D> + { + friend class GenericIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type, D, IteratorFacade>; + friend class GenericIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type, D, IteratorFacade>; + + typedef GenericIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type, typename mutable_reference<R>::type, D, IteratorFacade> MutableIterator; + typedef GenericIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type, typename const_reference<R>::type, D, IteratorFacade> ConstIterator; + + public: + + /** + * @brief The type of container we are an iterator for. + * + * The container type must provide an operator[] method. + * + * If C has a const qualifier we are a const iterator, otherwise we + * are a mutable iterator. + */ + typedef C Container; + + /** + * @brief The value type of the iterator. + * + * This is the return type when dereferencing the iterator. + */ + typedef T Value; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + // Constructors needed by the base iterators + GenericIterator() : container_(0), position_(0) + {} + + /** + * @brief Constructor + * @param cont Reference to the container we are an iterator for + * @param pos The position the iterator will be positioned to + * (e.g. 0 for an iterator returned by Container::begin() or + * the size of the container for an iterator returned by Container::end() + */ + GenericIterator(Container& cont, DifferenceType pos) + : container_(&cont), position_(pos) + {} + + /** + * @brief Copy constructor + * + * This is somehow hard to understand, therefore play with the cases: + * 1. if we are mutable this is the only valid copy constructor, as the argument is a mutable iterator + * 2. if we are a const iterator the argument is a mutable iterator => This is the needed conversion to initialize a const iterator from a mutable one. + */ + GenericIterator(const MutableIterator& other) : container_(other.container_), position_(other.position_) + {} + + /** + * @brief Copy constructor + * + * @warning Calling this method results in a compiler error, if this is a mutable iterator. + * + * This is somehow hard to understand, therefore play with the cases: + * 1. if we are mutable the arguments is a const iterator and therefore calling this method is mistake in the user's code and results in a (probably not understandable) compiler error + * 2. If we are a const iterator this is the default copy constructor as the argument is a const iterator too. + */ + GenericIterator(const ConstIterator& other) : container_(other.container_), position_(other.position_) + {} + + // Methods needed by the forward iterator + bool equals(const MutableIterator & other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + bool equals(const ConstIterator & other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + Reference dereference() const { + return container_->operator[](position_); + } + + void increment(){ + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement(){ + --position_; + } + + // Additional function needed by RandomAccessIterator + Reference elementAt(DifferenceType i) const { + return container_->operator[](position_+i); + } + + void advance(DifferenceType n){ + position_=position_+n; + } + + DifferenceType distanceTo(const MutableIterator& other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + + DifferenceType distanceTo(const ConstIterator& other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + + private: + Container *container_; + DifferenceType position_; + }; + + /** @} */ } // end namespace Dune diff --git a/dune/common/gmpfield.hh b/dune/common/gmpfield.hh index 4830350d4492bcea9bf6e78ee876fdbd84dc1ef3..d85bf1d952e66ce0834a85d85446e3b5a25528bc 100644 --- a/dune/common/gmpfield.hh +++ b/dune/common/gmpfield.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Wrapper for the GNU multiprecision (GMP) library -*/ + * \brief Wrapper for the GNU multiprecision (GMP) library + */ #include <iostream> #include <string> @@ -22,82 +22,82 @@ namespace Dune { -/** -* \ingroup Numbers -* \brief Number class for high precision floating point number using the GMP library mpf_class implementation -*/ -template< unsigned int precision > -class GMPField -: public mpf_class -{ -typedef mpf_class Base; - -public: -/** default constructor, initialize to zero */ -GMPField () -: Base(0,precision) -{} - -/** \brief initialize from a string -\note this is the only reliable way to initialize with higher precision values -*/ -GMPField ( const char* str ) -: Base(str,precision) -{} - -/** \brief initialize from a string -\note this is the only reliable way to initialize with higher precision values -*/ -GMPField ( const std::string& str ) -: Base(str,precision) -{} - -/** \brief initialize from a compatible scalar type -*/ -template< class T, -typename EnableIf = typename std::enable_if< -std::is_convertible<T, mpf_class>::value>::type -> -GMPField ( const T &v ) -: Base( v,precision ) -{} - -// type conversion operators -operator double () const -{ -return this->get_d(); -} - -}; - -template <unsigned int precision> -struct IsNumber<GMPField<precision>> -: public std::integral_constant<bool, true> { -}; - -template< unsigned int precision1, unsigned int precision2 > -struct PromotionTraits<GMPField<precision1>, GMPField<precision2>> -{ -typedef GMPField<(precision1 > precision2 ? precision1 : precision2)> PromotedType; -}; - -template< unsigned int precision > -struct PromotionTraits<GMPField<precision>,GMPField<precision>> -{ -typedef GMPField<precision> PromotedType; -}; - -template< unsigned int precision, class T > -struct PromotionTraits<GMPField<precision>, T> -{ -typedef GMPField<precision> PromotedType; -}; - -template< class T, unsigned int precision > -struct PromotionTraits<T, GMPField<precision>> -{ -typedef GMPField<precision> PromotedType; -}; + /** + * \ingroup Numbers + * \brief Number class for high precision floating point number using the GMP library mpf_class implementation + */ + template< unsigned int precision > + class GMPField + : public mpf_class + { + typedef mpf_class Base; + + public: + /** default constructor, initialize to zero */ + GMPField () + : Base(0,precision) + {} + + /** \brief initialize from a string + \note this is the only reliable way to initialize with higher precision values + */ + GMPField ( const char* str ) + : Base(str,precision) + {} + + /** \brief initialize from a string + \note this is the only reliable way to initialize with higher precision values + */ + GMPField ( const std::string& str ) + : Base(str,precision) + {} + + /** \brief initialize from a compatible scalar type + */ + template< class T, + typename EnableIf = typename std::enable_if< + std::is_convertible<T, mpf_class>::value>::type + > + GMPField ( const T &v ) + : Base( v,precision ) + {} + + // type conversion operators + operator double () const + { + return this->get_d(); + } + + }; + + template <unsigned int precision> + struct IsNumber<GMPField<precision>> + : public std::integral_constant<bool, true> { + }; + + template< unsigned int precision1, unsigned int precision2 > + struct PromotionTraits<GMPField<precision1>, GMPField<precision2>> + { + typedef GMPField<(precision1 > precision2 ? precision1 : precision2)> PromotedType; + }; + + template< unsigned int precision > + struct PromotionTraits<GMPField<precision>,GMPField<precision>> + { + typedef GMPField<precision> PromotedType; + }; + + template< unsigned int precision, class T > + struct PromotionTraits<GMPField<precision>, T> + { + typedef GMPField<precision> PromotedType; + }; + + template< class T, unsigned int precision > + struct PromotionTraits<T, GMPField<precision>> + { + typedef GMPField<precision> PromotedType; + }; } #endif // HAVE_GMP diff --git a/dune/common/hash.hh b/dune/common/hash.hh index 92eeaad91a3d493be844e9b4fb04603fa38e54fb..bbec1c1c94aa421298ff405e9b22f86ae9b60128 100644 --- a/dune/common/hash.hh +++ b/dune/common/hash.hh @@ -9,14 +9,14 @@ #include <dune/common/typetraits.hh> /** -* \file -* \brief Support for calculating hash values of objects. -* -* This file provides the functor Dune::hash to calculate hash values and -* some infrastructure to simplify extending Dune::hash for user-defined types, -* independent of the actual underlying implementation. -* -*/ + * \file + * \brief Support for calculating hash values of objects. + * + * This file provides the functor Dune::hash to calculate hash values and + * some infrastructure to simplify extending Dune::hash for user-defined types, + * independent of the actual underlying implementation. + * + */ @@ -28,91 +28,91 @@ namespace Dune { -//! Functor for hashing objects of type T. -/** -* The interface outlined below is compatible with std::hash, std::tr1::hash and -* boost::hash, so it is possible to use Dune::hash in associative containers from -* those libraries. -*/ -template<typename T> -struct hash -{ - -//! Calculates the hash of t. -std::size_t operator()(const T& t) const -{ -return hash(t); -} + //! Functor for hashing objects of type T. + /** + * The interface outlined below is compatible with std::hash, std::tr1::hash and + * boost::hash, so it is possible to use Dune::hash in associative containers from + * those libraries. + */ + template<typename T> + struct hash + { + + //! Calculates the hash of t. + std::size_t operator()(const T& t) const + { + return hash(t); + } -}; + }; } //! Defines the required struct specialization to make type hashable via `Dune::hash`. /** -* In order to calculate the hash, operator() of the generated specialization will -* return the result of an unqualified call to the global function `hash_value(const type&)`. -* As the call is not qualified, the function will be found using argument-dependent lookup, -* allowing implementors to conveniently place it inside the class body. -* -* Consider the following type: -* -* \code -* namespace ns { -* template<typename A, int i> -* class Foo -* { -* ... -* }; -* } -* \endcode -* -* In order to add support for `Dune::hash`, you need to extend the definition like this: -* -* \code -* namespace ns { -* template<typename A, int i> -* class Foo -* { -* ... -* // The keyword "friend" turns this into a global function that is a friend of Foo. -* inline friend std::size_t hash_value(const Foo& arg) -* { -* return ...; -* } -* }; -* } -* -* // Define hash struct specialization -* DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(typename A, int i),DUNE_HASH_TYPE(Foo<A,i>)) -* \endcode -* -* \warning -* As the specialization has to be placed in the original namespace of the -* `hash` struct (e.g. `std`), this macro *must* be called from the global namespace! -* -* \param template_args The template arguments required by the hash struct specialization, -* wrapped in a call to DUNE_HASH_TEMPLATE_ARGS. If this is a complete -* specialization, call DUNE_HASH_TEMPLATE_ARGS without arguments. -* \param type The exact type of the specialization, wrapped in a call to DUNE_HASH_TYPE. -*/ + * In order to calculate the hash, operator() of the generated specialization will + * return the result of an unqualified call to the global function `hash_value(const type&)`. + * As the call is not qualified, the function will be found using argument-dependent lookup, + * allowing implementors to conveniently place it inside the class body. + * + * Consider the following type: + * + * \code + * namespace ns { + * template<typename A, int i> + * class Foo + * { + * ... + * }; + * } + * \endcode + * + * In order to add support for `Dune::hash`, you need to extend the definition like this: + * + * \code + * namespace ns { + * template<typename A, int i> + * class Foo + * { + * ... + * // The keyword "friend" turns this into a global function that is a friend of Foo. + * inline friend std::size_t hash_value(const Foo& arg) + * { + * return ...; + * } + * }; + * } + * + * // Define hash struct specialization + * DUNE_DEFINE_HASH(DUNE_HASH_TEMPLATE_ARGS(typename A, int i),DUNE_HASH_TYPE(Foo<A,i>)) + * \endcode + * + * \warning + * As the specialization has to be placed in the original namespace of the + * `hash` struct (e.g. `std`), this macro *must* be called from the global namespace! + * + * \param template_args The template arguments required by the hash struct specialization, + * wrapped in a call to DUNE_HASH_TEMPLATE_ARGS. If this is a complete + * specialization, call DUNE_HASH_TEMPLATE_ARGS without arguments. + * \param type The exact type of the specialization, wrapped in a call to DUNE_HASH_TYPE. + */ #define DUNE_DEFINE_HASH(template_args,type) //! Wrapper macro for the template arguments in DUNE_DEFINE_HASH. /** -* This macro should always be used as a wrapper for the template arguments when calling DUNE_DEFINE_HASH. -* It works around some preprocessor limitations when the template arguments contain commas or the list -* is completely empty. -*/ + * This macro should always be used as a wrapper for the template arguments when calling DUNE_DEFINE_HASH. + * It works around some preprocessor limitations when the template arguments contain commas or the list + * is completely empty. + */ #define DUNE_HASH_TEMPLATE_ARGS(...) //! Wrapper macro for the type to be hashed in DUNE_DEFINE_HASH. /** -* This macro should always be used as a wrapper for the type of the specialization when calling -* DUNE_DEFINE_HASH. -* It works around some preprocessor limitations when the type contains commas. -*/ + * This macro should always be used as a wrapper for the type of the specialization when calling + * DUNE_DEFINE_HASH. + * It works around some preprocessor limitations when the type contains commas. + */ #define DUNE_HASH_TYPE(...) #else // DOXYGEN - hide all the ugly implementation @@ -126,7 +126,7 @@ return hash(t); // import std::hash into Dune namespace namespace Dune { -using std::hash; + using std::hash; } @@ -134,35 +134,35 @@ using std::hash; // This should not be called directly. Call DUNE_DEFINE_HASH // instead. #define DUNE_DEFINE_STD_HASH(template_args,type) \ -namespace std { \ -\ -template<template_args> \ -struct hash<type> \ -{ \ -\ -typedef type argument_type; \ -typedef std::size_t result_type; \ -\ -std::size_t operator()(const type& arg) const \ -{ \ -return hash_value(arg); \ -} \ -}; \ -\ -template<template_args> \ -struct hash<const type> \ -{ \ -\ -typedef type argument_type; \ -typedef std::size_t result_type; \ -\ -std::size_t operator()(const type& arg) const \ -{ \ -return hash_value(arg); \ -} \ -}; \ -\ -} \ + namespace std { \ + \ + template<template_args> \ + struct hash<type> \ + { \ + \ + typedef type argument_type; \ + typedef std::size_t result_type; \ + \ + std::size_t operator()(const type& arg) const \ + { \ + return hash_value(arg); \ + } \ + }; \ + \ + template<template_args> \ + struct hash<const type> \ + { \ + \ + typedef type argument_type; \ + typedef std::size_t result_type; \ + \ + std::size_t operator()(const type& arg) const \ + { \ + return hash_value(arg); \ + } \ + }; \ + \ + } \ // Wrapper macro for template arguments. // This is required because the template arguments can contain commas, @@ -186,7 +186,7 @@ return hash_value(arg); \ // Define specializations for all discovered hash implementations. #define DUNE_DEFINE_HASH(template_args,type) \ -DUNE_DEFINE_STD_HASH(DUNE_HASH_EXPAND_VA_ARGS template_args, DUNE_HASH_EXPAND_VA_ARGS type) \ + DUNE_DEFINE_STD_HASH(DUNE_HASH_EXPAND_VA_ARGS template_args, DUNE_HASH_EXPAND_VA_ARGS type) \ #endif // DOXYGEN @@ -199,151 +199,151 @@ DUNE_DEFINE_STD_HASH(DUNE_HASH_EXPAND_VA_ARGS template_args, DUNE_HASH_EXPAND_VA namespace Dune { -// The following functions are an implementation of the proposed hash extensions for -// the C++ standard by Peter Dimov -// (cf. http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf, issue 6.18). -// They are also contained in the boost::functional::hash library by Daniel James, but -// that implementation uses boost::hash internally, while we want to use Dune::hash. They -// are also considered for inclusion in TR2 (then based on std::hash, of course). + // The following functions are an implementation of the proposed hash extensions for + // the C++ standard by Peter Dimov + // (cf. http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf, issue 6.18). + // They are also contained in the boost::functional::hash library by Daniel James, but + // that implementation uses boost::hash internally, while we want to use Dune::hash. They + // are also considered for inclusion in TR2 (then based on std::hash, of course). #ifndef DOXYGEN -// helper struct for providing different hash combining algorithms dependent on -// the size of size_t. -// hash_combiner has to be specialized for the size (in bytes) of std::size_t. -// Specialized versions should provide a method -// -// template <typename typeof_size_t, typename T> -// void operator()(typeof_size_t& seed, const T& arg) const; -// -// that will be called by the interface function hash_combine() described further below. -// The redundant template parameter typeof_size_t is needed to avoid warnings for the -// unused 64-bit specialization on 32-bit systems. -// -// There is no default implementation! -template<int sizeof_size_t> -struct hash_combiner; - - -// hash combining for 64-bit platforms. -template<> -struct hash_combiner<8> -{ - -template<typename typeof_size_t, typename T> -void operator()(typeof_size_t& seed, const T& arg) const -{ -static_assert(sizeof(typeof_size_t)==8, "hash_combiner::operator() instantiated with nonmatching type and size"); - -// The following algorithm for combining two 64-bit hash values is inspired by a similar -// function in CityHash (http://cityhash.googlecode.com/svn-history/r2/trunk/src/city.h), -// which is in turn based on ideas from the MurmurHash library. The basic idea is easy to -// grasp, though: New information is XORed into the existing hash multiple times at different -// places (using shift operations), and the resulting pattern is spread over the complete -// range of available bits via multiplication with a "magic" constant. The constants used -// below (47 and 0x9ddfea08eb382d69ULL) are taken from the CityHash implementation. -// -// We opted not to use the mixing algorithm proposed in the C++ working group defect list at -// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf, p. 57f. because it -// has very bad hash distribution properties if you apply it to lists of very small numbers, -// an application that is frequent in PDELab's ordering framework. - -Dune::hash<T> hasher; -const typeof_size_t kMul = 0x9ddfea08eb382d69ULL; -typeof_size_t h = hasher(arg); -typeof_size_t a = (seed ^ h) * kMul; -a ^= (a >> 47); -typeof_size_t b = (h ^ a) * kMul; -b ^= (b >> 47); -b *= kMul; -seed = b; -} - -}; - - -// hash combining for 32-bit platforms. -template<> -struct hash_combiner<4> -{ - -template<typename typeof_size_t, typename T> -void operator()(typeof_size_t& seed, const T& arg) const -{ -static_assert(sizeof(typeof_size_t)==4, "hash_combiner::operator() instantiated with nonmatching type and size"); - -// The default algorithm above requires a 64-bit std::size_t. The following algorithm is a -// 32-bit compatible fallback, again inspired by CityHash and MurmurHash -// (http://cityhash.googlecode.com/svn-history/r2/trunk/src/city.cc). -// It uses 32-bit constants and relies on rotation instead of multiplication to spread the -// mixed bits as that is apparently more efficient on IA-32. The constants used below are again -// taken from CityHash, in particular from the file referenced above. - -Dune::hash<T> hasher; -const typeof_size_t c1 = 0xcc9e2d51; -const typeof_size_t c2 = 0x1b873593; -const typeof_size_t c3 = 0xe6546b64; -typeof_size_t h = hasher(arg); -typeof_size_t a = seed * c1; -a = (a >> 17) | (a << (32 - 17)); -a *= c2; -h ^= a; -h = (h >> 19) | (h << (32 - 19)); -seed = h * 5 + c3; -} - -}; + // helper struct for providing different hash combining algorithms dependent on + // the size of size_t. + // hash_combiner has to be specialized for the size (in bytes) of std::size_t. + // Specialized versions should provide a method + // + // template <typename typeof_size_t, typename T> + // void operator()(typeof_size_t& seed, const T& arg) const; + // + // that will be called by the interface function hash_combine() described further below. + // The redundant template parameter typeof_size_t is needed to avoid warnings for the + // unused 64-bit specialization on 32-bit systems. + // + // There is no default implementation! + template<int sizeof_size_t> + struct hash_combiner; + + + // hash combining for 64-bit platforms. + template<> + struct hash_combiner<8> + { + + template<typename typeof_size_t, typename T> + void operator()(typeof_size_t& seed, const T& arg) const + { + static_assert(sizeof(typeof_size_t)==8, "hash_combiner::operator() instantiated with nonmatching type and size"); + + // The following algorithm for combining two 64-bit hash values is inspired by a similar + // function in CityHash (http://cityhash.googlecode.com/svn-history/r2/trunk/src/city.h), + // which is in turn based on ideas from the MurmurHash library. The basic idea is easy to + // grasp, though: New information is XORed into the existing hash multiple times at different + // places (using shift operations), and the resulting pattern is spread over the complete + // range of available bits via multiplication with a "magic" constant. The constants used + // below (47 and 0x9ddfea08eb382d69ULL) are taken from the CityHash implementation. + // + // We opted not to use the mixing algorithm proposed in the C++ working group defect list at + // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1756.pdf, p. 57f. because it + // has very bad hash distribution properties if you apply it to lists of very small numbers, + // an application that is frequent in PDELab's ordering framework. + + Dune::hash<T> hasher; + const typeof_size_t kMul = 0x9ddfea08eb382d69ULL; + typeof_size_t h = hasher(arg); + typeof_size_t a = (seed ^ h) * kMul; + a ^= (a >> 47); + typeof_size_t b = (h ^ a) * kMul; + b ^= (b >> 47); + b *= kMul; + seed = b; + } + + }; + + + // hash combining for 32-bit platforms. + template<> + struct hash_combiner<4> + { + + template<typename typeof_size_t, typename T> + void operator()(typeof_size_t& seed, const T& arg) const + { + static_assert(sizeof(typeof_size_t)==4, "hash_combiner::operator() instantiated with nonmatching type and size"); + + // The default algorithm above requires a 64-bit std::size_t. The following algorithm is a + // 32-bit compatible fallback, again inspired by CityHash and MurmurHash + // (http://cityhash.googlecode.com/svn-history/r2/trunk/src/city.cc). + // It uses 32-bit constants and relies on rotation instead of multiplication to spread the + // mixed bits as that is apparently more efficient on IA-32. The constants used below are again + // taken from CityHash, in particular from the file referenced above. + + Dune::hash<T> hasher; + const typeof_size_t c1 = 0xcc9e2d51; + const typeof_size_t c2 = 0x1b873593; + const typeof_size_t c3 = 0xe6546b64; + typeof_size_t h = hasher(arg); + typeof_size_t a = seed * c1; + a = (a >> 17) | (a << (32 - 17)); + a *= c2; + h ^= a; + h = (h >> 19) | (h << (32 - 19)); + seed = h * 5 + c3; + } + + }; #endif // DOXYGEN -//! Calculates the hash value of arg and combines it in-place with seed. -/** -* -* \param seed The hash value that will be combined with the hash of arg. -* \param arg The object for which to calculate a hash value and combine it with seed. -*/ -template<typename T> -inline void hash_combine(std::size_t& seed, const T& arg) -{ -hash_combiner<sizeof(std::size_t)>()(seed,arg); -} - -//! Hashes all elements in the range [first,last) and returns the combined hash. -/** -* -* \param first Iterator pointing to the first object to hash. -* \param last Iterator pointing one past the last object to hash. - -* \returns The result of hashing all objects in the range and combining them -* using hash_combine() in sequential fashion, starting with seed 0. -*/ -template<typename It> -inline std::size_t hash_range(It first, It last) -{ -std::size_t seed = 0; -for (; first != last; ++first) -{ -hash_combine(seed,*first); -} -return seed; -} - -//! Hashes all elements in the range [first,last) and combines the hashes in-place with seed. -/** -* -* \param seed Start value that will be combined with the hash values of all objects in -* the range using hash_combine() in sequential fashion. -* \param first Iterator pointing to the first ojbect to hash. -* \param last Iterator pointing one past the last object to hash. -*/ -template<typename It> -inline void hash_range(std::size_t& seed, It first, It last) -{ -for (; first != last; ++first) -{ -hash_combine(seed,*first); -} -} + //! Calculates the hash value of arg and combines it in-place with seed. + /** + * + * \param seed The hash value that will be combined with the hash of arg. + * \param arg The object for which to calculate a hash value and combine it with seed. + */ + template<typename T> + inline void hash_combine(std::size_t& seed, const T& arg) + { + hash_combiner<sizeof(std::size_t)>()(seed,arg); + } + + //! Hashes all elements in the range [first,last) and returns the combined hash. + /** + * + * \param first Iterator pointing to the first object to hash. + * \param last Iterator pointing one past the last object to hash. + + * \returns The result of hashing all objects in the range and combining them + * using hash_combine() in sequential fashion, starting with seed 0. + */ + template<typename It> + inline std::size_t hash_range(It first, It last) + { + std::size_t seed = 0; + for (; first != last; ++first) + { + hash_combine(seed,*first); + } + return seed; + } + + //! Hashes all elements in the range [first,last) and combines the hashes in-place with seed. + /** + * + * \param seed Start value that will be combined with the hash values of all objects in + * the range using hash_combine() in sequential fashion. + * \param first Iterator pointing to the first ojbect to hash. + * \param last Iterator pointing one past the last object to hash. + */ + template<typename It> + inline void hash_range(std::size_t& seed, It first, It last) + { + for (; first != last; ++first) + { + hash_combine(seed,*first); + } + } } // end namespace Dune diff --git a/dune/common/hybridutilities.hh b/dune/common/hybridutilities.hh index b2c67e6a549feece9e028444e814c3628a70d1e3..1e4d09a9ef84a2cb9088ce6c6a4885b6aa87b62b 100644 --- a/dune/common/hybridutilities.hh +++ b/dune/common/hybridutilities.hh @@ -22,471 +22,471 @@ namespace Hybrid { namespace Impl { -// Try if tuple_size is implemented for class -template<class T, int i> -constexpr auto size(const Dune::FieldVector<T, i>&, const PriorityTag<5>&) --> decltype(std::integral_constant<std::size_t,i>()) -{ -return {}; -} - -// Try if tuple_size is implemented for class -template<class T> -constexpr auto size(const T&, const PriorityTag<3>&) --> decltype(std::integral_constant<std::size_t,std::tuple_size<T>::value>()) -{ -return {}; -} - -// Try if there's a static constexpr size() -template<class T> -constexpr auto size(const T&, const PriorityTag<1>&) --> decltype(std::integral_constant<std::size_t,T::size()>()) -{ -return {}; -} - -// As a last resort try if there's a static constexpr size() -template<class T> -constexpr auto size(const T& t, const PriorityTag<0>&) -{ -return t.size(); -} + // Try if tuple_size is implemented for class + template<class T, int i> + constexpr auto size(const Dune::FieldVector<T, i>&, const PriorityTag<5>&) + -> decltype(std::integral_constant<std::size_t,i>()) + { + return {}; + } + + // Try if tuple_size is implemented for class + template<class T> + constexpr auto size(const T&, const PriorityTag<3>&) + -> decltype(std::integral_constant<std::size_t,std::tuple_size<T>::value>()) + { + return {}; + } + + // Try if there's a static constexpr size() + template<class T> + constexpr auto size(const T&, const PriorityTag<1>&) + -> decltype(std::integral_constant<std::size_t,T::size()>()) + { + return {}; + } + + // As a last resort try if there's a static constexpr size() + template<class T> + constexpr auto size(const T& t, const PriorityTag<0>&) + { + return t.size(); + } } // namespace Impl /** -* \brief Size query -* -* \ingroup HybridUtilities -* -* \tparam T Type of container whose size is queried -* -* \param t Container whose size is queried -* -* \return Size of t -* -* If the size of t is known at compile type the size is -* returned as std::integral_constant<std::size_t, size>. -* Otherwise the result of t.size() is returned. -* -* Supported types for deriving the size at compile time are: -* * instances of std::integer_sequence -* * all types std::tuple_size is implemented for -* * all typed that have a static method ::size() -* * instances of Dune::FieldVector -*/ + * \brief Size query + * + * \ingroup HybridUtilities + * + * \tparam T Type of container whose size is queried + * + * \param t Container whose size is queried + * + * \return Size of t + * + * If the size of t is known at compile type the size is + * returned as std::integral_constant<std::size_t, size>. + * Otherwise the result of t.size() is returned. + * + * Supported types for deriving the size at compile time are: + * * instances of std::integer_sequence + * * all types std::tuple_size is implemented for + * * all typed that have a static method ::size() + * * instances of Dune::FieldVector + */ template<class T> constexpr auto size(const T& t) { -return Impl::size(t, PriorityTag<42>()); + return Impl::size(t, PriorityTag<42>()); } namespace Impl { -template<class Container, class Index, -std::enable_if_t<IsTuple<std::decay_t<Container>>::value, int> = 0> -constexpr decltype(auto) elementAt(Container&& c, Index&&, PriorityTag<2>) -{ -return std::get<std::decay_t<Index>::value>(c); -} - -template<class T, T... t, class Index> -constexpr decltype(auto) elementAt(std::integer_sequence<T, t...> c, Index, PriorityTag<1>) -{ -return Dune::integerSequenceEntry(c, std::integral_constant<std::size_t, Index::value>()); -} - -template<class Container, class Index> -constexpr decltype(auto) elementAt(Container&& c, Index&& i, PriorityTag<0>) -{ -return c[i]; -} + template<class Container, class Index, + std::enable_if_t<IsTuple<std::decay_t<Container>>::value, int> = 0> + constexpr decltype(auto) elementAt(Container&& c, Index&&, PriorityTag<2>) + { + return std::get<std::decay_t<Index>::value>(c); + } + + template<class T, T... t, class Index> + constexpr decltype(auto) elementAt(std::integer_sequence<T, t...> c, Index, PriorityTag<1>) + { + return Dune::integerSequenceEntry(c, std::integral_constant<std::size_t, Index::value>()); + } + + template<class Container, class Index> + constexpr decltype(auto) elementAt(Container&& c, Index&& i, PriorityTag<0>) + { + return c[i]; + } } // namespace Impl /** -* \brief Get element at given position from container -* -* \ingroup HybridUtilities -* -* \tparam Container Type of given container -* \tparam Index Type of index -* -* \param c Given container -* \param i Index of element to obtain -* -* \return The element at position i, i.e. c[i] -* -* If this returns the i-th entry of c. It supports the following -* containers -* * Containers providing dynamic access via operator[] -* * Heterogeneous containers providing access via operator[](integral_constant<...>) -* * std::tuple<...> -* * std::integer_sequence -*/ + * \brief Get element at given position from container + * + * \ingroup HybridUtilities + * + * \tparam Container Type of given container + * \tparam Index Type of index + * + * \param c Given container + * \param i Index of element to obtain + * + * \return The element at position i, i.e. c[i] + * + * If this returns the i-th entry of c. It supports the following + * containers + * * Containers providing dynamic access via operator[] + * * Heterogeneous containers providing access via operator[](integral_constant<...>) + * * std::tuple<...> + * * std::integer_sequence + */ template<class Container, class Index> constexpr decltype(auto) elementAt(Container&& c, Index&& i) { -return Impl::elementAt(std::forward<Container>(c), std::forward<Index>(i), PriorityTag<42>()); + return Impl::elementAt(std::forward<Container>(c), std::forward<Index>(i), PriorityTag<42>()); } namespace Impl { -template<class Begin, class End, -std::enable_if_t<IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value, int> = 0> -constexpr auto integralRange(const Begin& /*begin*/, const End& /*end*/, const PriorityTag<1>&) -{ -static_assert(Begin::value <= End::value, "You cannot create an integralRange where end<begin"); -return Dune::StaticIntegralRange<std::size_t, End::value, Begin::value>(); -} - -// This should be constexpr but gcc-4.9 does not support -// the relaxed constexpr requirements. Hence for being -// constexpr the function body can only contain a return -// statement and no assertion before this. -template<class Begin, class End> -constexpr auto integralRange(const Begin& begin, const End& end, const PriorityTag<0>&) -{ -return DUNE_ASSERT_AND_RETURN(begin<=end, Dune::IntegralRange<End>(begin, end)); -} + template<class Begin, class End, + std::enable_if_t<IsIntegralConstant<Begin>::value and IsIntegralConstant<End>::value, int> = 0> + constexpr auto integralRange(const Begin& /*begin*/, const End& /*end*/, const PriorityTag<1>&) + { + static_assert(Begin::value <= End::value, "You cannot create an integralRange where end<begin"); + return Dune::StaticIntegralRange<std::size_t, End::value, Begin::value>(); + } + + // This should be constexpr but gcc-4.9 does not support + // the relaxed constexpr requirements. Hence for being + // constexpr the function body can only contain a return + // statement and no assertion before this. + template<class Begin, class End> + constexpr auto integralRange(const Begin& begin, const End& end, const PriorityTag<0>&) + { + return DUNE_ASSERT_AND_RETURN(begin<=end, Dune::IntegralRange<End>(begin, end)); + } } // namespace Impl /** -* \brief Create an integral range -* -* \ingroup HybridUtilities -* -* \tparam Begin Type of begin entry of the range -* \tparam End Type of end entry of the range -* -* \param begin First entry of the range -* \param end One past the last entry of the range -* -* \returns An object encoding the given range -* -* If Begin and End are both instances of type -* std::integral_constant, the returned range -* encodes begin and end statically. -*/ + * \brief Create an integral range + * + * \ingroup HybridUtilities + * + * \tparam Begin Type of begin entry of the range + * \tparam End Type of end entry of the range + * + * \param begin First entry of the range + * \param end One past the last entry of the range + * + * \returns An object encoding the given range + * + * If Begin and End are both instances of type + * std::integral_constant, the returned range + * encodes begin and end statically. + */ template<class Begin, class End> constexpr auto integralRange(const Begin& begin, const End& end) { -return Impl::integralRange(begin, end, PriorityTag<42>()); + return Impl::integralRange(begin, end, PriorityTag<42>()); } /** -* \brief Create an integral range starting from 0 -* -* \ingroup HybridUtilities -* -* \tparam End Type of end entry of the range -* -* \param end One past the last entry of the range -* -* \returns An object encoding the given range -* -* This is a short cut for integralRange(_0, end). -*/ + * \brief Create an integral range starting from 0 + * + * \ingroup HybridUtilities + * + * \tparam End Type of end entry of the range + * + * \param end One past the last entry of the range + * + * \returns An object encoding the given range + * + * This is a short cut for integralRange(_0, end). + */ template<class End> constexpr auto integralRange(const End& end) { -return Impl::integralRange(Dune::Indices::_0, end, PriorityTag<42>()); + return Impl::integralRange(Dune::Indices::_0, end, PriorityTag<42>()); } namespace Impl { -template<class T> -constexpr void evaluateFoldExpression(std::initializer_list<T>&&) -{} - -template<class Range, class F, class Index, Index... i> -constexpr void forEachIndex(Range&& range, F&& f, std::integer_sequence<Index, i...>) -{ -evaluateFoldExpression<int>({(f(Hybrid::elementAt(range, std::integral_constant<Index,i>())), 0)...}); -} - -template<class F, class Index, Index... i> -constexpr void forEach(std::integer_sequence<Index, i...> /*range*/, F&& f, PriorityTag<2>) -{ -evaluateFoldExpression<int>({(f(std::integral_constant<Index,i>()), 0)...}); -} - - -template<class Range, class F, -std::enable_if_t<IsIntegralConstant<decltype(Hybrid::size(std::declval<Range>()))>::value, int> = 0> -constexpr void forEach(Range&& range, F&& f, PriorityTag<1>) -{ -auto size = Hybrid::size(range); -auto indices = std::make_index_sequence<size>(); -(forEachIndex)(std::forward<Range>(range), std::forward<F>(f), indices); -} - -template<class Range, class F> -constexpr void forEach(Range&& range, F&& f, PriorityTag<0>) -{ -for(auto&& e : range) -f(e); -} + template<class T> + constexpr void evaluateFoldExpression(std::initializer_list<T>&&) + {} + + template<class Range, class F, class Index, Index... i> + constexpr void forEachIndex(Range&& range, F&& f, std::integer_sequence<Index, i...>) + { + evaluateFoldExpression<int>({(f(Hybrid::elementAt(range, std::integral_constant<Index,i>())), 0)...}); + } + + template<class F, class Index, Index... i> + constexpr void forEach(std::integer_sequence<Index, i...> /*range*/, F&& f, PriorityTag<2>) + { + evaluateFoldExpression<int>({(f(std::integral_constant<Index,i>()), 0)...}); + } + + + template<class Range, class F, + std::enable_if_t<IsIntegralConstant<decltype(Hybrid::size(std::declval<Range>()))>::value, int> = 0> + constexpr void forEach(Range&& range, F&& f, PriorityTag<1>) + { + auto size = Hybrid::size(range); + auto indices = std::make_index_sequence<size>(); + (forEachIndex)(std::forward<Range>(range), std::forward<F>(f), indices); + } + + template<class Range, class F> + constexpr void forEach(Range&& range, F&& f, PriorityTag<0>) + { + for(auto&& e : range) + f(e); + } } // namespace Impl /** -* \brief Range based for loop -* -* \ingroup HybridUtilities -* -* \tparam Range Type of given range -* \tparam F Type of given predicate -* -* \param range The range to loop over -* \param f A predicate that will be called with each entry of the range -* -* This supports looping over the following ranges -* * ranges obtained from integralRange() -* * all ranges that provide Hybrid::size() and Hybrid::elementAt() -* -* This especially included instances of std::integer_sequence, -* std::tuple, Dune::TupleVector, and Dune::MultiTypeBlockVector. -*/ + * \brief Range based for loop + * + * \ingroup HybridUtilities + * + * \tparam Range Type of given range + * \tparam F Type of given predicate + * + * \param range The range to loop over + * \param f A predicate that will be called with each entry of the range + * + * This supports looping over the following ranges + * * ranges obtained from integralRange() + * * all ranges that provide Hybrid::size() and Hybrid::elementAt() + * + * This especially included instances of std::integer_sequence, + * std::tuple, Dune::TupleVector, and Dune::MultiTypeBlockVector. + */ template<class Range, class F> constexpr void forEach(Range&& range, F&& f) { -Impl::forEach(std::forward<Range>(range), std::forward<F>(f), PriorityTag<42>()); + Impl::forEach(std::forward<Range>(range), std::forward<F>(f), PriorityTag<42>()); } /** -* \brief Accumulate values -* -* \ingroup HybridUtilities -* -* \tparam Range Type of given range -* \tparam T Type of accumulated value -* \tparam F Type of binary accumulation operator -* -* \param range The range of values to accumulate -* \param value Initial value for accumulation -* \param f Binary operator for accumulation -* -* This supports looping over the same ranges as Hybrid::forEach -*/ + * \brief Accumulate values + * + * \ingroup HybridUtilities + * + * \tparam Range Type of given range + * \tparam T Type of accumulated value + * \tparam F Type of binary accumulation operator + * + * \param range The range of values to accumulate + * \param value Initial value for accumulation + * \param f Binary operator for accumulation + * + * This supports looping over the same ranges as Hybrid::forEach + */ template<class Range, class T, class F> constexpr T accumulate(Range&& range, T value, F&& f) { -forEach(std::forward<Range>(range), [&](auto&& entry) { -value = f(value, entry); -}); -return value; + forEach(std::forward<Range>(range), [&](auto&& entry) { + value = f(value, entry); + }); + return value; } namespace Impl { -struct Id { -template<class T> -constexpr T operator()(T&& x) const { -return std::forward<T>(x); -} -}; - -template<class IfFunc, class ElseFunc> -constexpr decltype(auto) ifElse(std::true_type, IfFunc&& ifFunc, ElseFunc&& /*elseFunc*/) -{ -return ifFunc(Id{}); -} - -template<class IfFunc, class ElseFunc> -constexpr decltype(auto) ifElse(std::false_type, IfFunc&& /*ifFunc*/, ElseFunc&& elseFunc) -{ -return elseFunc(Id{}); -} - -template<class IfFunc, class ElseFunc> -decltype(auto) ifElse(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc) -{ -if (condition) -return ifFunc(Id{}); -else -return elseFunc(Id{}); -} + struct Id { + template<class T> + constexpr T operator()(T&& x) const { + return std::forward<T>(x); + } + }; + + template<class IfFunc, class ElseFunc> + constexpr decltype(auto) ifElse(std::true_type, IfFunc&& ifFunc, ElseFunc&& /*elseFunc*/) + { + return ifFunc(Id{}); + } + + template<class IfFunc, class ElseFunc> + constexpr decltype(auto) ifElse(std::false_type, IfFunc&& /*ifFunc*/, ElseFunc&& elseFunc) + { + return elseFunc(Id{}); + } + + template<class IfFunc, class ElseFunc> + decltype(auto) ifElse(const bool& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc) + { + if (condition) + return ifFunc(Id{}); + else + return elseFunc(Id{}); + } } // namespace Impl /** -* \brief A conditional expression -* -* \ingroup HybridUtilities -* -* This will call either ifFunc or elseFunc depending -* on the condition. In any case a single argument -* will be passed to the called function. This will always -* be the identity function. Passing an expression through -* this function will lead to lazy evaluation. This way both -* 'branches' can contain expressions that are only valid -* within this branch if the condition is a std::integral_constant<bool,*>. -* -* In order to do this, the passed functors must have a single -* argument of type auto. -* -* Due to the lazy evaluation mechanism and support for -* std::integral_constant<bool,*> this allows to emulate -* a static if statement. -*/ + * \brief A conditional expression + * + * \ingroup HybridUtilities + * + * This will call either ifFunc or elseFunc depending + * on the condition. In any case a single argument + * will be passed to the called function. This will always + * be the identity function. Passing an expression through + * this function will lead to lazy evaluation. This way both + * 'branches' can contain expressions that are only valid + * within this branch if the condition is a std::integral_constant<bool,*>. + * + * In order to do this, the passed functors must have a single + * argument of type auto. + * + * Due to the lazy evaluation mechanism and support for + * std::integral_constant<bool,*> this allows to emulate + * a static if statement. + */ template<class Condition, class IfFunc, class ElseFunc> decltype(auto) ifElse(const Condition& condition, IfFunc&& ifFunc, ElseFunc&& elseFunc) { -return Impl::ifElse(condition, std::forward<IfFunc>(ifFunc), std::forward<ElseFunc>(elseFunc)); + return Impl::ifElse(condition, std::forward<IfFunc>(ifFunc), std::forward<ElseFunc>(elseFunc)); } /** -* \brief A conditional expression -* -* \ingroup HybridUtilities -* -* This provides an ifElse conditional with empty else clause. -*/ + * \brief A conditional expression + * + * \ingroup HybridUtilities + * + * This provides an ifElse conditional with empty else clause. + */ template<class Condition, class IfFunc> void ifElse(const Condition& condition, IfFunc&& ifFunc) { -ifElse(condition, std::forward<IfFunc>(ifFunc), [](auto&& i) { DUNE_UNUSED_PARAMETER(i); }); + ifElse(condition, std::forward<IfFunc>(ifFunc), [](auto&& i) { DUNE_UNUSED_PARAMETER(i); }); } namespace Impl { -template<class T1, class T2> -constexpr auto equals(const T1& /*t1*/, const T2& /*t2*/, PriorityTag<1>) -> decltype(T1::value, T2::value, std::integral_constant<bool,T1::value == T2::value>()) -{ return {}; } + template<class T1, class T2> + constexpr auto equals(const T1& /*t1*/, const T2& /*t2*/, PriorityTag<1>) -> decltype(T1::value, T2::value, std::integral_constant<bool,T1::value == T2::value>()) + { return {}; } -template<class T1, class T2> -constexpr auto equals(const T1& t1, const T2& t2, PriorityTag<0>) -{ -return t1==t2; -} + template<class T1, class T2> + constexpr auto equals(const T1& t1, const T2& t2, PriorityTag<0>) + { + return t1==t2; + } } // namespace Impl /** -* \brief Equality comparison -* -* \ingroup HybridUtilities -* -* If both types have a static member value, the result of comparing -* these is returned as std::integral_constant<bool, *>. Otherwise -* the result of a runtime comparison of t1 and t2 is directly returned. -*/ + * \brief Equality comparison + * + * \ingroup HybridUtilities + * + * If both types have a static member value, the result of comparing + * these is returned as std::integral_constant<bool, *>. Otherwise + * the result of a runtime comparison of t1 and t2 is directly returned. + */ template<class T1, class T2> constexpr auto equals(T1&& t1, T2&& t2) { -return Impl::equals(std::forward<T1>(t1), std::forward<T2>(t2), PriorityTag<1>()); + return Impl::equals(std::forward<T1>(t1), std::forward<T2>(t2), PriorityTag<1>()); } namespace Impl { -template<class Result, class T, class Value, class Branches, class ElseBranch> -constexpr Result switchCases(std::integer_sequence<T>, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch) -{ -return elseBranch(); -} - -template<class Result, class T, T t0, T... tt, class Value, class Branches, class ElseBranch> -constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const Value& value, Branches&& branches, ElseBranch&& elseBranch) -{ -return ifElse( -Hybrid::equals(std::integral_constant<T, t0>(), value), -[&](auto id) -> decltype(auto) { -return id(branches)(std::integral_constant<T, t0>()); -}, [&](auto id) -> decltype(auto) { -return Impl::switchCases<Result>(id(std::integer_sequence<T, tt...>()), value, branches, elseBranch); -}); -} + template<class Result, class T, class Value, class Branches, class ElseBranch> + constexpr Result switchCases(std::integer_sequence<T>, const Value& /*value*/, Branches&& /*branches*/, ElseBranch&& elseBranch) + { + return elseBranch(); + } + + template<class Result, class T, T t0, T... tt, class Value, class Branches, class ElseBranch> + constexpr Result switchCases(std::integer_sequence<T, t0, tt...>, const Value& value, Branches&& branches, ElseBranch&& elseBranch) + { + return ifElse( + Hybrid::equals(std::integral_constant<T, t0>(), value), + [&](auto id) -> decltype(auto) { + return id(branches)(std::integral_constant<T, t0>()); + }, [&](auto id) -> decltype(auto) { + return Impl::switchCases<Result>(id(std::integer_sequence<T, tt...>()), value, branches, elseBranch); + }); + } } // namespace Impl /** -* \brief Switch statement -* -* \ingroup HybridUtilities -* -* \tparam Cases Type of case range -* \tparam Value Type of value to check against the cases -* \tparam Branches Type of branch function -* \tparam ElseBranch Type of branch function -* -* \param cases A range of cases to check for -* \param value The value to check against the cases -* \param branches A callback that will be executed with matching entry from case list -* \param elseBranch A callback that will be executed if no other entry matches -* -* Value is checked against all entries of the given range. -* If one matches, then branches is executed with the matching -* value as single argument. If the range is an std::integer_sequence, -* the value is passed as std::integral_constant. -* If non of the entries matches, then elseBranch is executed -* without any argument. -* -* Notice that this short circuits, e.g., if one case matches, -* the others are no longer evaluated. -* -* The return value will be deduced from the else branch. -*/ + * \brief Switch statement + * + * \ingroup HybridUtilities + * + * \tparam Cases Type of case range + * \tparam Value Type of value to check against the cases + * \tparam Branches Type of branch function + * \tparam ElseBranch Type of branch function + * + * \param cases A range of cases to check for + * \param value The value to check against the cases + * \param branches A callback that will be executed with matching entry from case list + * \param elseBranch A callback that will be executed if no other entry matches + * + * Value is checked against all entries of the given range. + * If one matches, then branches is executed with the matching + * value as single argument. If the range is an std::integer_sequence, + * the value is passed as std::integral_constant. + * If non of the entries matches, then elseBranch is executed + * without any argument. + * + * Notice that this short circuits, e.g., if one case matches, + * the others are no longer evaluated. + * + * The return value will be deduced from the else branch. + */ template<class Cases, class Value, class Branches, class ElseBranch> constexpr decltype(auto) switchCases(const Cases& cases, const Value& value, Branches&& branches, ElseBranch&& elseBranch) { -return Impl::switchCases<decltype(elseBranch())>(cases, value, std::forward<Branches>(branches), std::forward<ElseBranch>(elseBranch)); + return Impl::switchCases<decltype(elseBranch())>(cases, value, std::forward<Branches>(branches), std::forward<ElseBranch>(elseBranch)); } /** -* \brief Switch statement -* -* \ingroup HybridUtilities -* -* \tparam Cases Type of case range -* \tparam Value Type of value to check against the cases -* \tparam Branches Type of branch function -* -* \param cases A range of cases to check for -* \param value The value to check against the cases -* \param branches A callback that will be executed with matching entry from case list -* -* Value is checked against all entries of the given range. -* If one matches, then branches is executed with the matching -* value as single argument. If the range is an std::integer_sequence, -* the value is passed as std::integral_constant. -* If non of the entries matches, then elseBranch is executed -* without any argument. -*/ + * \brief Switch statement + * + * \ingroup HybridUtilities + * + * \tparam Cases Type of case range + * \tparam Value Type of value to check against the cases + * \tparam Branches Type of branch function + * + * \param cases A range of cases to check for + * \param value The value to check against the cases + * \param branches A callback that will be executed with matching entry from case list + * + * Value is checked against all entries of the given range. + * If one matches, then branches is executed with the matching + * value as single argument. If the range is an std::integer_sequence, + * the value is passed as std::integral_constant. + * If non of the entries matches, then elseBranch is executed + * without any argument. + */ template<class Cases, class Value, class Branches> constexpr void switchCases(const Cases& cases, const Value& value, Branches&& branches) { -Impl::switchCases<void>(cases, value, std::forward<Branches>(branches), []() {}); + Impl::switchCases<void>(cases, value, std::forward<Branches>(branches), []() {}); } diff --git a/dune/common/indent.hh b/dune/common/indent.hh index 830371452c13fbfc11a572716fa8933fbd2f9c87..b7aeb9979d3d3e21f3e4bacd1d2cb80506da3b1d 100644 --- a/dune/common/indent.hh +++ b/dune/common/indent.hh @@ -8,108 +8,108 @@ #include <string> namespace Dune { -/** @addtogroup Common -* -* @{ -*/ -/** -* @file -* @brief Utility class for handling nested indentation in output. -* @author Jö Fahlke -*/ -//! Utility class for handling nested indentation in output. -/** -* An indentation object hast a string basic_indent and an indentation -* level. When it is put into a std::ostream using << it will print its -* basic_indent as many times as its indentation level. By default the -* basic_indent will be two spaces and the indentation level will be 0. -* -* An Indent object may also have a reference to a parent Indent object. If -* it has, that object it put into the stream with the << operator before -* the indentation of this object is put into the stream. This effectively -* chains Indent objects together. -* -* You can use the ++ operator to raise and the -- operator to lower the -* indentation by one level. -* -* You can use the + operator with a numeric second argument morelevel to -* create a copy of the Indent object with the indentation level increased -* morelevel times. This is mainly useful to pass indent+1 to a function, -* where indent is an indentation object. -* -* You can use the + operator with a string second argument newindent to -* create a new Indent object with this object as parent, a basic_indent of -* newindent, and an indentation level of one. This is mainly useful to -* pass indent+"> " to a function, where "> " is a possibly different -* indentation string then the one used by indent indentation object. -* -* \note The idea is for functions receive indentation objects as call by -* value parameters. This way, the indentation object of the caller -* will not be modified by the function and the function can simply -* return at anytime without having to clean up. -*/ -class Indent -{ -const Indent* parent; -std::string basic_indent; -unsigned level; + /** @addtogroup Common + * + * @{ + */ + /** + * @file + * @brief Utility class for handling nested indentation in output. + * @author Jö Fahlke + */ + //! Utility class for handling nested indentation in output. + /** + * An indentation object hast a string basic_indent and an indentation + * level. When it is put into a std::ostream using << it will print its + * basic_indent as many times as its indentation level. By default the + * basic_indent will be two spaces and the indentation level will be 0. + * + * An Indent object may also have a reference to a parent Indent object. If + * it has, that object it put into the stream with the << operator before + * the indentation of this object is put into the stream. This effectively + * chains Indent objects together. + * + * You can use the ++ operator to raise and the -- operator to lower the + * indentation by one level. + * + * You can use the + operator with a numeric second argument morelevel to + * create a copy of the Indent object with the indentation level increased + * morelevel times. This is mainly useful to pass indent+1 to a function, + * where indent is an indentation object. + * + * You can use the + operator with a string second argument newindent to + * create a new Indent object with this object as parent, a basic_indent of + * newindent, and an indentation level of one. This is mainly useful to + * pass indent+"> " to a function, where "> " is a possibly different + * indentation string then the one used by indent indentation object. + * + * \note The idea is for functions receive indentation objects as call by + * value parameters. This way, the indentation object of the caller + * will not be modified by the function and the function can simply + * return at anytime without having to clean up. + */ + class Indent + { + const Indent* parent; + std::string basic_indent; + unsigned level; -public: -//! setup without parent -/** -* \note Initial indentation level is 0 by default for this constructor. -*/ -inline Indent(const std::string& basic_indent_ = " ", unsigned level_ = 0) -: parent(0), basic_indent(basic_indent_), level(level_) -{ } + public: + //! setup without parent + /** + * \note Initial indentation level is 0 by default for this constructor. + */ + inline Indent(const std::string& basic_indent_ = " ", unsigned level_ = 0) + : parent(0), basic_indent(basic_indent_), level(level_) + { } -//! setup without parent and basic_indentation of two spaces -inline Indent(unsigned level_) -: parent(0), basic_indent(" "), level(level_) -{ } + //! setup without parent and basic_indentation of two spaces + inline Indent(unsigned level_) + : parent(0), basic_indent(" "), level(level_) + { } -//! setup with parent -/** -* \note Initial indentation level is 1 by default for this constructor. -*/ -inline Indent(const Indent* parent_, -const std::string& basic_indent_ = " ", unsigned level_ = 1) -: parent(parent_), basic_indent(basic_indent_), level(level_) -{ } + //! setup with parent + /** + * \note Initial indentation level is 1 by default for this constructor. + */ + inline Indent(const Indent* parent_, + const std::string& basic_indent_ = " ", unsigned level_ = 1) + : parent(parent_), basic_indent(basic_indent_), level(level_) + { } -//! setup with parent -inline Indent(const Indent* parent_, unsigned level_) -: parent(parent_), basic_indent(" "), level(level_) -{ } + //! setup with parent + inline Indent(const Indent* parent_, unsigned level_) + : parent(parent_), basic_indent(" "), level(level_) + { } -//! create new indentation object with this one as parent -inline Indent operator+(const std::string& newindent) const { -return Indent(this, newindent); -} -//! create a copy of this indentation object with raised level -inline Indent operator+(unsigned morelevel) const { -return Indent(parent, basic_indent, level+morelevel); -} -//! raise indentation level -inline Indent& operator++() { ++level; return *this; } -//! lower indentation level -inline Indent& operator--() { if ( level > 0 ) --level; return *this; } + //! create new indentation object with this one as parent + inline Indent operator+(const std::string& newindent) const { + return Indent(this, newindent); + } + //! create a copy of this indentation object with raised level + inline Indent operator+(unsigned morelevel) const { + return Indent(parent, basic_indent, level+morelevel); + } + //! raise indentation level + inline Indent& operator++() { ++level; return *this; } + //! lower indentation level + inline Indent& operator--() { if ( level > 0 ) --level; return *this; } -//! write indentation to a stream -friend inline std::ostream& operator<<(std::ostream& s, -const Indent& indent); -}; + //! write indentation to a stream + friend inline std::ostream& operator<<(std::ostream& s, + const Indent& indent); + }; -//! write indentation to a stream -inline std::ostream& operator<<(std::ostream& s, const Indent& indent) { -if(indent.parent) -s << *indent.parent; -for(unsigned i = 0; i < indent.level; ++i) -s << indent.basic_indent; -return s; -} + //! write indentation to a stream + inline std::ostream& operator<<(std::ostream& s, const Indent& indent) { + if(indent.parent) + s << *indent.parent; + for(unsigned i = 0; i < indent.level; ++i) + s << indent.basic_indent; + return s; + } -/** }@ group Common */ + /** }@ group Common */ } // namespace Dune diff --git a/dune/common/indices.hh b/dune/common/indices.hh index b4d1ea2dd232ca505e01e203fd2a348ed488e2dd..7c633365350e52f6cf8f7594480cd43a7f04aa14 100644 --- a/dune/common/indices.hh +++ b/dune/common/indices.hh @@ -13,118 +13,118 @@ namespace Dune { -/** \addtogroup Common -* \{ -*/ - -/** \brief An index constant with value i -* -* An index constant is a simple type alias for an integral_constant. -* Its main advantages are clarity (it is easier to see that code uses it -* as an index) and the fact that the integral type is fixed, reducing verbosity -* and avoiding the problem of maybe trying to overload / specialize using a different -* integral type. -*/ -template<std::size_t i> -using index_constant = std::integral_constant<std::size_t, i>; - - - -/** \brief Namespace with predefined compile time indices for the range [0,19] -* -* The predefined index objects in this namespace are `constexpr`, which allows them to -* be used in situations where a compile time constant is needed, e.g. for a template -* parameter. Apart from that, `constexpr` implies internal linkage, which helps to avoid -* ODR problems. -* -* The constants implicitly convert to their contained value, so you can for example write -* -* \code{.cc} -* std::array<int,_10> a; -* // the above line is equivalent to -* std::array<int,10> b; -* \endcode -* -*/ -namespace Indices -{ -//! Compile time index with value 0. -DUNE_INLINE_VARIABLE constexpr index_constant< 0> _0 = {}; - -//! Compile time index with value 1. -DUNE_INLINE_VARIABLE constexpr index_constant< 1> _1 = {}; - -//! Compile time index with value 2. -DUNE_INLINE_VARIABLE constexpr index_constant< 2> _2 = {}; - -//! Compile time index with value 3. -DUNE_INLINE_VARIABLE constexpr index_constant< 3> _3 = {}; - -//! Compile time index with value 4. -DUNE_INLINE_VARIABLE constexpr index_constant< 4> _4 = {}; - -//! Compile time index with value 5. -DUNE_INLINE_VARIABLE constexpr index_constant< 5> _5 = {}; - -//! Compile time index with value 6. -DUNE_INLINE_VARIABLE constexpr index_constant< 6> _6 = {}; - -//! Compile time index with value 7. -DUNE_INLINE_VARIABLE constexpr index_constant< 7> _7 = {}; - -//! Compile time index with value 8. -DUNE_INLINE_VARIABLE constexpr index_constant< 8> _8 = {}; - -//! Compile time index with value 9. -DUNE_INLINE_VARIABLE constexpr index_constant< 9> _9 = {}; - -//! Compile time index with value 10. -DUNE_INLINE_VARIABLE constexpr index_constant<10> _10 = {}; - -//! Compile time index with value 11. -DUNE_INLINE_VARIABLE constexpr index_constant<11> _11 = {}; - -//! Compile time index with value 12. -DUNE_INLINE_VARIABLE constexpr index_constant<12> _12 = {}; - -//! Compile time index with value 13. -DUNE_INLINE_VARIABLE constexpr index_constant<13> _13 = {}; - -//! Compile time index with value 14. -DUNE_INLINE_VARIABLE constexpr index_constant<14> _14 = {}; - -//! Compile time index with value 15. -DUNE_INLINE_VARIABLE constexpr index_constant<15> _15 = {}; - -//! Compile time index with value 16. -DUNE_INLINE_VARIABLE constexpr index_constant<16> _16 = {}; - -//! Compile time index with value 17. -DUNE_INLINE_VARIABLE constexpr index_constant<17> _17 = {}; - -//! Compile time index with value 18. -DUNE_INLINE_VARIABLE constexpr index_constant<18> _18 = {}; - -//! Compile time index with value 19. -DUNE_INLINE_VARIABLE constexpr index_constant<19> _19 = {}; - -} // namespace Indices - -/** -* \brief Unpack an std::integer_sequence<I,i...> to std::integral_constant<I,i>... -* -* This forward all entries of the given std::integer_sequence -* as individual std::integral_constant arguments to the given callback. -* -* \param f Callback which has to accept unpacked values -* \param sequence Packed std::integer_sequence of values -* \returns Result of calling f with unpacked integers. -*/ -template<class F, class I, I... i> -decltype(auto) unpackIntegerSequence(F&& f, std::integer_sequence<I, i...> sequence) -{ -return f(std::integral_constant<I, i>()...); -} + /** \addtogroup Common + * \{ + */ + + /** \brief An index constant with value i + * + * An index constant is a simple type alias for an integral_constant. + * Its main advantages are clarity (it is easier to see that code uses it + * as an index) and the fact that the integral type is fixed, reducing verbosity + * and avoiding the problem of maybe trying to overload / specialize using a different + * integral type. + */ + template<std::size_t i> + using index_constant = std::integral_constant<std::size_t, i>; + + + + /** \brief Namespace with predefined compile time indices for the range [0,19] + * + * The predefined index objects in this namespace are `constexpr`, which allows them to + * be used in situations where a compile time constant is needed, e.g. for a template + * parameter. Apart from that, `constexpr` implies internal linkage, which helps to avoid + * ODR problems. + * + * The constants implicitly convert to their contained value, so you can for example write + * + * \code{.cc} + * std::array<int,_10> a; + * // the above line is equivalent to + * std::array<int,10> b; + * \endcode + * + */ + namespace Indices + { + //! Compile time index with value 0. + DUNE_INLINE_VARIABLE constexpr index_constant< 0> _0 = {}; + + //! Compile time index with value 1. + DUNE_INLINE_VARIABLE constexpr index_constant< 1> _1 = {}; + + //! Compile time index with value 2. + DUNE_INLINE_VARIABLE constexpr index_constant< 2> _2 = {}; + + //! Compile time index with value 3. + DUNE_INLINE_VARIABLE constexpr index_constant< 3> _3 = {}; + + //! Compile time index with value 4. + DUNE_INLINE_VARIABLE constexpr index_constant< 4> _4 = {}; + + //! Compile time index with value 5. + DUNE_INLINE_VARIABLE constexpr index_constant< 5> _5 = {}; + + //! Compile time index with value 6. + DUNE_INLINE_VARIABLE constexpr index_constant< 6> _6 = {}; + + //! Compile time index with value 7. + DUNE_INLINE_VARIABLE constexpr index_constant< 7> _7 = {}; + + //! Compile time index with value 8. + DUNE_INLINE_VARIABLE constexpr index_constant< 8> _8 = {}; + + //! Compile time index with value 9. + DUNE_INLINE_VARIABLE constexpr index_constant< 9> _9 = {}; + + //! Compile time index with value 10. + DUNE_INLINE_VARIABLE constexpr index_constant<10> _10 = {}; + + //! Compile time index with value 11. + DUNE_INLINE_VARIABLE constexpr index_constant<11> _11 = {}; + + //! Compile time index with value 12. + DUNE_INLINE_VARIABLE constexpr index_constant<12> _12 = {}; + + //! Compile time index with value 13. + DUNE_INLINE_VARIABLE constexpr index_constant<13> _13 = {}; + + //! Compile time index with value 14. + DUNE_INLINE_VARIABLE constexpr index_constant<14> _14 = {}; + + //! Compile time index with value 15. + DUNE_INLINE_VARIABLE constexpr index_constant<15> _15 = {}; + + //! Compile time index with value 16. + DUNE_INLINE_VARIABLE constexpr index_constant<16> _16 = {}; + + //! Compile time index with value 17. + DUNE_INLINE_VARIABLE constexpr index_constant<17> _17 = {}; + + //! Compile time index with value 18. + DUNE_INLINE_VARIABLE constexpr index_constant<18> _18 = {}; + + //! Compile time index with value 19. + DUNE_INLINE_VARIABLE constexpr index_constant<19> _19 = {}; + + } // namespace Indices + + /** + * \brief Unpack an std::integer_sequence<I,i...> to std::integral_constant<I,i>... + * + * This forward all entries of the given std::integer_sequence + * as individual std::integral_constant arguments to the given callback. + * + * \param f Callback which has to accept unpacked values + * \param sequence Packed std::integer_sequence of values + * \returns Result of calling f with unpacked integers. + */ + template<class F, class I, I... i> + decltype(auto) unpackIntegerSequence(F&& f, std::integer_sequence<I, i...> sequence) + { + return f(std::integral_constant<I, i>()...); + } } //namespace Dune diff --git a/dune/common/interfaces.hh b/dune/common/interfaces.hh index 4e68284f11b12218581ded993f1c91e12a087366..d64b323bfb68c9d8a1d4d09cb397724325ed0bda 100644 --- a/dune/common/interfaces.hh +++ b/dune/common/interfaces.hh @@ -5,27 +5,27 @@ #include <dune/internal/dune-common.hh> /** @file -@author Robert Kloefkorn -@brief Provides interfaces for detection of specific behavior -*/ + @author Robert Kloefkorn + @brief Provides interfaces for detection of specific behavior + */ namespace Dune { -//! An interface class for cloneable objects -struct Cloneable { + //! An interface class for cloneable objects + struct Cloneable { -/** \brief Clones the object -* clone needs to be redefined by an implementation class, with the -* return type covariantly adapted. Remember to -* delete the resulting pointer. -*/ -virtual Cloneable* clone() const = 0; + /** \brief Clones the object + * clone needs to be redefined by an implementation class, with the + * return type covariantly adapted. Remember to + * delete the resulting pointer. + */ + virtual Cloneable* clone() const = 0; -/** \brief Destructor */ -virtual ~Cloneable() -{} + /** \brief Destructor */ + virtual ~Cloneable() + {} -}; + }; } // end namespace Dune #endif diff --git a/dune/common/ios_state.hh b/dune/common/ios_state.hh index ebc72dcf43299bc88f08c0bbcfaf28d2551b8328..134969916f6acd4c7fd16a7e1c13993652ae5e2a 100644 --- a/dune/common/ios_state.hh +++ b/dune/common/ios_state.hh @@ -7,70 +7,70 @@ #include <ios> namespace Dune { -/** @addtogroup Common -* -* @{ -*/ -/** -* @file -* @brief Utility class for storing and resetting stream attributes. -* @author Markus Blatt -*/ -/** -* @brief Utility class for storing and resetting stream attributes. -* -* The constructor saves the attributes currently set in the ios_base -* object and the destructor restores these attributes again. The -* attributes can also be restores at any time by calling the method -* restore(). -* -* The saved attributes are the format flags, precision, and width. -* -* @note The interface of this class is meant to be drop-in compatible the -* the class of the same name from <boost/io/ios_state.hpp>. -*/ -class ios_base_all_saver -{ -public: -/** @brief Export type of object we save the state for */ -typedef std::ios_base state_type; + /** @addtogroup Common + * + * @{ + */ + /** + * @file + * @brief Utility class for storing and resetting stream attributes. + * @author Markus Blatt + */ + /** + * @brief Utility class for storing and resetting stream attributes. + * + * The constructor saves the attributes currently set in the ios_base + * object and the destructor restores these attributes again. The + * attributes can also be restores at any time by calling the method + * restore(). + * + * The saved attributes are the format flags, precision, and width. + * + * @note The interface of this class is meant to be drop-in compatible the + * the class of the same name from <boost/io/ios_state.hpp>. + */ + class ios_base_all_saver + { + public: + /** @brief Export type of object we save the state for */ + typedef std::ios_base state_type; -/** -* @brief Constructor that stores the currently used flags. -* @param ios_ The ios_base object whose flags are to be saved and -* restored. Any stream object should work here. -* -* @note A reference to the ios_base object is store in this object. Thus -* the ios_base object must remain valid until the destructor of -* this object has been called. -*/ -ios_base_all_saver(state_type& ios_); + /** + * @brief Constructor that stores the currently used flags. + * @param ios_ The ios_base object whose flags are to be saved and + * restored. Any stream object should work here. + * + * @note A reference to the ios_base object is store in this object. Thus + * the ios_base object must remain valid until the destructor of + * this object has been called. + */ + ios_base_all_saver(state_type& ios_); -/** -* @brief Destructor that restores the flags stored by the constructor. -*/ -~ios_base_all_saver(); + /** + * @brief Destructor that restores the flags stored by the constructor. + */ + ~ios_base_all_saver(); -/** -* @brief Restore flags now -* -* The flags will also be restored at destruction time even if this method -* was used. -*/ -void restore(); + /** + * @brief Restore flags now + * + * The flags will also be restored at destruction time even if this method + * was used. + */ + void restore(); -private: -/** @brief the ios object to restore the flags to. */ -state_type& ios; -/** @brief The flags used when the constructor was called. */ -state_type::fmtflags oldflags; -/** @brief The precision in use when the constructor was called. */ -std::streamsize oldprec; -/** @brief The width in use when the constructor was called. */ -std::streamsize oldwidth; -}; + private: + /** @brief the ios object to restore the flags to. */ + state_type& ios; + /** @brief The flags used when the constructor was called. */ + state_type::fmtflags oldflags; + /** @brief The precision in use when the constructor was called. */ + std::streamsize oldprec; + /** @brief The width in use when the constructor was called. */ + std::streamsize oldwidth; + }; -/** }@ */ + /** }@ */ } #endif // DUNE_COMMON_IOS_STATE_HH diff --git a/dune/common/iteratorfacades.hh b/dune/common/iteratorfacades.hh index 27612dff4caabfdc760dd2cb9c9dd0eba0c1a082..4c48c812fce1896840cb5f3142d4404307b8c9b1 100644 --- a/dune/common/iteratorfacades.hh +++ b/dune/common/iteratorfacades.hh @@ -11,727 +11,727 @@ namespace Dune { -/*! \defgroup IteratorFacades Iterator facades -\ingroup Common + /*! \defgroup IteratorFacades Iterator facades + \ingroup Common -\brief Iterator facades for writing stl conformant iterators. - -With using these facades writing iterators for arbitrary containers becomes much less -cumbersome as only few functions have to be implemented. All other functions needed by -the stl are provided by the facades using the Barton-Nackman trick (also known as -curiously recurring template pattern). - -The following example illustrates how a random access iterator might be written: - -\code -#include<dune/common/iteratorfacades.hh> - -... - -template<class C, class T> -class TestIterator : public Dune::BidirectionalIteratorFacade<TestIterator<C,T>,T, T&, int> -{ -friend class TestIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type >; -friend class TestIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type >; - -public: - -// Constructors needed by the facade iterators. -TestIterator(): container_(0), position_(0) -{ } - -TestIterator(C& cont, int pos) -: container_(&cont), position_(pos) -{} - -TestIterator(const TestIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type >& other) -: container_(other.container_), position_(other.position_) -{} - - -TestIterator(const TestIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type >& other) -: container_(other.container_), position_(other.position_) -{} - -// Methods needed by the forward iterator -bool equals(const TestIterator<typename std::remove_const<C>::type,typename std::remove_const<T>::type>& other) const -{ -return position_ == other.position_ && container_ == other.container_; -} - - -bool equals(const TestIterator<const typename std::remove_const<C>::type,const typename std::remove_const<T>::type>& other) const -{ -return position_ == other.position_ && container_ == other.container_; -} - -T& dereference() const -{ -return container_->values_[position_]; -} - -void increment() -{ -++position_; -} - -// Additional function needed by BidirectionalIterator -void decrement() -{ ---position_; -} - -// Additional function needed by RandomAccessIterator -T& elementAt(int i)const -{ -return container_->operator[](position_+i); -} - -void advance(int n) -{ -position_=position_+n; -} - -std::ptrdiff_t distanceTo(TestIterator<const typename std::remove_const<C>::type,const typename std::remove_const<T>::type> other) const -{ -assert(other.container_==container_); -return other.position_ - position_; -} - -std::ptrdiff_t distanceTo(TestIterator<const typename std::remove_const<C>::type, typename std::remove_const<T>::type> other) const -{ -assert(other.container_==container_); -return other.position_ - position_; -} -private: -C *container_; -size_t position_; -}; - -\endcode -See dune/common/test/iteratorbase.hh for details. -*/ - - -/** -* @file -* @brief This file implements iterator facade classes for writing stl conformant iterators. -* -* With using these facades writing iterators for arbitrary containers becomes much less -* cumbersome as only few functions have to be implemented. All other functions needed by -* the stl are provided by the facades using the Barton-Nackman trick (also known as -* curiously recurring template pattern. -*/ - -/** @addtogroup IteratorFacades -* -* @{ -*/ -/** -* @brief Base class for stl conformant forward iterators. -* -* \tparam T The derived class -* \tparam V The value type -* \tparam R The reference type -* \tparam D The type for differences between two iterators -*/ -template<class T, class V, class R = V&, class D = std::ptrdiff_t> -class ForwardIteratorFacade -{ - -public: -/* type aliases required by C++ for iterators */ -using iterator_category = std::forward_iterator_tag; -using value_type = typename std::remove_const<V>::type; -using difference_type = D; -using pointer = V*; -using reference = R; - -/** -* @brief The type of derived iterator. -* -* The iterator has to define following -* functions have to be present: -* -* \code -* -* // Access the value referred to. -* Reference dereference() const; -* -* // Compare for equality with iterator j -* bool equals(j); -* -* // position the iterator at the next element. -* void increment() -* -* // check for equality with other iterator -* bool equals(other) -* \endcode -* -* For an elaborate explanation see the -* <A HREF="http://www.sgi.com/tech/stl/iterator_traits.html">STL Documentation</A>! -*/ -typedef T DerivedType; - -/** -* @brief The type of value accessed through the iterator. -*/ -typedef V Value; - -/** -* @brief The pointer to the Value. -*/ -typedef V* Pointer; - -/** -* @brief The type of the difference between two positions. -*/ -typedef D DifferenceType; - -/** -* @brief The type of the reference to the values accessed. -*/ -typedef R Reference; - -/** @brief Dereferencing operator. */ -Reference operator*() const -{ -return static_cast<DerivedType const*>(this)->dereference(); -} - -Pointer operator->() const -{ -return &(static_cast<const DerivedType *>(this)->dereference()); -} - -/** @brief Preincrement operator. */ -DerivedType& operator++() -{ -static_cast<DerivedType *>(this)->increment(); -return *static_cast<DerivedType *>(this); -} - -/** @brief Postincrement operator. */ -DerivedType operator++(int) -{ -DerivedType tmp(static_cast<DerivedType const&>(*this)); -this->operator++(); -return tmp; -} -}; - -/** -* @brief Checks for equality. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator==(const ForwardIteratorFacade<T1,V1,R1,D>& lhs, -const ForwardIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); -else -return static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); -} - -/** -* @brief Checks for inequality. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator!=(const ForwardIteratorFacade<T1,V1,R1,D>& lhs, -const ForwardIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return !static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); -else -return !static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); -} - -/** -* @brief Facade class for stl conformant bidirectional iterators. -* -*/ -template<class T, class V, class R = V&, class D = std::ptrdiff_t> -class BidirectionalIteratorFacade -{ - -public: -/* type aliases required by C++ for iterators */ -using iterator_category = std::bidirectional_iterator_tag; -using value_type = typename std::remove_const<V>::type; -using difference_type = D; -using pointer = V*; -using reference = R; - -/** -* @brief The type of derived iterator. -* -* The iterator has to define following -* functions have to be present: -* -* \code -* -* // Access the value referred to. -* Reference dereference() const; -* -* // Compare for equality with j -* bool equals(j); -* -* // position the iterator at the next element. -* void increment() -* -* // position the iterator at the previous element. -* void decrement() -* -* \endcode -* -* For an elaborate explanation see the -* <A HREF="http://www.sgi.com/tech/stl/iterator_traits.html">STL Documentation</A> -*/ -typedef T DerivedType; - -/** -* @brief The type of value accessed through the iterator. -*/ -typedef V Value; - -/** -* @brief The pointer to the Value. -*/ -typedef V* Pointer; - -/** -* @brief The type of the difference between two positions. -*/ -typedef D DifferenceType; - -/** -* @brief The type of the reference to the values accessed. -*/ -typedef R Reference; - -/** @brief Dereferencing operator. */ -Reference operator*() const -{ -return static_cast<DerivedType const*>(this)->dereference(); -} - -Pointer operator->() const -{ -return &(static_cast<const DerivedType *>(this)->dereference()); -} - -/** @brief Preincrement operator. */ -DerivedType& operator++() -{ -static_cast<DerivedType *>(this)->increment(); -return *static_cast<DerivedType *>(this); -} - -/** @brief Postincrement operator. */ -DerivedType operator++(int) -{ -DerivedType tmp(static_cast<DerivedType const&>(*this)); -this->operator++(); -return tmp; -} - - -/** @brief Preincrement operator. */ -DerivedType& operator--() -{ -static_cast<DerivedType *>(this)->decrement(); -return *static_cast<DerivedType *>(this); -} - -/** @brief Postincrement operator. */ -DerivedType operator--(int) -{ -DerivedType tmp(static_cast<DerivedType const&>(*this)); -this->operator--(); -return tmp; -} -}; - -/** -* @brief Checks for equality. -* -* This operation is only defined if T2 is convertible to T1, otherwise it -* is removed from the overload set since the enable_if for the return type -* yield an invalid type expression. -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename std::enable_if<std::is_convertible<T2,T1>::value,bool>::type -operator==(const BidirectionalIteratorFacade<T1,V1,R1,D>& lhs, -const BidirectionalIteratorFacade<T2,V2,R2,D>& rhs) -{ -return static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); -} - -/** -* @brief Checks for equality. -* -* This operation is only defined if either T1 is convertible to T2, and T2 -* is not convetible to T1. Otherwise the operator is removed from the -* overload set since the enable_if for the return type yield an invalid -* type expression. -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline -typename std::enable_if<std::is_convertible<T1,T2>::value && !std::is_convertible<T2,T1>::value, -bool>::type -operator==(const BidirectionalIteratorFacade<T1,V1,R1,D>& lhs, -const BidirectionalIteratorFacade<T2,V2,R2,D>& rhs) -{ -return static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); -} - -/** -* @brief Checks for inequality. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator!=(const BidirectionalIteratorFacade<T1,V1,R1,D>& lhs, -const BidirectionalIteratorFacade<T2,V2,R2,D>& rhs) -{ -return !(lhs == rhs); -} - -/** -* @brief Base class for stl conformant forward iterators. -* -*/ -template<class T, class V, class R = V&, class D = std::ptrdiff_t> -class RandomAccessIteratorFacade -{ - -public: -/* type aliases required by C++ for iterators */ -using iterator_category = std::random_access_iterator_tag; -using value_type = typename std::remove_const<V>::type; -using difference_type = D; -using pointer = V*; -using reference = R; - -/** -* @brief The type of derived iterator. -* -* The iterator has to define following -* functions have to be present: -* -* \code -* -* // Access the value referred to. -* Reference dereference() const; -* // Access the value at some other location -* Reference elementAt(n) const; -* -* // Compare for equality with j -* bool equals(j); -* -* // position the iterator at the next element. -* void increment() -* -* // position the iterator at the previous element. -* void decrement() -* -* // advance the iterator by a number of positions- -* void advance(DifferenceType n); -* // calculate the distance to another iterator. -* // One should incorporate an assertion whether -* // the same containers are referenced -* DifferenceType distanceTo(j) const; -* \endcode -* -* For an elaborate explanation see the -* <A HREF="http://www.sgi.com/tech/stl/iterator_traits.html">STL Documentation</A> -*/ -typedef T DerivedType; - -/** -* @brief The type of value accessed through the iterator. -*/ -typedef V Value; - -/** -* @brief The pointer to the Value. -*/ -typedef V* Pointer; - -/** -* @brief The type of the difference between two positions. -*/ -typedef D DifferenceType; - -/** -* @brief The type of the reference to the values accessed. -*/ -typedef R Reference; - -/** @brief Dereferencing operator. */ -Reference operator*() const -{ -return static_cast<DerivedType const*>(this)->dereference(); -} - -Pointer operator->() const -{ -return &(static_cast<const DerivedType *>(this)->dereference()); -} - -/** -* @brief Get the element n positions from the current one. -* @param n The distance to the element. -* @return The element at that distance. -*/ -Reference operator[](DifferenceType n) const -{ -return static_cast<const DerivedType *>(this)->elementAt(n); -} - -/** @brief Preincrement operator. */ -DerivedType& operator++() -{ -static_cast<DerivedType *>(this)->increment(); -return *static_cast<DerivedType *>(this); -} - -/** @brief Postincrement operator. */ -DerivedType operator++(int) -{ -DerivedType tmp(static_cast<DerivedType const&>(*this)); -this->operator++(); -return tmp; -} - -DerivedType& operator+=(DifferenceType n) -{ -static_cast<DerivedType *>(this)->advance(n); -return *static_cast<DerivedType *>(this); -} - -DerivedType operator+(DifferenceType n) const -{ -DerivedType tmp(static_cast<DerivedType const&>(*this)); -tmp.advance(n); -return tmp; -} - - -/** @brief Predecrement operator. */ -DerivedType& operator--() -{ -static_cast<DerivedType *>(this)->decrement(); -return *static_cast<DerivedType *>(this); -} - -/** @brief Postdecrement operator. */ -DerivedType operator--(int) -{ -DerivedType tmp(static_cast<DerivedType const&>(*this)); -this->operator--(); -return tmp; -} - -DerivedType& operator-=(DifferenceType n) -{ -static_cast<DerivedType *>(this)->advance(-n); -return *static_cast<DerivedType *>(this); -} - -DerivedType operator-(DifferenceType n) const -{ -DerivedType tmp(static_cast<DerivedType const&>(*this)); -tmp.advance(-n); -return tmp; -} - - -}; - -/** -* @brief Checks for equality. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator==(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, -const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); -else -return static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); -} - -/** -* @brief Checks for inequality. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator!=(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, -const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return !static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); -else -return !static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); -} - -/** -* @brief Comparison operator. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator<(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, -const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))>0; -else -return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))<0; -} - - -/** -* @brief Comparison operator. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator<=(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, -const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))>=0; -else -return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))<=0; -} - - -/** -* @brief Comparison operator. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator>(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, -const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))<0; -else -return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))>0; -} - -/** -* @brief Comparison operator. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,bool>::type -operator>=(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, -const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))<=0; -else -return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))>=0; -} - -/** -* @brief Calculates the difference between two pointers. -* -* This operation is only defined if either D2 -* is convertible to D1 or vice versa. If that is -* not the case the compiler will report an error -* as EnableIfInterOperable<D1,D2,bool>::type is -* not defined. -* -*/ -template<class T1, class V1, class R1, class D, -class T2, class V2, class R2> -inline typename EnableIfInterOperable<T1,T2,D>::type -operator-(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, -const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) -{ -if(std::is_convertible<T2,T1>::value) -return -static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs)); -else -return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs)); -} - -/** @} */ + \brief Iterator facades for writing stl conformant iterators. + + With using these facades writing iterators for arbitrary containers becomes much less + cumbersome as only few functions have to be implemented. All other functions needed by + the stl are provided by the facades using the Barton-Nackman trick (also known as + curiously recurring template pattern). + + The following example illustrates how a random access iterator might be written: + + \code + #include<dune/common/iteratorfacades.hh> + + ... + + template<class C, class T> + class TestIterator : public Dune::BidirectionalIteratorFacade<TestIterator<C,T>,T, T&, int> + { + friend class TestIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type >; + friend class TestIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type >; + + public: + + // Constructors needed by the facade iterators. + TestIterator(): container_(0), position_(0) + { } + + TestIterator(C& cont, int pos) + : container_(&cont), position_(pos) + {} + + TestIterator(const TestIterator<typename std::remove_const<C>::type, typename std::remove_const<T>::type >& other) + : container_(other.container_), position_(other.position_) + {} + + + TestIterator(const TestIterator<const typename std::remove_const<C>::type, const typename std::remove_const<T>::type >& other) + : container_(other.container_), position_(other.position_) + {} + + // Methods needed by the forward iterator + bool equals(const TestIterator<typename std::remove_const<C>::type,typename std::remove_const<T>::type>& other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + + bool equals(const TestIterator<const typename std::remove_const<C>::type,const typename std::remove_const<T>::type>& other) const + { + return position_ == other.position_ && container_ == other.container_; + } + + T& dereference() const + { + return container_->values_[position_]; + } + + void increment() + { + ++position_; + } + + // Additional function needed by BidirectionalIterator + void decrement() + { + --position_; + } + + // Additional function needed by RandomAccessIterator + T& elementAt(int i)const + { + return container_->operator[](position_+i); + } + + void advance(int n) + { + position_=position_+n; + } + + std::ptrdiff_t distanceTo(TestIterator<const typename std::remove_const<C>::type,const typename std::remove_const<T>::type> other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + + std::ptrdiff_t distanceTo(TestIterator<const typename std::remove_const<C>::type, typename std::remove_const<T>::type> other) const + { + assert(other.container_==container_); + return other.position_ - position_; + } + private: + C *container_; + size_t position_; + }; + + \endcode + See dune/common/test/iteratorbase.hh for details. + */ + + + /** + * @file + * @brief This file implements iterator facade classes for writing stl conformant iterators. + * + * With using these facades writing iterators for arbitrary containers becomes much less + * cumbersome as only few functions have to be implemented. All other functions needed by + * the stl are provided by the facades using the Barton-Nackman trick (also known as + * curiously recurring template pattern. + */ + + /** @addtogroup IteratorFacades + * + * @{ + */ + /** + * @brief Base class for stl conformant forward iterators. + * + * \tparam T The derived class + * \tparam V The value type + * \tparam R The reference type + * \tparam D The type for differences between two iterators + */ + template<class T, class V, class R = V&, class D = std::ptrdiff_t> + class ForwardIteratorFacade + { + + public: + /* type aliases required by C++ for iterators */ + using iterator_category = std::forward_iterator_tag; + using value_type = typename std::remove_const<V>::type; + using difference_type = D; + using pointer = V*; + using reference = R; + + /** + * @brief The type of derived iterator. + * + * The iterator has to define following + * functions have to be present: + * + * \code + * + * // Access the value referred to. + * Reference dereference() const; + * + * // Compare for equality with iterator j + * bool equals(j); + * + * // position the iterator at the next element. + * void increment() + * + * // check for equality with other iterator + * bool equals(other) + * \endcode + * + * For an elaborate explanation see the + * <A HREF="http://www.sgi.com/tech/stl/iterator_traits.html">STL Documentation</A>! + */ + typedef T DerivedType; + + /** + * @brief The type of value accessed through the iterator. + */ + typedef V Value; + + /** + * @brief The pointer to the Value. + */ + typedef V* Pointer; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + /** @brief Dereferencing operator. */ + Reference operator*() const + { + return static_cast<DerivedType const*>(this)->dereference(); + } + + Pointer operator->() const + { + return &(static_cast<const DerivedType *>(this)->dereference()); + } + + /** @brief Preincrement operator. */ + DerivedType& operator++() + { + static_cast<DerivedType *>(this)->increment(); + return *static_cast<DerivedType *>(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator++(int) + { + DerivedType tmp(static_cast<DerivedType const&>(*this)); + this->operator++(); + return tmp; + } + }; + + /** + * @brief Checks for equality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator==(const ForwardIteratorFacade<T1,V1,R1,D>& lhs, + const ForwardIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); + else + return static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); + } + + /** + * @brief Checks for inequality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator!=(const ForwardIteratorFacade<T1,V1,R1,D>& lhs, + const ForwardIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return !static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); + else + return !static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); + } + + /** + * @brief Facade class for stl conformant bidirectional iterators. + * + */ + template<class T, class V, class R = V&, class D = std::ptrdiff_t> + class BidirectionalIteratorFacade + { + + public: + /* type aliases required by C++ for iterators */ + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename std::remove_const<V>::type; + using difference_type = D; + using pointer = V*; + using reference = R; + + /** + * @brief The type of derived iterator. + * + * The iterator has to define following + * functions have to be present: + * + * \code + * + * // Access the value referred to. + * Reference dereference() const; + * + * // Compare for equality with j + * bool equals(j); + * + * // position the iterator at the next element. + * void increment() + * + * // position the iterator at the previous element. + * void decrement() + * + * \endcode + * + * For an elaborate explanation see the + * <A HREF="http://www.sgi.com/tech/stl/iterator_traits.html">STL Documentation</A> + */ + typedef T DerivedType; + + /** + * @brief The type of value accessed through the iterator. + */ + typedef V Value; + + /** + * @brief The pointer to the Value. + */ + typedef V* Pointer; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + /** @brief Dereferencing operator. */ + Reference operator*() const + { + return static_cast<DerivedType const*>(this)->dereference(); + } + + Pointer operator->() const + { + return &(static_cast<const DerivedType *>(this)->dereference()); + } + + /** @brief Preincrement operator. */ + DerivedType& operator++() + { + static_cast<DerivedType *>(this)->increment(); + return *static_cast<DerivedType *>(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator++(int) + { + DerivedType tmp(static_cast<DerivedType const&>(*this)); + this->operator++(); + return tmp; + } + + + /** @brief Preincrement operator. */ + DerivedType& operator--() + { + static_cast<DerivedType *>(this)->decrement(); + return *static_cast<DerivedType *>(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator--(int) + { + DerivedType tmp(static_cast<DerivedType const&>(*this)); + this->operator--(); + return tmp; + } + }; + + /** + * @brief Checks for equality. + * + * This operation is only defined if T2 is convertible to T1, otherwise it + * is removed from the overload set since the enable_if for the return type + * yield an invalid type expression. + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename std::enable_if<std::is_convertible<T2,T1>::value,bool>::type + operator==(const BidirectionalIteratorFacade<T1,V1,R1,D>& lhs, + const BidirectionalIteratorFacade<T2,V2,R2,D>& rhs) + { + return static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); + } + + /** + * @brief Checks for equality. + * + * This operation is only defined if either T1 is convertible to T2, and T2 + * is not convetible to T1. Otherwise the operator is removed from the + * overload set since the enable_if for the return type yield an invalid + * type expression. + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline + typename std::enable_if<std::is_convertible<T1,T2>::value && !std::is_convertible<T2,T1>::value, + bool>::type + operator==(const BidirectionalIteratorFacade<T1,V1,R1,D>& lhs, + const BidirectionalIteratorFacade<T2,V2,R2,D>& rhs) + { + return static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); + } + + /** + * @brief Checks for inequality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator!=(const BidirectionalIteratorFacade<T1,V1,R1,D>& lhs, + const BidirectionalIteratorFacade<T2,V2,R2,D>& rhs) + { + return !(lhs == rhs); + } + + /** + * @brief Base class for stl conformant forward iterators. + * + */ + template<class T, class V, class R = V&, class D = std::ptrdiff_t> + class RandomAccessIteratorFacade + { + + public: + /* type aliases required by C++ for iterators */ + using iterator_category = std::random_access_iterator_tag; + using value_type = typename std::remove_const<V>::type; + using difference_type = D; + using pointer = V*; + using reference = R; + + /** + * @brief The type of derived iterator. + * + * The iterator has to define following + * functions have to be present: + * + * \code + * + * // Access the value referred to. + * Reference dereference() const; + * // Access the value at some other location + * Reference elementAt(n) const; + * + * // Compare for equality with j + * bool equals(j); + * + * // position the iterator at the next element. + * void increment() + * + * // position the iterator at the previous element. + * void decrement() + * + * // advance the iterator by a number of positions- + * void advance(DifferenceType n); + * // calculate the distance to another iterator. + * // One should incorporate an assertion whether + * // the same containers are referenced + * DifferenceType distanceTo(j) const; + * \endcode + * + * For an elaborate explanation see the + * <A HREF="http://www.sgi.com/tech/stl/iterator_traits.html">STL Documentation</A> + */ + typedef T DerivedType; + + /** + * @brief The type of value accessed through the iterator. + */ + typedef V Value; + + /** + * @brief The pointer to the Value. + */ + typedef V* Pointer; + + /** + * @brief The type of the difference between two positions. + */ + typedef D DifferenceType; + + /** + * @brief The type of the reference to the values accessed. + */ + typedef R Reference; + + /** @brief Dereferencing operator. */ + Reference operator*() const + { + return static_cast<DerivedType const*>(this)->dereference(); + } + + Pointer operator->() const + { + return &(static_cast<const DerivedType *>(this)->dereference()); + } + + /** + * @brief Get the element n positions from the current one. + * @param n The distance to the element. + * @return The element at that distance. + */ + Reference operator[](DifferenceType n) const + { + return static_cast<const DerivedType *>(this)->elementAt(n); + } + + /** @brief Preincrement operator. */ + DerivedType& operator++() + { + static_cast<DerivedType *>(this)->increment(); + return *static_cast<DerivedType *>(this); + } + + /** @brief Postincrement operator. */ + DerivedType operator++(int) + { + DerivedType tmp(static_cast<DerivedType const&>(*this)); + this->operator++(); + return tmp; + } + + DerivedType& operator+=(DifferenceType n) + { + static_cast<DerivedType *>(this)->advance(n); + return *static_cast<DerivedType *>(this); + } + + DerivedType operator+(DifferenceType n) const + { + DerivedType tmp(static_cast<DerivedType const&>(*this)); + tmp.advance(n); + return tmp; + } + + + /** @brief Predecrement operator. */ + DerivedType& operator--() + { + static_cast<DerivedType *>(this)->decrement(); + return *static_cast<DerivedType *>(this); + } + + /** @brief Postdecrement operator. */ + DerivedType operator--(int) + { + DerivedType tmp(static_cast<DerivedType const&>(*this)); + this->operator--(); + return tmp; + } + + DerivedType& operator-=(DifferenceType n) + { + static_cast<DerivedType *>(this)->advance(-n); + return *static_cast<DerivedType *>(this); + } + + DerivedType operator-(DifferenceType n) const + { + DerivedType tmp(static_cast<DerivedType const&>(*this)); + tmp.advance(-n); + return tmp; + } + + + }; + + /** + * @brief Checks for equality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator==(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, + const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); + else + return static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); + } + + /** + * @brief Checks for inequality. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator!=(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, + const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return !static_cast<const T1&>(lhs).equals(static_cast<const T2&>(rhs)); + else + return !static_cast<const T2&>(rhs).equals(static_cast<const T1&>(lhs)); + } + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator<(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, + const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))>0; + else + return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))<0; + } + + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator<=(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, + const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))>=0; + else + return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))<=0; + } + + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator>(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, + const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))<0; + else + return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))>0; + } + + /** + * @brief Comparison operator. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,bool>::type + operator>=(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, + const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs))<=0; + else + return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs))>=0; + } + + /** + * @brief Calculates the difference between two pointers. + * + * This operation is only defined if either D2 + * is convertible to D1 or vice versa. If that is + * not the case the compiler will report an error + * as EnableIfInterOperable<D1,D2,bool>::type is + * not defined. + * + */ + template<class T1, class V1, class R1, class D, + class T2, class V2, class R2> + inline typename EnableIfInterOperable<T1,T2,D>::type + operator-(const RandomAccessIteratorFacade<T1,V1,R1,D>& lhs, + const RandomAccessIteratorFacade<T2,V2,R2,D>& rhs) + { + if(std::is_convertible<T2,T1>::value) + return -static_cast<const T1&>(lhs).distanceTo(static_cast<const T2&>(rhs)); + else + return static_cast<const T2&>(rhs).distanceTo(static_cast<const T1&>(lhs)); + } + + /** @} */ } #endif diff --git a/dune/common/iteratorrange.hh b/dune/common/iteratorrange.hh index a78c88dc6af192ca49fc1b8de30b9da346385caa..be0c0be1148a5c102218d920fe6f49716e066506 100644 --- a/dune/common/iteratorrange.hh +++ b/dune/common/iteratorrange.hh @@ -6,59 +6,59 @@ namespace Dune { -//! Simple range between a begin and an end iterator. -/** -* IteratorRange is mainly useful as a lightweight adaptor -* class when adding support for range-based for loops to -* existing containers that lack a standard begin(), end() -* pair of member functions. -* -* \tparam Iterator The type of iterator -* \ingroup CxxUtilities -*/ -template<typename Iterator> -class IteratorRange -{ + //! Simple range between a begin and an end iterator. + /** + * IteratorRange is mainly useful as a lightweight adaptor + * class when adding support for range-based for loops to + * existing containers that lack a standard begin(), end() + * pair of member functions. + * + * \tparam Iterator The type of iterator + * \ingroup CxxUtilities + */ + template<typename Iterator> + class IteratorRange + { -public: + public: -//! The iterator belonging to this range. -typedef Iterator iterator; + //! The iterator belonging to this range. + typedef Iterator iterator; -//! The iterator belonging to this range. -/** -* This typedef is here mainly for compatibility reasons. -*/ -typedef Iterator const_iterator; + //! The iterator belonging to this range. + /** + * This typedef is here mainly for compatibility reasons. + */ + typedef Iterator const_iterator; -//! Constructs an iterator range on [begin,end). -IteratorRange(const Iterator& begin, const Iterator& end) -: _begin(begin) -, _end(end) -{} + //! Constructs an iterator range on [begin,end). + IteratorRange(const Iterator& begin, const Iterator& end) + : _begin(begin) + , _end(end) + {} -//! Default constructor, relies on iterators being default-constructible. -IteratorRange() -{} + //! Default constructor, relies on iterators being default-constructible. + IteratorRange() + {} -//! Returns an iterator pointing to the begin of the range. -iterator begin() const -{ -return _begin; -} + //! Returns an iterator pointing to the begin of the range. + iterator begin() const + { + return _begin; + } -//! Returns an iterator pointing past the end of the range. -iterator end() const -{ -return _end; -} + //! Returns an iterator pointing past the end of the range. + iterator end() const + { + return _end; + } -private: + private: -Iterator _begin; -Iterator _end; + Iterator _begin; + Iterator _end; -}; + }; } diff --git a/dune/common/keywords.hh b/dune/common/keywords.hh index 6de625af21807d3c76341bc74d49d2069107c64d..2fa82b6a84836befe17fff76d3725a3456ef8bdd 100644 --- a/dune/common/keywords.hh +++ b/dune/common/keywords.hh @@ -3,15 +3,15 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Definitions of several macros that conditionally make C++ syntax -* available. -* -* This header contains several macros that enable C++ features depending on your -* compiler. Most of these features are optional and provide additional functionality -* like making code constexpr. -* -* \ingroup CxxUtilities -*/ + * \brief Definitions of several macros that conditionally make C++ syntax + * available. + * + * This header contains several macros that enable C++ features depending on your + * compiler. Most of these features are optional and provide additional functionality + * like making code constexpr. + * + * \ingroup CxxUtilities + */ #if __cpp_inline_variables >= 201606 @@ -19,8 +19,8 @@ #else //! Preprocessor macro used for marking variables inline on supported compilers. /** -* \ingroup CxxUtilities -*/ + * \ingroup CxxUtilities + */ #define DUNE_INLINE_VARIABLE #endif @@ -30,8 +30,8 @@ #else //! Preprocessor macro used for marking code as constexpr under the relaxed rules of C++14 if supported by the compiler. /** -* \ingroup CxxUtilities -*/ + * \ingroup CxxUtilities + */ #define DUNE_GENERALIZED_CONSTEXPR #endif diff --git a/dune/common/lcm.hh b/dune/common/lcm.hh index c89887723bdaf77e9c7136217d7cfeaae3d8d187..0d972099d71dc260d6389d6a5170ac4cb5bf4284 100644 --- a/dune/common/lcm.hh +++ b/dune/common/lcm.hh @@ -7,41 +7,41 @@ #warning "This header is deprecated and will be removed after release 2.8. Use std::lcm instead." /** \file -* \brief Statically compute the least common multiple of two integers -*/ + * \brief Statically compute the least common multiple of two integers + */ #include <numeric> namespace Dune { -/** -* @addtogroup Common -* @{ -*/ -/** -* @file -* This file provides template constructs for calculation the -* least common multiple. -*/ + /** + * @addtogroup Common + * @{ + */ + /** + * @file + * This file provides template constructs for calculation the + * least common multiple. + */ -/** -* @brief Calculate the least common multiple of two numbers -*/ -template<long m, long n> -struct [[deprecated("Will be removed after Dune 2.8. Use std::lcm instead.")]] Lcm -{ -static void conceptCheck() -{ -static_assert(0<m, "m must be positive!"); -static_assert(0<n, "n must be positive!"); -} -/** -* @brief The least common multiple of the template parameters -* m and n. -*/ -constexpr static long value = std::lcm(m,n); -}; + /** + * @brief Calculate the least common multiple of two numbers + */ + template<long m, long n> + struct [[deprecated("Will be removed after Dune 2.8. Use std::lcm instead.")]] Lcm + { + static void conceptCheck() + { + static_assert(0<m, "m must be positive!"); + static_assert(0<n, "n must be positive!"); + } + /** + * @brief The least common multiple of the template parameters + * m and n. + */ + constexpr static long value = std::lcm(m,n); + }; } #endif diff --git a/dune/common/lru.hh b/dune/common/lru.hh index 64cc707e4909b8100ce26ce59076fc9edae6ee74..fcac126dbe30db16c3ab31936d55a31fd0185f36 100644 --- a/dune/common/lru.hh +++ b/dune/common/lru.hh @@ -13,226 +13,226 @@ #include <dune/common/unused.hh> /** @file -@author Christian Engwer -@brief LRU Cache Container, using an STL like interface -*/ + @author Christian Engwer + @brief LRU Cache Container, using an STL like interface + */ namespace Dune { -namespace { - -/* -hide the default traits in an empty namespace -*/ -template <typename Key, typename Tp, -typename Alloc = std::allocator<Tp> > -struct _lru_default_traits -{ -typedef Key key_type; -typedef Alloc allocator; -typedef std::list< std::pair<Key, Tp> > list_type; -typedef typename list_type::iterator iterator; -typedef typename std::less<key_type> cmp; -typedef std::map< key_type, iterator, cmp, -typename std::allocator_traits<allocator>::template rebind_alloc<std::pair<const key_type, iterator> > > map_type; -}; - -} // end empty namespace - -/** -@brief LRU Cache Container - -Implementation of an LRU (least recently used) cache -container. This implementation follows the approach presented in -http://aim.adc.rmit.edu.au/phd/sgreuter/papers/graphite2003.pdf -*/ -template <typename Key, typename Tp, -typename Traits = _lru_default_traits<Key, Tp> > -class lru -{ -typedef typename Traits::list_type list_type; -typedef typename Traits::map_type map_type; -typedef typename Traits::allocator allocator; -typedef typename map_type::iterator map_iterator; -typedef typename map_type::const_iterator const_map_iterator; - -public: -typedef typename Traits::key_type key_type; -typedef typename allocator::value_type value_type; -using pointer = typename allocator::value_type*; -using const_pointer = typename allocator::value_type const*; -using const_reference = typename allocator::value_type const&; -using reference = typename allocator::value_type&; -typedef typename allocator::size_type size_type; -typedef typename list_type::iterator iterator; -typedef typename list_type::const_iterator const_iterator; - -/** -* Returns a read/write reference to the data of the most -* recently used entry. -*/ -reference front() -{ -return _data.front().second; -} - -/** -* Returns a read-only (constant) reference to the data of the -* most recently used entry. -*/ -const_reference front() const -{ -return _data.front().second; -} - -/** -* Returns a read/write reference to the data of the least -* recently used entry. -*/ -reference back() -{ -return _data.back().second; -} - -/** -* Returns a read-only (constant) reference to the data of the -* least recently used entry. -*/ -const_reference back (int i) const -{ -DUNE_UNUSED_PARAMETER(i); -return _data.back().second; -} - - -/** -* @brief Removes the first element. -*/ -void pop_front() -{ -key_type k = _data.front().first; -_data.pop_front(); -_index.erase(k); -} -/** -* @brief Removes the last element. -*/ -void pop_back() -{ -key_type k = _data.back().first; -_data.pop_back(); -_index.erase(k); -} - -/** -* @brief Finds the element whose key is k. -* -* @return iterator -*/ -iterator find (const key_type & key) -{ -const map_iterator it = _index.find(key); -if (it == _index.end()) return _data.end(); -return it->second; -} - -/** -* @brief Finds the element whose key is k. -* -* @return const_iterator -*/ -const_iterator find (const key_type & key) const -{ -const map_iterator it = _index.find(key); -if (it == _index.end()) return _data.end(); -return it->second; -} - -/** -* @brief Insert a value into the container -* -* Stores value under key and marks it as most recent. If this key -* is already present, the associated data is replaced. -* -* @param key associated with data -* @param data to store -* -* @return reference of stored data -*/ -reference insert (const key_type & key, const_reference data) -{ -std::pair<key_type, value_type> x(key, data); -/* insert item as mru */ -iterator it = _data.insert(_data.begin(), x); -/* store index */ -_index.insert(std::make_pair(key,it)); - -return it->second; -} - -/** -* @copydoc touch -*/ -reference insert (const key_type & key) -{ -return touch (key); -} - -/** -* @brief mark data associated with key as most recent -* -* @return reference of stored data -*/ -reference touch (const key_type & key) -{ -/* query _index for iterator */ -map_iterator it = _index.find(key); -if (it == _index.end()) -DUNE_THROW(Dune::RangeError, -"Failed to touch key " << key << ", it is not in the lru container"); -/* update _data -move it to the front -*/ -_data.splice(_data.begin(), _data, it->second); -return it->second->second; -} - -/** -* @brief Retrieve number of entries in the container -*/ -size_type size() const -{ -return _data.size(); -} - -/** -* @brief ensure a maximum size of the container -* -* If new_size is smaller than size the oldest elements are -* dropped. Otherwise nothing happens. -*/ -void resize(size_type new_size) -{ -assert(new_size <= size()); - -while (new_size < size()) -pop_back(); -} - -/** -* -*/ -void clear() -{ -_data.clear(); -_index.clear(); -} - -private: -list_type _data; -map_type _index; - -}; + namespace { + + /* + hide the default traits in an empty namespace + */ + template <typename Key, typename Tp, + typename Alloc = std::allocator<Tp> > + struct _lru_default_traits + { + typedef Key key_type; + typedef Alloc allocator; + typedef std::list< std::pair<Key, Tp> > list_type; + typedef typename list_type::iterator iterator; + typedef typename std::less<key_type> cmp; + typedef std::map< key_type, iterator, cmp, + typename std::allocator_traits<allocator>::template rebind_alloc<std::pair<const key_type, iterator> > > map_type; + }; + + } // end empty namespace + + /** + @brief LRU Cache Container + + Implementation of an LRU (least recently used) cache + container. This implementation follows the approach presented in + http://aim.adc.rmit.edu.au/phd/sgreuter/papers/graphite2003.pdf + */ + template <typename Key, typename Tp, + typename Traits = _lru_default_traits<Key, Tp> > + class lru + { + typedef typename Traits::list_type list_type; + typedef typename Traits::map_type map_type; + typedef typename Traits::allocator allocator; + typedef typename map_type::iterator map_iterator; + typedef typename map_type::const_iterator const_map_iterator; + + public: + typedef typename Traits::key_type key_type; + typedef typename allocator::value_type value_type; + using pointer = typename allocator::value_type*; + using const_pointer = typename allocator::value_type const*; + using const_reference = typename allocator::value_type const&; + using reference = typename allocator::value_type&; + typedef typename allocator::size_type size_type; + typedef typename list_type::iterator iterator; + typedef typename list_type::const_iterator const_iterator; + + /** + * Returns a read/write reference to the data of the most + * recently used entry. + */ + reference front() + { + return _data.front().second; + } + + /** + * Returns a read-only (constant) reference to the data of the + * most recently used entry. + */ + const_reference front() const + { + return _data.front().second; + } + + /** + * Returns a read/write reference to the data of the least + * recently used entry. + */ + reference back() + { + return _data.back().second; + } + + /** + * Returns a read-only (constant) reference to the data of the + * least recently used entry. + */ + const_reference back (int i) const + { + DUNE_UNUSED_PARAMETER(i); + return _data.back().second; + } + + + /** + * @brief Removes the first element. + */ + void pop_front() + { + key_type k = _data.front().first; + _data.pop_front(); + _index.erase(k); + } + /** + * @brief Removes the last element. + */ + void pop_back() + { + key_type k = _data.back().first; + _data.pop_back(); + _index.erase(k); + } + + /** + * @brief Finds the element whose key is k. + * + * @return iterator + */ + iterator find (const key_type & key) + { + const map_iterator it = _index.find(key); + if (it == _index.end()) return _data.end(); + return it->second; + } + + /** + * @brief Finds the element whose key is k. + * + * @return const_iterator + */ + const_iterator find (const key_type & key) const + { + const map_iterator it = _index.find(key); + if (it == _index.end()) return _data.end(); + return it->second; + } + + /** + * @brief Insert a value into the container + * + * Stores value under key and marks it as most recent. If this key + * is already present, the associated data is replaced. + * + * @param key associated with data + * @param data to store + * + * @return reference of stored data + */ + reference insert (const key_type & key, const_reference data) + { + std::pair<key_type, value_type> x(key, data); + /* insert item as mru */ + iterator it = _data.insert(_data.begin(), x); + /* store index */ + _index.insert(std::make_pair(key,it)); + + return it->second; + } + + /** + * @copydoc touch + */ + reference insert (const key_type & key) + { + return touch (key); + } + + /** + * @brief mark data associated with key as most recent + * + * @return reference of stored data + */ + reference touch (const key_type & key) + { + /* query _index for iterator */ + map_iterator it = _index.find(key); + if (it == _index.end()) + DUNE_THROW(Dune::RangeError, + "Failed to touch key " << key << ", it is not in the lru container"); + /* update _data + move it to the front + */ + _data.splice(_data.begin(), _data, it->second); + return it->second->second; + } + + /** + * @brief Retrieve number of entries in the container + */ + size_type size() const + { + return _data.size(); + } + + /** + * @brief ensure a maximum size of the container + * + * If new_size is smaller than size the oldest elements are + * dropped. Otherwise nothing happens. + */ + void resize(size_type new_size) + { + assert(new_size <= size()); + + while (new_size < size()) + pop_back(); + } + + /** + * + */ + void clear() + { + _data.clear(); + _index.clear(); + } + + private: + list_type _data; + map_type _index; + + }; } // namespace Dune diff --git a/dune/common/mallocallocator.hh b/dune/common/mallocallocator.hh index 2d3e21a8b390603f093d66e8d57878c4c7ada7e7..78cb3c86842adbbcfd69528135f116bb3b16b58c 100644 --- a/dune/common/mallocallocator.hh +++ b/dune/common/mallocallocator.hh @@ -11,108 +11,108 @@ #include <dune/common/unused.hh> /** -* @file -* @brief Allocators that use malloc/free. -*/ + * @file + * @brief Allocators that use malloc/free. + */ namespace Dune { -/** -@ingroup Allocators -@brief Allocators implementation which simply calls malloc/free -*/ -template <class T> -class MallocAllocator { -public: -typedef std::size_t size_type; -typedef std::ptrdiff_t difference_type; -typedef T* pointer; -typedef const T* const_pointer; -typedef T& reference; -typedef const T& const_reference; -typedef T value_type; -template <class U> struct rebind { -typedef MallocAllocator<U> other; -}; + /** + @ingroup Allocators + @brief Allocators implementation which simply calls malloc/free + */ + template <class T> + class MallocAllocator { + public: + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template <class U> struct rebind { + typedef MallocAllocator<U> other; + }; -//! create a new MallocAllocator -MallocAllocator() noexcept {} -//! copy construct from an other MallocAllocator, possibly for a different result type -template <class U> -MallocAllocator(const MallocAllocator<U>&) noexcept {} -//! cleanup this allocator -~MallocAllocator() noexcept {} + //! create a new MallocAllocator + MallocAllocator() noexcept {} + //! copy construct from an other MallocAllocator, possibly for a different result type + template <class U> + MallocAllocator(const MallocAllocator<U>&) noexcept {} + //! cleanup this allocator + ~MallocAllocator() noexcept {} -pointer address(reference x) const -{ -return &x; -} -const_pointer address(const_reference x) const -{ -return &x; -} + pointer address(reference x) const + { + return &x; + } + const_pointer address(const_reference x) const + { + return &x; + } -//! allocate n objects of type T -pointer allocate(size_type n, -const void* hint = 0) -{ -DUNE_UNUSED_PARAMETER(hint); -if (n > this->max_size()) -throw std::bad_alloc(); + //! allocate n objects of type T + pointer allocate(size_type n, + const void* hint = 0) + { + DUNE_UNUSED_PARAMETER(hint); + if (n > this->max_size()) + throw std::bad_alloc(); -pointer ret = static_cast<pointer>(std::malloc(n * sizeof(T))); -if (!ret) -throw std::bad_alloc(); -return ret; -} + pointer ret = static_cast<pointer>(std::malloc(n * sizeof(T))); + if (!ret) + throw std::bad_alloc(); + return ret; + } -//! deallocate n objects of type T at address p -void deallocate(pointer p, size_type n) -{ -DUNE_UNUSED_PARAMETER(n); -std::free(p); -} + //! deallocate n objects of type T at address p + void deallocate(pointer p, size_type n) + { + DUNE_UNUSED_PARAMETER(n); + std::free(p); + } -//! max size for allocate -size_type max_size() const noexcept -{ -return size_type(-1) / sizeof(T); -} + //! max size for allocate + size_type max_size() const noexcept + { + return size_type(-1) / sizeof(T); + } -//! copy-construct an object of type T (i.e. make a placement new on p) -void construct(pointer p, const T& val) -{ -::new((void*)p)T(val); -} + //! copy-construct an object of type T (i.e. make a placement new on p) + void construct(pointer p, const T& val) + { + ::new((void*)p)T(val); + } -//! construct an object of type T from variadic parameters -template<typename ... Args> -void construct(pointer p, Args&&... args) -{ -::new((void *)p)T(std::forward<Args>(args) ...); -} + //! construct an object of type T from variadic parameters + template<typename ... Args> + void construct(pointer p, Args&&... args) + { + ::new((void *)p)T(std::forward<Args>(args) ...); + } -//! destroy an object of type T (i.e. call the destructor) -void destroy(pointer p) -{ -p->~T(); -} -}; + //! destroy an object of type T (i.e. call the destructor) + void destroy(pointer p) + { + p->~T(); + } + }; -//! check whether allocators are equivalent -template<class T> -constexpr bool -operator==(const MallocAllocator<T> &, const MallocAllocator<T> &) -{ -return true; -} + //! check whether allocators are equivalent + template<class T> + constexpr bool + operator==(const MallocAllocator<T> &, const MallocAllocator<T> &) + { + return true; + } -//! check whether allocators are not equivalent -template<class T> -constexpr bool -operator!=(const MallocAllocator<T> &, const MallocAllocator<T> &) -{ -return false; -} + //! check whether allocators are not equivalent + template<class T> + constexpr bool + operator!=(const MallocAllocator<T> &, const MallocAllocator<T> &) + { + return false; + } } #endif // DUNE_MALLOC_ALLOCATOR_HH diff --git a/dune/common/math.hh b/dune/common/math.hh index 42f32bba42548a9bb7d1a192585561028e3a7739..bd62938dc729aabd7d20ee4b3510c332b2cafb82 100644 --- a/dune/common/math.hh +++ b/dune/common/math.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Some useful basic math stuff -*/ + * \brief Some useful basic math stuff + */ #include <cmath> #include <complex> @@ -18,348 +18,348 @@ namespace Dune { -/** -\brief Standard implementation of MathematicalConstants. - -This implementation will work with all built-in floating point -types. It provides - -* e as exp(1.0) -* pi as acos(-1.0) - -*/ -template< class T > -struct StandardMathematicalConstants -{ -/** -* \brief Euler's number -*/ -static const T e () -{ -using std::exp; -static const T e = exp( T( 1 ) ); -return e; -} - -/** -* \brief Archimedes' constant -*/ -static const T pi () -{ -using std::acos; -static const T pi = acos( T( -1 ) ); -return pi; -} -}; - - -/** -\brief Provides commonly used mathematical constants. - -a struct that is specialized for types repesenting real or complex -numbers. It provides commonly used mathematical constants with the -required accuary for the specified type. -*/ -template< class Field > -struct MathematicalConstants -: public StandardMathematicalConstants<Field> -{}; - - -/** \brief Power method for integer exponents -* -* \note Make sure that Mantissa is a non-integer type when using negative exponents! -*/ -template <class Mantissa, class Exponent> -constexpr Mantissa power(Mantissa m, Exponent p) -{ -static_assert(std::numeric_limits<Exponent>::is_integer, "Exponent must be an integer type!"); - -auto result = Mantissa(1); -auto absp = (p<0) ? -p : p; // This is simply abs, but std::abs is not constexpr -for (Exponent i = Exponent(0); i<absp; i++) -result *= m; - -if (p<0) -result = Mantissa(1)/result; - -return result; -} - -//! Calculates the factorial of m at compile time -template <int m> -struct Factorial -{ -//! factorial stores m! -enum { factorial = m * Factorial<m-1>::factorial }; -}; - -//! end of recursion of factorial via specialization -template <> -struct Factorial<0> -{ -// 0! = 1 -enum { factorial = 1 }; -}; - - -//! calculate the factorial of n as a constexpr -// T has to be an integral type -template<class T> -constexpr inline static T factorial(const T& n) noexcept -{ -static_assert(std::numeric_limits<T>::is_integer, "`factorial(n)` has to be called with an integer type."); -T fac = 1; -for(T k = 0; k < n; ++k) -fac *= k+1; -return fac; -} - -//! calculate the factorial of n as a constexpr -template<class T, T n> -constexpr inline static auto factorial (std::integral_constant<T, n>) noexcept -{ -return std::integral_constant<T, factorial(n)>{}; -} - - -//! calculate the binomial coefficient n over k as a constexpr -// T has to be an integral type -template<class T> -constexpr inline static T binomial (const T& n, const T& k) noexcept -{ -static_assert(std::numeric_limits<T>::is_integer, "`binomial(n, k)` has to be called with an integer type."); - -if( k < 0 || k > n ) -return 0; - -if (2*k > n) -return binomial(n, n-k); - -T bin = 1; -for(auto i = n-k; i < n; ++i) -bin *= i+1; -return bin / factorial(k); -} - -//! calculate the binomial coefficient n over k as a constexpr -template<class T, T n, T k> -constexpr inline static auto binomial (std::integral_constant<T, n>, std::integral_constant<T, k>) noexcept -{ -return std::integral_constant<T, binomial(n, k)>{}; -} - -template<class T, T n> -constexpr inline static auto binomial (std::integral_constant<T, n>, std::integral_constant<T, n>) noexcept -{ -return std::integral_constant<T, (n >= 0 ? 1 : 0)>{}; -} - - -//! compute conjugate complex of x -// conjugate complex does nothing for non-complex types -template<class K> -inline K conjugateComplex (const K& x) -{ -return x; -} + /** + \brief Standard implementation of MathematicalConstants. + + This implementation will work with all built-in floating point + types. It provides + + * e as exp(1.0) + * pi as acos(-1.0) + + */ + template< class T > + struct StandardMathematicalConstants + { + /** + * \brief Euler's number + */ + static const T e () + { + using std::exp; + static const T e = exp( T( 1 ) ); + return e; + } + + /** + * \brief Archimedes' constant + */ + static const T pi () + { + using std::acos; + static const T pi = acos( T( -1 ) ); + return pi; + } + }; + + + /** + \brief Provides commonly used mathematical constants. + + a struct that is specialized for types repesenting real or complex + numbers. It provides commonly used mathematical constants with the + required accuary for the specified type. + */ + template< class Field > + struct MathematicalConstants + : public StandardMathematicalConstants<Field> + {}; + + + /** \brief Power method for integer exponents + * + * \note Make sure that Mantissa is a non-integer type when using negative exponents! + */ + template <class Mantissa, class Exponent> + constexpr Mantissa power(Mantissa m, Exponent p) + { + static_assert(std::numeric_limits<Exponent>::is_integer, "Exponent must be an integer type!"); + + auto result = Mantissa(1); + auto absp = (p<0) ? -p : p; // This is simply abs, but std::abs is not constexpr + for (Exponent i = Exponent(0); i<absp; i++) + result *= m; + + if (p<0) + result = Mantissa(1)/result; + + return result; + } + + //! Calculates the factorial of m at compile time + template <int m> + struct Factorial + { + //! factorial stores m! + enum { factorial = m * Factorial<m-1>::factorial }; + }; + + //! end of recursion of factorial via specialization + template <> + struct Factorial<0> + { + // 0! = 1 + enum { factorial = 1 }; + }; + + + //! calculate the factorial of n as a constexpr + // T has to be an integral type + template<class T> + constexpr inline static T factorial(const T& n) noexcept + { + static_assert(std::numeric_limits<T>::is_integer, "`factorial(n)` has to be called with an integer type."); + T fac = 1; + for(T k = 0; k < n; ++k) + fac *= k+1; + return fac; + } + + //! calculate the factorial of n as a constexpr + template<class T, T n> + constexpr inline static auto factorial (std::integral_constant<T, n>) noexcept + { + return std::integral_constant<T, factorial(n)>{}; + } + + + //! calculate the binomial coefficient n over k as a constexpr + // T has to be an integral type + template<class T> + constexpr inline static T binomial (const T& n, const T& k) noexcept + { + static_assert(std::numeric_limits<T>::is_integer, "`binomial(n, k)` has to be called with an integer type."); + + if( k < 0 || k > n ) + return 0; + + if (2*k > n) + return binomial(n, n-k); + + T bin = 1; + for(auto i = n-k; i < n; ++i) + bin *= i+1; + return bin / factorial(k); + } + + //! calculate the binomial coefficient n over k as a constexpr + template<class T, T n, T k> + constexpr inline static auto binomial (std::integral_constant<T, n>, std::integral_constant<T, k>) noexcept + { + return std::integral_constant<T, binomial(n, k)>{}; + } + + template<class T, T n> + constexpr inline static auto binomial (std::integral_constant<T, n>, std::integral_constant<T, n>) noexcept + { + return std::integral_constant<T, (n >= 0 ? 1 : 0)>{}; + } + + + //! compute conjugate complex of x + // conjugate complex does nothing for non-complex types + template<class K> + inline K conjugateComplex (const K& x) + { + return x; + } #ifndef DOXYGEN -// specialization for complex -template<class K> -inline std::complex<K> conjugateComplex (const std::complex<K>& c) -{ -return std::complex<K>(c.real(),-c.imag()); -} + // specialization for complex + template<class K> + inline std::complex<K> conjugateComplex (const std::complex<K>& c) + { + return std::complex<K>(c.real(),-c.imag()); + } #endif -//! Return the sign of the value -template <class T> -int sign(const T& val) -{ -return (val < 0 ? -1 : 1); -} + //! Return the sign of the value + template <class T> + int sign(const T& val) + { + return (val < 0 ? -1 : 1); + } -namespace Impl { -// Returns whether a given type behaves like std::complex<>, i.e. whether -// real() and imag() are defined -template<class T> -struct isComplexLike { -private: -template<class U> -static auto test(U* u) -> decltype(u->real(), u->imag(), std::true_type()); + namespace Impl { + // Returns whether a given type behaves like std::complex<>, i.e. whether + // real() and imag() are defined + template<class T> + struct isComplexLike { + private: + template<class U> + static auto test(U* u) -> decltype(u->real(), u->imag(), std::true_type()); -template<class U> -static auto test(...) -> decltype(std::false_type()); + template<class U> + static auto test(...) -> decltype(std::false_type()); -public: -static const bool value = decltype(test<T>(0))::value; -}; -} // namespace Impl + public: + static const bool value = decltype(test<T>(0))::value; + }; + } // namespace Impl -//! namespace for customization of math functions with Dune-Semantics -/** -You can add overloads for the Dune-semantics of math-functions in this -namespace. These overloads will be used by functors like `Dune::isNaN` -to implement these functions, and will be preferred over functions found -by ADL, or the corresponding functions from the standard (whether they -are found by ADL or in the namespace `std`. + //! namespace for customization of math functions with Dune-Semantics + /** + You can add overloads for the Dune-semantics of math-functions in this + namespace. These overloads will be used by functors like `Dune::isNaN` + to implement these functions, and will be preferred over functions found + by ADL, or the corresponding functions from the standard (whether they + are found by ADL or in the namespace `std`. -PriorityTag -=========== + PriorityTag + =========== -There are two predefined priorities: + There are two predefined priorities: -<1> provides a default implementation, only applicable if the -camelCase-Version of the function (e.g. `isNaN`) can be found via ADL -for an argument of type `T`. (Otherwise the overload should not -participate in overload resolution.) + <1> provides a default implementation, only applicable if the + camelCase-Version of the function (e.g. `isNaN`) can be found via ADL + for an argument of type `T`. (Otherwise the overload should not + participate in overload resolution.) -<0> provides a default implementation that forwards the call to the -lower-case version of the function (e.g. `isnan`), found via ADL and -the namespace `std`. + <0> provides a default implementation that forwards the call to the + lower-case version of the function (e.g. `isnan`), found via ADL and + the namespace `std`. -Any higher priority up to 10 can be used by other overloads. -*/ -namespace MathOverloads { + Any higher priority up to 10 can be used by other overloads. + */ + namespace MathOverloads { -//! Tag to make sure the functions in this namespace can be found by ADL. -struct ADLTag {}; + //! Tag to make sure the functions in this namespace can be found by ADL. + struct ADLTag {}; #define DUNE_COMMON_MATH_ISFUNCTION(function, stdfunction) \ -template<class T> \ -auto function(const T &t, PriorityTag<1>, ADLTag) \ --> decltype(function(t)) { \ -return function(t); \ -} \ -template<class T> \ -auto function(const T &t, PriorityTag<0>, ADLTag) { \ -using std::stdfunction; \ -return stdfunction(t); \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_COMMON_MATH_ISFUNCTION(isNaN,isnan); -DUNE_COMMON_MATH_ISFUNCTION(isInf,isinf); -DUNE_COMMON_MATH_ISFUNCTION(isFinite,isfinite); + template<class T> \ + auto function(const T &t, PriorityTag<1>, ADLTag) \ + -> decltype(function(t)) { \ + return function(t); \ + } \ + template<class T> \ + auto function(const T &t, PriorityTag<0>, ADLTag) { \ + using std::stdfunction; \ + return stdfunction(t); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_COMMON_MATH_ISFUNCTION(isNaN,isnan); + DUNE_COMMON_MATH_ISFUNCTION(isInf,isinf); + DUNE_COMMON_MATH_ISFUNCTION(isFinite,isfinite); #undef DUNE_COMMON_MATH_ISFUNCTION -template<class T> -auto isUnordered(const T &t1, const T &t2, PriorityTag<1>, ADLTag) --> decltype(isUnordered(t1, t2)) { -return isUnordered(t1, t2); -} - -template<class T> -auto isUnordered(const T &t1, const T &t2, PriorityTag<0>, ADLTag) { -using std::isunordered; -return isunordered(t1, t2); -} -} - -namespace MathImpl { - -// NOTE: it is important that these functors have names different from the -// names of the functions they are forwarding to. Otherwise the -// unqualified call would find the functor type, not a function, and ADL -// would never be attempted. + template<class T> + auto isUnordered(const T &t1, const T &t2, PriorityTag<1>, ADLTag) + -> decltype(isUnordered(t1, t2)) { + return isUnordered(t1, t2); + } + + template<class T> + auto isUnordered(const T &t1, const T &t2, PriorityTag<0>, ADLTag) { + using std::isunordered; + return isunordered(t1, t2); + } + } + + namespace MathImpl { + + // NOTE: it is important that these functors have names different from the + // names of the functions they are forwarding to. Otherwise the + // unqualified call would find the functor type, not a function, and ADL + // would never be attempted. #define DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(function) \ -struct function##Impl { \ -template<class T> \ -constexpr auto operator()(const T &t) const { \ -return function(t, PriorityTag<10>{}, MathOverloads::ADLTag{}); \ -} \ -}; \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isNaN); -DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isInf); -DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isFinite); + struct function##Impl { \ + template<class T> \ + constexpr auto operator()(const T &t) const { \ + return function(t, PriorityTag<10>{}, MathOverloads::ADLTag{}); \ + } \ + }; \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isNaN); + DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isInf); + DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR(isFinite); #undef DUNE_COMMON_MATH_ISFUNCTION_FUNCTOR -struct isUnorderedImpl { -template<class T> -constexpr auto operator()(const T &t1, const T &t2) const { -return isUnordered(t1, t2, PriorityTag<10>{}, MathOverloads::ADLTag{}); -} -}; - -} //MathImpl - - -namespace Impl { -/* This helper has a math functor as a static constexpr member. Doing -this as a static member of a template struct means we can do this -without violating the ODR or putting the definition into a seperate -compilation unit, while still still ensuring the functor is the same -lvalue across all compilation units. -*/ -template<class T> -struct MathDummy -{ -static constexpr T value{}; -}; - -template<class T> -constexpr T MathDummy<T>::value; - -} //namespace Impl - -namespace { -/* Provide the math functors directly in the `Dune` namespace. - -This actually declares a different name in each translation unit, but -they all resolve to the same lvalue. -*/ - -//! check wether the argument is NaN -/** -* Dune-Semantic: for multi-valued types (complex, vectors), check whether -* *any* value is NaN. -*/ -constexpr auto const &isNaN = Impl::MathDummy<MathImpl::isNaNImpl>::value; - -//! check wether the argument is infinite or NaN -/** -* Dune-Semantic: for multi-valued types (complex, vectors), check whether -* *any* value is infinite or NaN. -*/ -constexpr auto const &isInf = Impl::MathDummy<MathImpl::isInfImpl>::value; - -//! check wether the argument is finite and non-NaN -/** -* Dune-Semantic: for multi-valued types (complex, vectors), check whether -* *all* values are finite and non-NaN. -*/ -constexpr auto const &isFinite = Impl::MathDummy<MathImpl::isFiniteImpl>::value; - -//! check wether the arguments are ordered -/** -* Dune-Semantic: for multi-valued types (complex, vectors), there is -* never an ordering, so at the moment these types are not supported as -* arguments. -*/ -constexpr auto const &isUnordered = Impl::MathDummy<MathImpl::isUnorderedImpl>::value; -} - -namespace MathOverloads { -/*Overloads for complex types*/ -template<class T, class = std::enable_if_t<Impl::isComplexLike<T>::value> > -auto isNaN(const T &t, PriorityTag<2>, ADLTag) { -return Dune::isNaN(real(t)) || Dune::isNaN(imag(t)); -} - -template<class T, class = std::enable_if_t<Impl::isComplexLike<T>::value> > -auto isInf(const T &t, PriorityTag<2>, ADLTag) { -return Dune::isInf(real(t)) || Dune::isInf(imag(t)); -} - -template<class T, class = std::enable_if_t<Impl::isComplexLike<T>::value> > -auto isFinite(const T &t, PriorityTag<2>, ADLTag) { -return Dune::isFinite(real(t)) && Dune::isFinite(imag(t)); -} -} //MathOverloads + struct isUnorderedImpl { + template<class T> + constexpr auto operator()(const T &t1, const T &t2) const { + return isUnordered(t1, t2, PriorityTag<10>{}, MathOverloads::ADLTag{}); + } + }; + + } //MathImpl + + + namespace Impl { + /* This helper has a math functor as a static constexpr member. Doing + this as a static member of a template struct means we can do this + without violating the ODR or putting the definition into a seperate + compilation unit, while still still ensuring the functor is the same + lvalue across all compilation units. + */ + template<class T> + struct MathDummy + { + static constexpr T value{}; + }; + + template<class T> + constexpr T MathDummy<T>::value; + + } //namespace Impl + + namespace { + /* Provide the math functors directly in the `Dune` namespace. + + This actually declares a different name in each translation unit, but + they all resolve to the same lvalue. + */ + + //! check wether the argument is NaN + /** + * Dune-Semantic: for multi-valued types (complex, vectors), check whether + * *any* value is NaN. + */ + constexpr auto const &isNaN = Impl::MathDummy<MathImpl::isNaNImpl>::value; + + //! check wether the argument is infinite or NaN + /** + * Dune-Semantic: for multi-valued types (complex, vectors), check whether + * *any* value is infinite or NaN. + */ + constexpr auto const &isInf = Impl::MathDummy<MathImpl::isInfImpl>::value; + + //! check wether the argument is finite and non-NaN + /** + * Dune-Semantic: for multi-valued types (complex, vectors), check whether + * *all* values are finite and non-NaN. + */ + constexpr auto const &isFinite = Impl::MathDummy<MathImpl::isFiniteImpl>::value; + + //! check wether the arguments are ordered + /** + * Dune-Semantic: for multi-valued types (complex, vectors), there is + * never an ordering, so at the moment these types are not supported as + * arguments. + */ + constexpr auto const &isUnordered = Impl::MathDummy<MathImpl::isUnorderedImpl>::value; + } + + namespace MathOverloads { + /*Overloads for complex types*/ + template<class T, class = std::enable_if_t<Impl::isComplexLike<T>::value> > + auto isNaN(const T &t, PriorityTag<2>, ADLTag) { + return Dune::isNaN(real(t)) || Dune::isNaN(imag(t)); + } + + template<class T, class = std::enable_if_t<Impl::isComplexLike<T>::value> > + auto isInf(const T &t, PriorityTag<2>, ADLTag) { + return Dune::isInf(real(t)) || Dune::isInf(imag(t)); + } + + template<class T, class = std::enable_if_t<Impl::isComplexLike<T>::value> > + auto isFinite(const T &t, PriorityTag<2>, ADLTag) { + return Dune::isFinite(real(t)) && Dune::isFinite(imag(t)); + } + } //MathOverloads } #endif // #ifndef DUNE_MATH_HH diff --git a/dune/common/matvectraits.hh b/dune/common/matvectraits.hh index 207caaa8346446c31d6f63f22be7f64a56951ad9..0fba897657bdc101f9618985fb9bbf4bfbc57c89 100644 --- a/dune/common/matvectraits.hh +++ b/dune/common/matvectraits.hh @@ -5,29 +5,29 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Documentation of the traits classes you need to write for each implementation of DenseVector or DenseMatrix -*/ + * \brief Documentation of the traits classes you need to write for each implementation of DenseVector or DenseMatrix + */ namespace Dune { -/** -@addtogroup DenseMatVec -\brief Type Traits to retrieve types associated with an implementation of Dune::DenseVector or Dune::DenseMatrix + /** + @addtogroup DenseMatVec + \brief Type Traits to retrieve types associated with an implementation of Dune::DenseVector or Dune::DenseMatrix -you have to specialize this class for every implementation of DenseVector or DenseMatrix. + you have to specialize this class for every implementation of DenseVector or DenseMatrix. -\code -//! export the type of the derived class (e.g. FieldVector<K,SIZE>) -typedef ... derived_type; -//! export the type of the stored values -typedef ... value_type; -//! export the type representing the size information -typedef ... size_type; -\endcode + \code + //! export the type of the derived class (e.g. FieldVector<K,SIZE>) + typedef ... derived_type; + //! export the type of the stored values + typedef ... value_type; + //! export the type representing the size information + typedef ... size_type; + \endcode -*/ -template<class T> -struct DenseMatVecTraits {}; + */ + template<class T> + struct DenseMatVecTraits {}; } // end namespace Dune diff --git a/dune/common/overloadset.hh b/dune/common/overloadset.hh index 1a671a8b73fdf938921b006f604e15bb90eb9d77..6819727a392a1c69a62b67e285a14758c28d2ab5 100644 --- a/dune/common/overloadset.hh +++ b/dune/common/overloadset.hh @@ -17,58 +17,58 @@ namespace Impl { #if __cpp_variadic_using >= 201611 -template<typename... F> -class OverloadSet -: public F... -{ + template<typename... F> + class OverloadSet + : public F... + { -public: + public: -template<typename... FF> -OverloadSet(FF&&... ff) -: F(std::forward<FF>(ff))... -{} + template<typename... FF> + OverloadSet(FF&&... ff) + : F(std::forward<FF>(ff))... + {} -using F::operator()...; + using F::operator()...; -}; + }; #else // __cpp_variadic_using >= 201611 -// This overload set derives from -// all passed functions. Since we -// cannot do argument pack expansion -// on using statements this is done recursively. -template<class F0, class... F> -class OverloadSet: public OverloadSet<F...>, F0 -{ -using Base = OverloadSet<F...>; -public: - -template<class FF0, class... FF> -OverloadSet(FF0&& f0, FF&&... ff) : -Base(std::forward<FF>(ff)...), -F0(std::forward<FF0>(f0)) -{} - -// pull in operator() of F0 and of all F... via the base class -using F0::operator(); -using Base::operator(); -}; - -template<class F0> -class OverloadSet<F0>: public F0 -{ -public: - -template<class FF0> -OverloadSet(FF0&& f0) : -F0(std::forward<FF0>(f0)) -{} - -// pull in operator() of F0 -using F0::operator(); -}; + // This overload set derives from + // all passed functions. Since we + // cannot do argument pack expansion + // on using statements this is done recursively. + template<class F0, class... F> + class OverloadSet: public OverloadSet<F...>, F0 + { + using Base = OverloadSet<F...>; + public: + + template<class FF0, class... FF> + OverloadSet(FF0&& f0, FF&&... ff) : + Base(std::forward<FF>(ff)...), + F0(std::forward<FF0>(f0)) + {} + + // pull in operator() of F0 and of all F... via the base class + using F0::operator(); + using Base::operator(); + }; + + template<class F0> + class OverloadSet<F0>: public F0 + { + public: + + template<class FF0> + OverloadSet(FF0&& f0) : + F0(std::forward<FF0>(f0)) + {} + + // pull in operator() of F0 + using F0::operator(); + }; #endif // __cpp_variadic_using >= 201611 @@ -77,133 +77,133 @@ using F0::operator(); /** -* \brief Create an overload set -* -* \tparam F List of function object types -* \param f List of function objects -* -* This returns an object that contains all -* operator() implementations of the passed -* functions. All those are available when -* calling operator() of the returned object. -* -* The returned object derives from -* those implementations such that it contains -* all operator() implementations in its -* overload set. When calling operator() -* this will select the best overload. -* If multiple overload are equally good this -* will lead to ambiguity. -* -* Notice that the passed function objects are -* stored by value and must be copy-constructible. -* -* On gcc 5 and gcc 6 mixing templated overloads -* (i.e. using auto-parameter) and non-templated -* ones may not compile if both they are captureless -* lambdas. The problem can be avoided by capturing -* a dummy value. -* -* \ingroup CxxUtilities -*/ + * \brief Create an overload set + * + * \tparam F List of function object types + * \param f List of function objects + * + * This returns an object that contains all + * operator() implementations of the passed + * functions. All those are available when + * calling operator() of the returned object. + * + * The returned object derives from + * those implementations such that it contains + * all operator() implementations in its + * overload set. When calling operator() + * this will select the best overload. + * If multiple overload are equally good this + * will lead to ambiguity. + * + * Notice that the passed function objects are + * stored by value and must be copy-constructible. + * + * On gcc 5 and gcc 6 mixing templated overloads + * (i.e. using auto-parameter) and non-templated + * ones may not compile if both they are captureless + * lambdas. The problem can be avoided by capturing + * a dummy value. + * + * \ingroup CxxUtilities + */ template<class... F> auto overload(F&&... f) { -return Impl::OverloadSet<std::decay_t<F>...>(std::forward<F>(f)...); + return Impl::OverloadSet<std::decay_t<F>...>(std::forward<F>(f)...); } namespace Impl { -template<class F0, class... F> -class OrderedOverloadSet: public OrderedOverloadSet<F...>, F0 -{ -using Base = OrderedOverloadSet<F...>; -public: - -template<class FF0, class... FF> -OrderedOverloadSet(FF0&& f0, FF&&... ff) : -Base(std::forward<FF>(ff)...), -F0(std::forward<FF0>(f0)) -{} - -// Forward to operator() of F0 if it can be called with the given arguments. -template<class... Args, -std::enable_if_t<Std::is_callable<F0(Args&&...)>::value, int> = 0> -decltype(auto) operator()(Args&&... args) -{ -return F0::operator()(std::forward<Args>(args)...); -} - -// Forward to operator() of base class if F0 cannot be called with the given -// arguments. In this case the base class will successively try operator() -// of all F... . -template<class... Args, -std::enable_if_t< not Std::is_callable<F0(Args&&...)>::value, int> = 0> -decltype(auto) operator()(Args&&... args) -{ -return Base::operator()(std::forward<Args>(args)...); -} - -}; - -template<class F0> -class OrderedOverloadSet<F0>: public F0 -{ -public: - -template<class FF0> -OrderedOverloadSet(FF0&& f0) : -F0(std::forward<FF0>(f0)) -{} - -// Forward to operator() of F0. If it cannot be called with -// the given arguments a static assertion will fail. -template<class... Args> -decltype(auto) operator()(Args&&... args) -{ -static_assert(Std::is_callable<F0(Args&&...)>::value, "No matching overload found in OrderedOverloadSet"); -return F0::operator()(std::forward<Args>(args)...); -} -}; + template<class F0, class... F> + class OrderedOverloadSet: public OrderedOverloadSet<F...>, F0 + { + using Base = OrderedOverloadSet<F...>; + public: + + template<class FF0, class... FF> + OrderedOverloadSet(FF0&& f0, FF&&... ff) : + Base(std::forward<FF>(ff)...), + F0(std::forward<FF0>(f0)) + {} + + // Forward to operator() of F0 if it can be called with the given arguments. + template<class... Args, + std::enable_if_t<Std::is_callable<F0(Args&&...)>::value, int> = 0> + decltype(auto) operator()(Args&&... args) + { + return F0::operator()(std::forward<Args>(args)...); + } + + // Forward to operator() of base class if F0 cannot be called with the given + // arguments. In this case the base class will successively try operator() + // of all F... . + template<class... Args, + std::enable_if_t< not Std::is_callable<F0(Args&&...)>::value, int> = 0> + decltype(auto) operator()(Args&&... args) + { + return Base::operator()(std::forward<Args>(args)...); + } + + }; + + template<class F0> + class OrderedOverloadSet<F0>: public F0 + { + public: + + template<class FF0> + OrderedOverloadSet(FF0&& f0) : + F0(std::forward<FF0>(f0)) + {} + + // Forward to operator() of F0. If it cannot be called with + // the given arguments a static assertion will fail. + template<class... Args> + decltype(auto) operator()(Args&&... args) + { + static_assert(Std::is_callable<F0(Args&&...)>::value, "No matching overload found in OrderedOverloadSet"); + return F0::operator()(std::forward<Args>(args)...); + } + }; } // end namespace Impl /** -* \brief Create an ordered overload set -* -* \tparam F List of function object types -* \param f List of function objects -* -* This returns an object that contains all -* operator() implementations of the passed -* functions. All those are available when -* calling operator() of the returned object. -* -* In contrast to overload() these overloads -* are ordered in the sense that the first -* matching overload for the given arguments -* is selected and later ones are ignored. -* Hence such a call is never ambiguous. -* -* Notice that the passed function objects are -* stored by value and must be copy-constructible. -* -* On gcc 5 and gcc 6 mixing templated overloads -* (i.e. using auto-parameter) and non-templated -* ones may not compile if both they are captureless -* lambdas. The problem can be avoided by capturing -* a dummy value. -* -* \ingroup CxxUtilities -*/ + * \brief Create an ordered overload set + * + * \tparam F List of function object types + * \param f List of function objects + * + * This returns an object that contains all + * operator() implementations of the passed + * functions. All those are available when + * calling operator() of the returned object. + * + * In contrast to overload() these overloads + * are ordered in the sense that the first + * matching overload for the given arguments + * is selected and later ones are ignored. + * Hence such a call is never ambiguous. + * + * Notice that the passed function objects are + * stored by value and must be copy-constructible. + * + * On gcc 5 and gcc 6 mixing templated overloads + * (i.e. using auto-parameter) and non-templated + * ones may not compile if both they are captureless + * lambdas. The problem can be avoided by capturing + * a dummy value. + * + * \ingroup CxxUtilities + */ template<class... F> auto orderedOverload(F&&... f) { -return Impl::OrderedOverloadSet<std::decay_t<F>...>(std::forward<F>(f)...); + return Impl::OrderedOverloadSet<std::decay_t<F>...>(std::forward<F>(f)...); } diff --git a/dune/common/parallel/communication.hh b/dune/common/parallel/communication.hh index 9b51e2668e41ec0b58e66cccd0ee3236d39bb3d9..9c20c4d8c8347b8a3917ed6fe821c7be76651918 100644 --- a/dune/common/parallel/communication.hh +++ b/dune/common/parallel/communication.hh @@ -4,12 +4,12 @@ #define DUNE_COMMON_PARALLEL_COMMUNICATION_HH #include <dune/internal/dune-common.hh> /*! -\file -\brief Implements an utility class that provides -collective communication methods for sequential programs. + \file + \brief Implements an utility class that provides + collective communication methods for sequential programs. -\ingroup ParallelCommunication -*/ + \ingroup ParallelCommunication + */ #include <iostream> #include <complex> #include <algorithm> @@ -21,533 +21,533 @@ collective communication methods for sequential programs. #include <dune/common/parallel/future.hh> /*! \defgroup ParallelCommunication Parallel Communication -\ingroup Common + \ingroup Common -\brief Abstractions for parallel computing + \brief Abstractions for parallel computing -Dune offers an abstraction to the basic methods of parallel -communication. It allows one to switch parallel features on and off, -without changing the code. This is done using either Communication -or MPICommunication. + Dune offers an abstraction to the basic methods of parallel + communication. It allows one to switch parallel features on and off, + without changing the code. This is done using either Communication + or MPICommunication. -*/ + */ /*! -\file -\brief An abstraction to the basic methods of parallel communication, -following the message-passing paradigm. -\ingroup ParallelCommunication -*/ + \file + \brief An abstraction to the basic methods of parallel communication, + following the message-passing paradigm. + \ingroup ParallelCommunication + */ namespace Dune { -/* define some type that definitely differs from MPI_Comm */ -struct No_Comm {}; - -/*! @brief Comparison operator for MPI compatibility - -Always returns true. -*/ -inline bool operator==(const No_Comm&, const No_Comm&) -{ -return true; -} - -/*! @brief Comparison operator for MPI compatibility - -Always returns false. -*/ -inline bool operator!=(const No_Comm&, const No_Comm&) -{ -return false; -} - -/*! @brief Collective communication interface and sequential default implementation - -Communication offers an abstraction to the basic methods -of parallel communication, following the message-passing -paradigm. It allows one to switch parallel features on and off, without -changing the code. Currently only MPI and sequential code are -supported. - -A Communication object is returned by all grids (also -the sequential ones) in order to allow code to be written in -a transparent way for sequential and parallel grids. - -This class provides a default implementation for sequential grids. -The number of processes involved is 1, any sum, maximum, etc. returns -just its input argument and so on. - -In specializations one can implement the real thing using appropriate -communication functions, e.g. there exists an implementation using -the Message Passing %Interface (MPI), see Dune::Communication<MPI_Comm>. - -Moreover, the communication subsystem used by an implementation -is not visible in the interface, i.e. Dune grid implementations -are not restricted to MPI. - -\tparam Communicator The communicator type used by your message-passing implementation. -For MPI this will be MPI_Comm. For sequential codes there is the dummy communicator No_Comm. -It is assumed that if you want to specialize the Communication class for a -message-passing system other than MPI, that message-passing system will have something -equivalent to MPI communicators. - -\ingroup ParallelCommunication -*/ -template<typename Communicator> -class Communication -{ -public: -//! Construct default object -Communication() -{} - -/** \brief Constructor with a given communicator -* -* As this is implementation for the sequential setting, the communicator is a dummy and simply discarded. -*/ -Communication (const Communicator&) -{} - -//! Return rank, is between 0 and size()-1 -int rank () const -{ -return 0; -} - -//! cast to the underlying Fake MPI communicator -operator No_Comm() const -{ -return {}; -} - -//! Number of processes in set, is greater than 0 -int size () const -{ -return 1; -} - -/** @brief Sends the data to the dest_rank -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<class T> -int send(const T& data, int dest_rank, int tag){ -DUNE_UNUSED_PARAMETER(data); -DUNE_UNUSED_PARAMETER(dest_rank); -DUNE_UNUSED_PARAMETER(tag); -DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); -} - -/** @brief Sends the data to the dest_rank nonblocking -@returns Future<T> containing the send buffer, completes when data is send -*/ -template<class T> -PseudoFuture<T> isend(const T&& data, int dest_rank, int tag){ -DUNE_UNUSED_PARAMETER(data); -DUNE_UNUSED_PARAMETER(dest_rank); -DUNE_UNUSED_PARAMETER(tag); -DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); -} - -/** @brief Receives the data from the source_rank -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<class T> -T recv(T&& data, int source_rank, int tag, void* status = 0){ -DUNE_UNUSED_PARAMETER(data); -DUNE_UNUSED_PARAMETER(source_rank); -DUNE_UNUSED_PARAMETER(tag); -DUNE_UNUSED_PARAMETER(status); -DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); -} - -/** @brief Receives the data from the source_rank nonblocking -@returns Future<T> containing the received data when complete -*/ -template<class T> -PseudoFuture<T> irecv(T&& data, int source_rank, int tag){ -DUNE_UNUSED_PARAMETER(data); -DUNE_UNUSED_PARAMETER(source_rank); -DUNE_UNUSED_PARAMETER(tag); -DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); -} - -template<class T> -T rrecv(T&& data, int source_rank, int tag, void* status = 0) const -{ -DUNE_UNUSED_PARAMETER(data); -DUNE_UNUSED_PARAMETER(source_rank); -DUNE_UNUSED_PARAMETER(tag); -DUNE_UNUSED_PARAMETER(status); -DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); -} -/** @brief Compute the sum of the argument over all processes and -return the result in every process. Assumes that T has an operator+ -*/ -template<typename T> -T sum (const T& in) const -{ -return in; -} - -/** @brief Compute the sum over all processes for each component of an array and return the result -in every process. Assumes that T has an operator+ - -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int sum (T* inout, int len) const -{ -DUNE_UNUSED_PARAMETER(inout); -DUNE_UNUSED_PARAMETER(len); -return 0; -} - -/** @brief Compute the product of the argument over all processes and -return the result in every process. Assumes that T has an operator* -*/ -template<typename T> -T prod (const T& in) const -{ -return in; -} - -/** @brief Compute the product over all processes -for each component of an array and return the result -in every process. Assumes that T has an operator* -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int prod (T* inout, int len) const -{ -DUNE_UNUSED_PARAMETER(inout); -DUNE_UNUSED_PARAMETER(len); -return 0; -} - -/** @brief Compute the minimum of the argument over all processes and -return the result in every process. Assumes that T has an operator< -*/ -template<typename T> -T min (const T& in) const -{ -return in; -} - -/** @brief Compute the minimum over all processes -for each component of an array and return the result -in every process. Assumes that T has an operator< -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int min (T* inout, int len) const -{ -DUNE_UNUSED_PARAMETER(inout); -DUNE_UNUSED_PARAMETER(len); -return 0; -} - -/** @brief Compute the maximum of the argument over all processes and -return the result in every process. Assumes that T has an operator< -*/ -template<typename T> -T max (const T& in) const -{ -return in; -} - -/** @brief Compute the maximum over all processes -for each component of an array and return the result -in every process. Assumes that T has an operator< -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int max (T* inout, int len) const -{ -DUNE_UNUSED_PARAMETER(inout); -DUNE_UNUSED_PARAMETER(len); -return 0; -} - -/** @brief Wait until all processes have arrived at this point in the program. -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -int barrier () const -{ -return 0; -} - -/** @brief Nonblocking barrier -@returns Future<void> which is complete when all processes have reached the barrier -*/ -PseudoFuture<void> ibarrier () const -{ -return {true}; // return a valid future -} - -/** @brief Distribute an array from the process with rank root to all other processes -@returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int broadcast (T* inout, int len, int root) const -{ -DUNE_UNUSED_PARAMETER(inout); -DUNE_UNUSED_PARAMETER(len); -DUNE_UNUSED_PARAMETER(root); -return 0; -} - -/** @brief Distribute an array from the process with rank root to all other processes nonblocking -@returns Future<T> containing the distributed data -*/ -template<class T> -PseudoFuture<T> ibroadcast(T&& data, int root) const{ -return {std::forward<T>(data)}; -} - - -/** @brief Gather arrays on root task. -* -* Each process sends its in array of length len to the root process -* (including the root itself). In the root process these arrays are stored in rank -* order in the out array which must have size len * number of processes. -* @param[in] in The send buffer with the data to send. -* @param[out] out The buffer to store the received data in. Might have length zero on non-root -* tasks. -* @param[in] len The number of elements to send on each task. -* @param[in] root The root task that gathers the data. -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int gather (const T* in, T* out, int len, int root) const // note out must have same size as in -{ -DUNE_UNUSED_PARAMETER(root); -for (int i=0; i<len; i++) -out[i] = in[i]; -return 0; -} - -/** @brief Gather arrays on root task nonblocking -@returns Future<TOUT, TIN> containing the gathered data -*/ -template<class TIN, class TOUT = std::vector<TIN>> -PseudoFuture<TOUT> igather(TIN&& data_in, TOUT&& data_out, int root){ -*(data_out.begin()) = std::forward<TIN>(data_in); -return {std::forward<TOUT>(data_out)}; -} - - -/** @brief Gather arrays of variable size on root task. -* -* Each process sends its in array of length sendlen to the root process -* (including the root itself). In the root process these arrays are stored in rank -* order in the out array. -* @param[in] in The send buffer with the data to be sent -* @param[in] sendlen The number of elements to send on each task -* @param[out] out The buffer to store the received data in. May have length zero on non-root -* tasks. -* @param[in] recvlen An array with size equal to the number of processes containing the number -* of elements to receive from process i at position i, i.e. the number that -* is passed as sendlen argument to this function in process i. -* May have length zero on non-root tasks. -* @param[out] displ An array with size equal to the number of processes. Data received from -* process i will be written starting at out+displ[i] on the root process. -* May have length zero on non-root tasks. -* @param[in] root The root task that gathers the data. -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int gatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ, int root) const -{ -DUNE_UNUSED_PARAMETER(recvlen); -DUNE_UNUSED_PARAMETER(root); -for (int i=*displ; i<sendlen; i++) -out[i] = in[i]; -return 0; -} - -/** @brief Scatter array from a root to all other task. -* -* The root process sends the elements with index from k*len to (k+1)*len-1 in its array to -* task k, which stores it at index 0 to len-1. -* @param[in] send The array to scatter. Might have length zero on non-root -* tasks. -* @param[out] recv The buffer to store the received data in. Upon completion of the -* method each task will have same data stored there as the one in -* send buffer of the root task before. -* @param[in] len The number of elements in the recv buffer. -* @param[in] root The root task that gathers the data. -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int scatter (const T* send, T* recv, int len, int root) const // note out must have same size as in -{ -DUNE_UNUSED_PARAMETER(root); -for (int i=0; i<len; i++) -recv[i] = send[i]; -return 0; -} - -/** @brief Scatter array from a root to all other task nonblocking. -* @returns Future<TOUT, TIN> containing scattered data; -*/ -template<class TIN, class TOUT = TIN> -PseudoFuture<TOUT> iscatter(TIN&& data_in, TOUT&& data_out, int root){ -data_out = *(std::forward<TIN>(data_in).begin()); -return {std::forward<TOUT>(data_out)}; -} - -/** @brief Scatter arrays of variable length from a root to all other tasks. -* -* The root process sends the elements with index from send+displ[k] to send+displ[k]-1 in -* its array to task k, which stores it at index 0 to recvlen-1. -* @param[in] send The array to scatter. May have length zero on non-root -* tasks. -* @param[in] sendlen An array with size equal to the number of processes containing the number -* of elements to scatter to process i at position i, i.e. the number that -* is passed as recvlen argument to this function in process i. -* @param[in] displ An array with size equal to the number of processes. Data scattered to -* process i will be read starting at send+displ[i] on root the process. -* @param[out] recv The buffer to store the received data in. Upon completion of the -* method each task will have the same data stored there as the one in -* send buffer of the root task before. -* @param[in] recvlen The number of elements in the recv buffer. -* @param[in] root The root task that gathers the data. -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int scatterv (const T* send, int* sendlen, int* displ, T* recv, int recvlen, int root) const -{ -DUNE_UNUSED_PARAMETER(recvlen); -DUNE_UNUSED_PARAMETER(root); -for (int i=*displ; i<*sendlen; i++) -recv[i] = send[i]; -return 0; -} - -/** -* @brief Gathers data from all tasks and distribute it to all. -* -* The block of data sent from the jth process is received by every -* process and placed in the jth block of the buffer recvbuf. -* -* @param[in] sbuf The buffer with the data to send. Has to be the same for -* each task. -* @param[in] count The number of elements to send by any process. -* @param[out] rbuf The receive buffer for the data. Has to be of size -* notasks*count, with notasks being the number of tasks in the communicator. -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int allgather(const T* sbuf, int count, T* rbuf) const -{ -for(const T* end=sbuf+count; sbuf < end; ++sbuf, ++rbuf) -*rbuf=*sbuf; -return 0; -} - -/** -* @brief Gathers data from all tasks and distribute it to all nonblocking. -@returns Future<TOUT, TIN> containing the distributed data -*/ -template<class TIN, class TOUT = TIN> -PseudoFuture<TOUT> iallgather(TIN&& data_in, TOUT&& data_out){ -return {std::forward<TOUT>(data_out)}; -} - -/** -* @brief Gathers data of variable length from all tasks and distribute it to all. -* -* The block of data sent from the jth process is received by every -* process and placed in the jth block of the buffer out. -* -* @param[in] in The send buffer with the data to send. -* @param[in] sendlen The number of elements to send on each task. -* @param[out] out The buffer to store the received data in. -* @param[in] recvlen An array with size equal to the number of processes containing the number -* of elements to receive from process i at position i, i.e. the number that -* is passed as sendlen argument to this function in process i. -* @param[in] displ An array with size equal to the number of processes. Data received from -* process i will be written starting at out+displ[i]. -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename T> -int allgatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ) const -{ -DUNE_UNUSED_PARAMETER(recvlen); -for (int i=*displ; i<sendlen; i++) -out[i] = in[i]; -return 0; -} - -/** -* @brief Compute something over all processes -* for each component of an array and return the result -* in every process. -* -* The template parameter BinaryFunction is the type of -* the binary function to use for the computation -* -* @param inout The array to compute on. -* @param len The number of components in the array -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename BinaryFunction, typename Type> -int allreduce(Type* inout, int len) const -{ -DUNE_UNUSED_PARAMETER(inout); -DUNE_UNUSED_PARAMETER(len); -return 0; -} - -/** -* @brief Compute something over all processes nonblocking -@return Future<TOUT, TIN> containing the computed something -*/ -template<class BinaryFunction, class TIN, class TOUT = TIN> -PseudoFuture<TOUT> iallreduce(TIN&& data_in, TOUT&& data_out){ -data_out = std::forward<TIN>(data_in); -return {std::forward<TOUT>(data_out)}; -} - -/** -* @brief Compute something over all processes nonblocking and in-place -@return Future<T> containing the computed something -*/ -template<class BinaryFunction, class T> -PseudoFuture<T> iallreduce(T&& data){ -return {std::forward<T>(data)}; -} - - -/** -* @brief Compute something over all processes -* for each component of an array and return the result -* in every process. -* -* The template parameter BinaryFunction is the type of -* the binary function to use for the computation -* -* @param in The array to compute on. -* @param out The array to store the results in. -* @param len The number of components in the array -* @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise -*/ -template<typename BinaryFunction, typename Type> -int allreduce(const Type* in, Type* out, int len) const -{ -std::copy(in, in+len, out); -return 0; -} - -}; - -template<class T> -using CollectiveCommunication -// Will be deprecated after the 2.7 release -//[[deprecated("CollectiveCommunication is deprecated. Use Communication instead.")]] -= Communication<T>; + /* define some type that definitely differs from MPI_Comm */ + struct No_Comm {}; + + /*! @brief Comparison operator for MPI compatibility + + Always returns true. + */ + inline bool operator==(const No_Comm&, const No_Comm&) + { + return true; + } + + /*! @brief Comparison operator for MPI compatibility + + Always returns false. + */ + inline bool operator!=(const No_Comm&, const No_Comm&) + { + return false; + } + + /*! @brief Collective communication interface and sequential default implementation + + Communication offers an abstraction to the basic methods + of parallel communication, following the message-passing + paradigm. It allows one to switch parallel features on and off, without + changing the code. Currently only MPI and sequential code are + supported. + + A Communication object is returned by all grids (also + the sequential ones) in order to allow code to be written in + a transparent way for sequential and parallel grids. + + This class provides a default implementation for sequential grids. + The number of processes involved is 1, any sum, maximum, etc. returns + just its input argument and so on. + + In specializations one can implement the real thing using appropriate + communication functions, e.g. there exists an implementation using + the Message Passing %Interface (MPI), see Dune::Communication<MPI_Comm>. + + Moreover, the communication subsystem used by an implementation + is not visible in the interface, i.e. Dune grid implementations + are not restricted to MPI. + + \tparam Communicator The communicator type used by your message-passing implementation. + For MPI this will be MPI_Comm. For sequential codes there is the dummy communicator No_Comm. + It is assumed that if you want to specialize the Communication class for a + message-passing system other than MPI, that message-passing system will have something + equivalent to MPI communicators. + + \ingroup ParallelCommunication + */ + template<typename Communicator> + class Communication + { + public: + //! Construct default object + Communication() + {} + + /** \brief Constructor with a given communicator + * + * As this is implementation for the sequential setting, the communicator is a dummy and simply discarded. + */ + Communication (const Communicator&) + {} + + //! Return rank, is between 0 and size()-1 + int rank () const + { + return 0; + } + + //! cast to the underlying Fake MPI communicator + operator No_Comm() const + { + return {}; + } + + //! Number of processes in set, is greater than 0 + int size () const + { + return 1; + } + + /** @brief Sends the data to the dest_rank + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<class T> + int send(const T& data, int dest_rank, int tag){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(dest_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + /** @brief Sends the data to the dest_rank nonblocking + @returns Future<T> containing the send buffer, completes when data is send + */ + template<class T> + PseudoFuture<T> isend(const T&& data, int dest_rank, int tag){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(dest_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + /** @brief Receives the data from the source_rank + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<class T> + T recv(T&& data, int source_rank, int tag, void* status = 0){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(source_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_UNUSED_PARAMETER(status); + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + /** @brief Receives the data from the source_rank nonblocking + @returns Future<T> containing the received data when complete + */ + template<class T> + PseudoFuture<T> irecv(T&& data, int source_rank, int tag){ + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(source_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + + template<class T> + T rrecv(T&& data, int source_rank, int tag, void* status = 0) const + { + DUNE_UNUSED_PARAMETER(data); + DUNE_UNUSED_PARAMETER(source_rank); + DUNE_UNUSED_PARAMETER(tag); + DUNE_UNUSED_PARAMETER(status); + DUNE_THROW(ParallelError, "This method is not supported in sequential programs"); + } + /** @brief Compute the sum of the argument over all processes and + return the result in every process. Assumes that T has an operator+ + */ + template<typename T> + T sum (const T& in) const + { + return in; + } + + /** @brief Compute the sum over all processes for each component of an array and return the result + in every process. Assumes that T has an operator+ + + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int sum (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + return 0; + } + + /** @brief Compute the product of the argument over all processes and + return the result in every process. Assumes that T has an operator* + */ + template<typename T> + T prod (const T& in) const + { + return in; + } + + /** @brief Compute the product over all processes + for each component of an array and return the result + in every process. Assumes that T has an operator* + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int prod (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + return 0; + } + + /** @brief Compute the minimum of the argument over all processes and + return the result in every process. Assumes that T has an operator< + */ + template<typename T> + T min (const T& in) const + { + return in; + } + + /** @brief Compute the minimum over all processes + for each component of an array and return the result + in every process. Assumes that T has an operator< + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int min (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + return 0; + } + + /** @brief Compute the maximum of the argument over all processes and + return the result in every process. Assumes that T has an operator< + */ + template<typename T> + T max (const T& in) const + { + return in; + } + + /** @brief Compute the maximum over all processes + for each component of an array and return the result + in every process. Assumes that T has an operator< + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int max (T* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + return 0; + } + + /** @brief Wait until all processes have arrived at this point in the program. + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + int barrier () const + { + return 0; + } + + /** @brief Nonblocking barrier + @returns Future<void> which is complete when all processes have reached the barrier + */ + PseudoFuture<void> ibarrier () const + { + return {true}; // return a valid future + } + + /** @brief Distribute an array from the process with rank root to all other processes + @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int broadcast (T* inout, int len, int root) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + DUNE_UNUSED_PARAMETER(root); + return 0; + } + + /** @brief Distribute an array from the process with rank root to all other processes nonblocking + @returns Future<T> containing the distributed data + */ + template<class T> + PseudoFuture<T> ibroadcast(T&& data, int root) const{ + return {std::forward<T>(data)}; + } + + + /** @brief Gather arrays on root task. + * + * Each process sends its in array of length len to the root process + * (including the root itself). In the root process these arrays are stored in rank + * order in the out array which must have size len * number of processes. + * @param[in] in The send buffer with the data to send. + * @param[out] out The buffer to store the received data in. Might have length zero on non-root + * tasks. + * @param[in] len The number of elements to send on each task. + * @param[in] root The root task that gathers the data. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int gather (const T* in, T* out, int len, int root) const // note out must have same size as in + { + DUNE_UNUSED_PARAMETER(root); + for (int i=0; i<len; i++) + out[i] = in[i]; + return 0; + } + + /** @brief Gather arrays on root task nonblocking + @returns Future<TOUT, TIN> containing the gathered data + */ + template<class TIN, class TOUT = std::vector<TIN>> + PseudoFuture<TOUT> igather(TIN&& data_in, TOUT&& data_out, int root){ + *(data_out.begin()) = std::forward<TIN>(data_in); + return {std::forward<TOUT>(data_out)}; + } + + + /** @brief Gather arrays of variable size on root task. + * + * Each process sends its in array of length sendlen to the root process + * (including the root itself). In the root process these arrays are stored in rank + * order in the out array. + * @param[in] in The send buffer with the data to be sent + * @param[in] sendlen The number of elements to send on each task + * @param[out] out The buffer to store the received data in. May have length zero on non-root + * tasks. + * @param[in] recvlen An array with size equal to the number of processes containing the number + * of elements to receive from process i at position i, i.e. the number that + * is passed as sendlen argument to this function in process i. + * May have length zero on non-root tasks. + * @param[out] displ An array with size equal to the number of processes. Data received from + * process i will be written starting at out+displ[i] on the root process. + * May have length zero on non-root tasks. + * @param[in] root The root task that gathers the data. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int gatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ, int root) const + { + DUNE_UNUSED_PARAMETER(recvlen); + DUNE_UNUSED_PARAMETER(root); + for (int i=*displ; i<sendlen; i++) + out[i] = in[i]; + return 0; + } + + /** @brief Scatter array from a root to all other task. + * + * The root process sends the elements with index from k*len to (k+1)*len-1 in its array to + * task k, which stores it at index 0 to len-1. + * @param[in] send The array to scatter. Might have length zero on non-root + * tasks. + * @param[out] recv The buffer to store the received data in. Upon completion of the + * method each task will have same data stored there as the one in + * send buffer of the root task before. + * @param[in] len The number of elements in the recv buffer. + * @param[in] root The root task that gathers the data. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int scatter (const T* send, T* recv, int len, int root) const // note out must have same size as in + { + DUNE_UNUSED_PARAMETER(root); + for (int i=0; i<len; i++) + recv[i] = send[i]; + return 0; + } + + /** @brief Scatter array from a root to all other task nonblocking. + * @returns Future<TOUT, TIN> containing scattered data; + */ + template<class TIN, class TOUT = TIN> + PseudoFuture<TOUT> iscatter(TIN&& data_in, TOUT&& data_out, int root){ + data_out = *(std::forward<TIN>(data_in).begin()); + return {std::forward<TOUT>(data_out)}; + } + + /** @brief Scatter arrays of variable length from a root to all other tasks. + * + * The root process sends the elements with index from send+displ[k] to send+displ[k]-1 in + * its array to task k, which stores it at index 0 to recvlen-1. + * @param[in] send The array to scatter. May have length zero on non-root + * tasks. + * @param[in] sendlen An array with size equal to the number of processes containing the number + * of elements to scatter to process i at position i, i.e. the number that + * is passed as recvlen argument to this function in process i. + * @param[in] displ An array with size equal to the number of processes. Data scattered to + * process i will be read starting at send+displ[i] on root the process. + * @param[out] recv The buffer to store the received data in. Upon completion of the + * method each task will have the same data stored there as the one in + * send buffer of the root task before. + * @param[in] recvlen The number of elements in the recv buffer. + * @param[in] root The root task that gathers the data. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int scatterv (const T* send, int* sendlen, int* displ, T* recv, int recvlen, int root) const + { + DUNE_UNUSED_PARAMETER(recvlen); + DUNE_UNUSED_PARAMETER(root); + for (int i=*displ; i<*sendlen; i++) + recv[i] = send[i]; + return 0; + } + + /** + * @brief Gathers data from all tasks and distribute it to all. + * + * The block of data sent from the jth process is received by every + * process and placed in the jth block of the buffer recvbuf. + * + * @param[in] sbuf The buffer with the data to send. Has to be the same for + * each task. + * @param[in] count The number of elements to send by any process. + * @param[out] rbuf The receive buffer for the data. Has to be of size + * notasks*count, with notasks being the number of tasks in the communicator. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int allgather(const T* sbuf, int count, T* rbuf) const + { + for(const T* end=sbuf+count; sbuf < end; ++sbuf, ++rbuf) + *rbuf=*sbuf; + return 0; + } + + /** + * @brief Gathers data from all tasks and distribute it to all nonblocking. + @returns Future<TOUT, TIN> containing the distributed data + */ + template<class TIN, class TOUT = TIN> + PseudoFuture<TOUT> iallgather(TIN&& data_in, TOUT&& data_out){ + return {std::forward<TOUT>(data_out)}; + } + + /** + * @brief Gathers data of variable length from all tasks and distribute it to all. + * + * The block of data sent from the jth process is received by every + * process and placed in the jth block of the buffer out. + * + * @param[in] in The send buffer with the data to send. + * @param[in] sendlen The number of elements to send on each task. + * @param[out] out The buffer to store the received data in. + * @param[in] recvlen An array with size equal to the number of processes containing the number + * of elements to receive from process i at position i, i.e. the number that + * is passed as sendlen argument to this function in process i. + * @param[in] displ An array with size equal to the number of processes. Data received from + * process i will be written starting at out+displ[i]. + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename T> + int allgatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ) const + { + DUNE_UNUSED_PARAMETER(recvlen); + for (int i=*displ; i<sendlen; i++) + out[i] = in[i]; + return 0; + } + + /** + * @brief Compute something over all processes + * for each component of an array and return the result + * in every process. + * + * The template parameter BinaryFunction is the type of + * the binary function to use for the computation + * + * @param inout The array to compute on. + * @param len The number of components in the array + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename BinaryFunction, typename Type> + int allreduce(Type* inout, int len) const + { + DUNE_UNUSED_PARAMETER(inout); + DUNE_UNUSED_PARAMETER(len); + return 0; + } + + /** + * @brief Compute something over all processes nonblocking + @return Future<TOUT, TIN> containing the computed something + */ + template<class BinaryFunction, class TIN, class TOUT = TIN> + PseudoFuture<TOUT> iallreduce(TIN&& data_in, TOUT&& data_out){ + data_out = std::forward<TIN>(data_in); + return {std::forward<TOUT>(data_out)}; + } + + /** + * @brief Compute something over all processes nonblocking and in-place + @return Future<T> containing the computed something + */ + template<class BinaryFunction, class T> + PseudoFuture<T> iallreduce(T&& data){ + return {std::forward<T>(data)}; + } + + + /** + * @brief Compute something over all processes + * for each component of an array and return the result + * in every process. + * + * The template parameter BinaryFunction is the type of + * the binary function to use for the computation + * + * @param in The array to compute on. + * @param out The array to store the results in. + * @param len The number of components in the array + * @returns MPI_SUCCESS (==0) if successful, an MPI error code otherwise + */ + template<typename BinaryFunction, typename Type> + int allreduce(const Type* in, Type* out, int len) const + { + std::copy(in, in+len, out); + return 0; + } + + }; + + template<class T> + using CollectiveCommunication + // Will be deprecated after the 2.7 release + //[[deprecated("CollectiveCommunication is deprecated. Use Communication instead.")]] + = Communication<T>; } #endif diff --git a/dune/common/parallel/communicator.hh b/dune/common/parallel/communicator.hh index e2a39f2fb6990b4ffa44927db49afd831733c0aa..04f67cc2aaea2e8c8f385594b24f91aeb172694b 100644 --- a/dune/common/parallel/communicator.hh +++ b/dune/common/parallel/communicator.hh @@ -23,1525 +23,1525 @@ namespace Dune { -/** @defgroup Common_Parallel Parallel Computing based on Indexsets -* @ingroup ParallelCommunication -* @brief Provides classes for syncing distributed indexed -* data structures. -* -* In a parallel representation a container \f$x\f$, -* e.g. a plain C-array, cannot be stored with all entries on each process -* because of limited memory and efficiency reasons. Therefore -* it is represented by individual -* pieces \f$x_p\f$, \f$p=0, \ldots, P-1\f$, where \f$x_p\f$ is the piece stored on -* process \f$p\f$ of the \f$P\f$ processes participating in the calculation. -* Although the global representation of the container is not -* available on any process, a process \f$p\f$ needs to know how the entries -* of it's local piece \f$x_p\f$ correspond to the entries of the global -* container \f$x\f$, which would be used in a sequential program. In this -* module we present classes describing the mapping of the local pieces -* to the global -* view and the communication interfaces. -* -* @section IndexSet Parallel Index Sets -* -* Form an abstract point of view a random access container \f$x: I -* \rightarrow K\f$ provides a -* mapping from an index set \f$I \subset N_0\f$ onto a set of objects -* \f$K\f$. Note that we do not require \f$I\f$ to be consecutive. The piece -* \f$x_p\f$ of the container \f$x\f$ stored on process \f$p\f$ is a mapping \f$x_p:I_p -* \rightarrow K\f$, where \f$I_p \subset I\f$. Due to efficiency the entries -* of \f$x_p\f$ should be stored in consecutive memory. -* -* This means that for the local computation the data must be addressable -* by a consecutive index starting from \f$0\f$. When using adaptive -* discretisation methods there might be the need to reorder the indices -* after adding and/or deleting some of the discretisation -* points. Therefore this index does not have to be persistent. Further -* on we will call this index <em>local index</em>. -* -* For the communication phases of our algorithms these locally stored -* entries must also be addressable by a global identifier to be able to -* store the received values tagged with the global identifiers at the -* correct local index in the consecutive local memory chunk. To ease the -* addition and removal of discretisation points this global identifier has -* to be persistent. Further on we will call this global identifier -* <em>global index</em>. -* -* Classes to build the mapping are ParallelIndexSet and ParallelLocalIndex. -* As these just provide a mapping from the global index to the local index, -* the wrapper class GlobalLookupIndexSet facilitates the reverse lookup. -* -* @section remote Remote Index Information -* -* To setup communication between the processes every process needs to -* know what indices are also known to other processes and what -* attributes are attached to them on the remote side. This information is -* calculated and encapsulated in class RemoteIndices. -* -* @section comm Communication -* -* Based on the information about the distributed index sets, data -* independent interfaces between different sets of the index sets -* can be setup using the class Interface. For the actual communication -* data dependent communicators can be setup using BufferedCommunicator, -* DatatypeCommunicator VariableSizeCommunicator based on the interface -* information. In contrast to the former -* the latter is independent of the class Interface can work on a map -* from process number to a pair of index lists describing which local indices -* are send and received from that processs, respectively. -*/ -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Provides utility classes for syncing distributed data via -* MPI communication. -* @author Markus Blatt -*/ - -/** -* @brief Flag for marking indexed data structures where data at -* each index is of the same size. -* @see VariableSize -*/ -struct SizeOne -{}; - -/** -* @brief Flag for marking indexed data structures where the data at each index may -* be a variable multiple of another type. -* @see SizeOne -*/ -struct VariableSize -{}; - - -/** -* @brief Default policy used for communicating an indexed type. -* -* This -*/ -template<class V> -struct CommPolicy -{ -/** -* @brief The type the policy is for. -* -* It has to provide the mode -* \code Type::IndexedType operator[](int i);\endcode -* for -* the access of the value at index i and a typedef IndexedType. -* It is assumed -* that only one entry is at each index (as in scalar -* vector. -*/ -typedef V Type; - -/** -* @brief The type we get at each index with operator[]. -* -* The default is the value_type typedef of the container. -*/ -typedef typename V::value_type IndexedType; - -/** -* @brief Whether the indexed type has variable size or there -* is always one value at each index. -*/ -typedef SizeOne IndexedTypeFlag; - -/** -* @brief Get the address of entry at an index. -* -* The default implementation uses operator[] to -* get the address. -* @param v An existing representation of the type that has more elements than index. -* @param index The index of the entry. -*/ -static const void* getAddress(const V& v, int index); - -/** -* @brief Get the number of primitve elements at that index. -* -* The default always returns 1. -*/ -static int getSize(const V&, int index); -}; - -template<class K, int n> class FieldVector; - -template<class B, class A> class VariableBlockVector; - -template<class K, class A, int n> -struct CommPolicy<VariableBlockVector<FieldVector<K, n>, A> > -{ -typedef VariableBlockVector<FieldVector<K, n>, A> Type; - -typedef typename Type::B IndexedType; - -typedef VariableSize IndexedTypeFlag; - -static const void* getAddress(const Type& v, int i); - -static int getSize(const Type& v, int i); -}; - -/** -* @brief Error thrown if there was a problem with the communication. -*/ -class CommunicationError : public IOError -{}; - -/** -* @brief GatherScatter default implementation that just copies data. -*/ -template<class T> -struct CopyGatherScatter -{ -typedef typename CommPolicy<T>::IndexedType IndexedType; - -static const IndexedType& gather(const T& vec, std::size_t i); - -static void scatter(T& vec, const IndexedType& v, std::size_t i); - -}; - -/** -* @brief An utility class for communicating distributed data structures via MPI datatypes. -* -* This communicator creates special MPI datatypes that address the non contiguous elements -* to be send and received. The idea was to prevent the copying to an additional buffer and -* the mpi implementation decide whether to allocate buffers or use buffers offered by the -* interconnection network. -* -* Unfortunately the implementation of MPI datatypes seems to be poor. Therefore for most MPI -* implementations using a BufferedCommunicator will be more efficient. -*/ -template<typename T> -class DatatypeCommunicator : public InterfaceBuilder -{ -public: - -/** -* @brief Type of the index set. -*/ -typedef T ParallelIndexSet; - -/** -* @brief Type of the underlying remote indices class. -*/ -typedef Dune::RemoteIndices<ParallelIndexSet> RemoteIndices; - -/** -* @brief The type of the global index. -*/ -typedef typename RemoteIndices::GlobalIndex GlobalIndex; - -/** -* @brief The type of the attribute. -*/ -typedef typename RemoteIndices::Attribute Attribute; - -/** -* @brief The type of the local index. -*/ -typedef typename RemoteIndices::LocalIndex LocalIndex; - -/** -* @brief Creates a new DatatypeCommunicator. -*/ -DatatypeCommunicator(); - -/** -* @brief Destructor. -*/ -~DatatypeCommunicator(); - -/** -* @brief Builds the interface between the index sets. -* -* Has to be called before the actual communication by forward or backward -* can be called. Nonpublic indices will be ignored! -* -* -* The types T1 and T2 are classes representing a set of -* enumeration values of type DatatypeCommunicator::Attribute. -* They have to provide -* a (static) method -* \code -* bool contains(Attribute flag) const; -* \endcode -* for checking whether the set contains a specific flag. -* This functionality is for example provided the classes -* EnumItem, EnumRange and Combine. -* -* @param remoteIndices The indices present on remote processes. -* @param sourceFlags The set of attributes which mark indices we send to other -* processes. -* @param sendData The indexed data structure whose data will be send. -* @param destFlags The set of attributes which mark the indices we might -* receive values from. -* @param receiveData The indexed data structure for which we receive data. -*/ -template<class T1, class T2, class V> -void build(const RemoteIndices& remoteIndices, const T1& sourceFlags, V& sendData, const T2& destFlags, V& receiveData); - -/** -* @brief Sends the primitive values from the source to the destination. -*/ -void forward(); - -/** -* @brief Sends the primitive values from the destination to the source. -*/ -void backward(); - -/** -* @brief Deallocates the MPI requests and data types. -*/ -void free(); -private: -enum { -/** -* @brief Tag for the MPI communication. -*/ -commTag_ = 234 -}; - -/** -* @brief The indices also known at other processes. -*/ -const RemoteIndices* remoteIndices_; - -typedef std::map<int,std::pair<MPI_Datatype,MPI_Datatype> > -MessageTypeMap; - -/** -* @brief The datatypes built according to the communication interface. -*/ -MessageTypeMap messageTypes; - -/** -* @brief The pointer to the data whose entries we communicate. -*/ -void* data_; - -MPI_Request* requests_[2]; - -/** -* @brief True if the request and data types were created. -*/ -bool created_; - -/** -* @brief Creates the MPI_Requests for the forward communication. -*/ -template<class V, bool FORWARD> -void createRequests(V& sendData, V& receiveData); - -/** -* @brief Creates the data types needed for the unbuffered receive. -*/ -template<class T1, class T2, class V, bool send> -void createDataTypes(const T1& source, const T2& destination, V& data); - -/** -* @brief Initiates the sending and receive. -*/ -void sendRecv(MPI_Request* req); - -/** -* @brief Information used for setting up the MPI Datatypes. -*/ -struct IndexedTypeInformation -{ -/** -* @brief Allocate space for setting up the MPI datatype. -* -* @param i The number of values the datatype will have. -*/ -void build(int i) -{ -length = new int[i]; -displ = new MPI_Aint[i]; -size = i; -} - -/** -* @brief Free the allocated space. -*/ -void free() -{ -delete[] length; -delete[] displ; -} -/** @brief The number of values at each index. */ -int* length; -/** @brief The displacement at each index. */ -MPI_Aint* displ; -/** -* @brief The number of elements we send. -* In case of variable sizes this will differ from -* size. -*/ -int elements; -/** -* @param The number of indices in the data type. -*/ -int size; -}; - -/** -* @brief Functor for the InterfaceBuilder. -* -* It will record the information needed to build the MPI_Datatypes. -*/ -template<class V> -struct MPIDatatypeInformation -{ -/** -* @brief Constructor. -* @param data The data we construct an MPI data type for. -*/ -MPIDatatypeInformation(const V& data) : data_(data) -{} - -/** -* @brief Reserver space for the information about the datatype. -* @param proc The rank of the process this information is for. -* @param size The number of indices the datatype will contain. -*/ -void reserve(int proc, int size) -{ -information_[proc].build(size); -} -/** -* @brief Add a new index to the datatype. -* @param proc The rank of the process this index is send to -* or received from. -* @param local The index to add. -*/ -void add(int proc, int local) -{ -IndexedTypeInformation& info=information_[proc]; -assert((info.elements)<info.size); -MPI_Get_address( const_cast<void*>(CommPolicy<V>::getAddress(data_, local)), -info.displ+info.elements); -info.length[info.elements]=CommPolicy<V>::getSize(data_, local); -info.elements++; -} - -/** -* @brief The information about the datatypes to send to or -* receive from each process. -*/ -std::map<int,IndexedTypeInformation> information_; -/** -* @brief A representative of the indexed data we send. -*/ -const V& data_; - -}; - -}; - -/** -* @brief A communicator that uses buffers to gather and scatter -* the data to be send or received. -* -* Before the data is sent it is copied to a consecutive buffer and -* then that buffer is sent. -* The data is received in another buffer and then copied to the actual -* position. -*/ -class BufferedCommunicator -{ - -public: -/** -* @brief Constructor. -*/ -BufferedCommunicator(); - -/** -* @brief Build the buffers and information for the communication process. -* -* -* @param interface The interface that defines what indices are to be communicated. -*/ -template<class Data, class Interface> -typename std::enable_if<std::is_same<SizeOne,typename CommPolicy<Data>::IndexedTypeFlag>::value, void>::type -build(const Interface& interface); - -/** -* @brief Build the buffers and information for the communication process. -* -* @param source The source in a forward send. The values will be copied from here to the send buffers. -* @param target The target in a forward send. The received values will be copied to here. -* @param interface The interface that defines what indices are to be communicated. -*/ -template<class Data, class Interface> -void build(const Data& source, const Data& target, const Interface& interface); - -/** -* @brief Send from source to target. -* -* The template parameter GatherScatter (e.g. CopyGatherScatter) has to have a static method -* \code -* // Gather the data at index index of data -* static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); -* -* // Scatter the value at a index of data -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne -* and -* -* \code -* static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); -* -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index, int subindex); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the -* subindex of the block at index. -* @warning The source and target data have to have the same layout as the ones given -* to the build function in case of variable size values at the indices. -* @param source The values will be copied from here to the send buffers. -* @param dest The received values will be copied to here. -*/ -template<class GatherScatter, class Data> -void forward(const Data& source, Data& dest); - -/** -* @brief Communicate in the reverse direction, i.e. send from target to source. -* -* The template parameter GatherScatter (e.g. CopyGatherScatter) has to have a static method -* \code -* // Gather the data at index index of data -* static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); -* -* // Scatter the value at a index of data -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne -* and -* -* \code -* static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); -* -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index, int subindex); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the -* subindex of the block at index. -* @warning The source and target data have to have the same layout as the ones given -* to the build function in case of variable size values at the indices. -* @param dest The values will be copied from here to the send buffers. -* @param source The received values will be copied to here. -*/ -template<class GatherScatter, class Data> -void backward(Data& source, const Data& dest); - -/** -* @brief Forward send where target and source are the same. -* -* The template parameter GatherScatter has to have a static method -* \code -* // Gather the data at index index of data -* static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); -* -* // Scatter the value at a index of data -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne -* and -* -* \code -* static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); -* -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index, int subindex); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the -* subindex of the block at index. -* @param data Source and target of the communication. -*/ -template<class GatherScatter, class Data> -void forward(Data& data); - -/** -* @brief Backward send where target and source are the same. -* -* The template parameter GatherScatter has to have a static method -* \code -* // Gather the data at index index of data -* static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); -* -* // Scatter the value at a index of data -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne -* and -* -* \code -* static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); -* -* static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, -* int index, int subindex); -* \endcode -* in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the -* subindex of the block at index. -* @param data Source and target of the communication. -*/ -template<class GatherScatter, class Data> -void backward(Data& data); - -/** -* @brief Free the allocated memory (i.e. buffers and message information. -*/ -void free(); - -/** -* @brief Destructor. -*/ -~BufferedCommunicator(); - -private: - -/** -* @brief The type of the map that maps interface information to processors. -*/ -typedef std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > -InterfaceMap; - - -/** -* @brief Functors for message size caculation -*/ -template<class Data, typename IndexedTypeFlag> -struct MessageSizeCalculator -{}; - -/** -* @brief Functor for message size caculation for datatypes -* where at each index is only one value. -*/ -template<class Data> -struct MessageSizeCalculator<Data,SizeOne> -{ -/** -* @brief Calculate the number of values in message -* @param info The information about the interface corresponding -* to the message. -* @return The number of values in th message. -*/ -inline int operator()(const InterfaceInformation& info) const; -/** -* @brief Calculate the number of values in message -* -* @param info The information about the interface corresponding -* to the message. -* @param data ignored. -* @return The number of values in th message. -*/ -inline int operator()(const Data& data, const InterfaceInformation& info) const; -}; - -/** -* @brief Functor for message size caculation for datatypes -* where at each index can be a variable number of values. -*/ -template<class Data> -struct MessageSizeCalculator<Data,VariableSize> -{ -/** -* @brief Calculate the number of values in message -* -* @param info The information about the interface corresponding -* to the message. -* @param data A representative of the data we send. -* @return The number of values in th message. -*/ -inline int operator()(const Data& data, const InterfaceInformation& info) const; -}; - -/** -* @brief Functors for message data gathering. -*/ -template<class Data, class GatherScatter, bool send, typename IndexedTypeFlag> -struct MessageGatherer -{}; - -/** -* @brief Functor for message data gathering for datatypes -* where at each index is only one value. -*/ -template<class Data, class GatherScatter, bool send> -struct MessageGatherer<Data,GatherScatter,send,SizeOne> -{ -/** @brief The type of the values we send. */ -typedef typename CommPolicy<Data>::IndexedType Type; - -/** -* @brief The type of the functor that does the actual copying -* during the data Scattering. -*/ -typedef GatherScatter Gatherer; - -enum { -/** -* @brief The communication mode -* -* True if this was a forward communication. -*/ -forward=send -}; - -/** -* @brief Copies the values to send into the buffer. -* @param interface The interface used in the send. -* @param data The data from which we copy the values. -* @param buffer The send buffer to copy to. -* @param bufferSize The size of the buffer in bytes. For checks. -*/ -inline void operator()(const InterfaceMap& interface, const Data& data, Type* buffer, size_t bufferSize) const; -}; - -/** -* @brief Functor for message data scattering for datatypes -* where at each index can be a variable size of values -*/ -template<class Data, class GatherScatter, bool send> -struct MessageGatherer<Data,GatherScatter,send,VariableSize> -{ -/** @brief The type of the values we send. */ -typedef typename CommPolicy<Data>::IndexedType Type; - -/** -* @brief The type of the functor that does the actual copying -* during the data Scattering. -*/ -typedef GatherScatter Gatherer; - -enum { -/** -* @brief The communication mode -* -* True if this was a forward communication. -*/ -forward=send -}; - -/** -* @brief Copies the values to send into the buffer. -* @param interface The interface used in the send. -* @param data The data from which we copy the values. -* @param buffer The send buffer to copy to. -* @param bufferSize The size of the buffer in bytes. For checks. -*/ -inline void operator()(const InterfaceMap& interface, const Data& data, Type* buffer, size_t bufferSize) const; -}; - -/** -* @brief Functors for message data scattering. -*/ -template<class Data, class GatherScatter, bool send, typename IndexedTypeFlag> -struct MessageScatterer -{}; - -/** -* @brief Functor for message data gathering for datatypes -* where at each index is only one value. -*/ -template<class Data, class GatherScatter, bool send> -struct MessageScatterer<Data,GatherScatter,send,SizeOne> -{ -/** @brief The type of the values we send. */ -typedef typename CommPolicy<Data>::IndexedType Type; - -/** -* @brief The type of the functor that does the actual copying -* during the data Scattering. -*/ -typedef GatherScatter Scatterer; - -enum { -/** -* @brief The communication mode -* -* True if this was a forward communication. -*/ -forward=send -}; - -/** -* @brief Copy the message data from the receive buffer to the data. -* @param interface The interface used in the send. -* @param data The data to which we copy the values. -* @param buffer The receive buffer to copy from. -* @param proc The rank of the process the message is from. -*/ -inline void operator()(const InterfaceMap& interface, Data& data, Type* buffer, const int& proc) const; -}; -/** -* @brief Functor for message data scattering for datatypes -* where at each index can be a variable size of values -*/ -template<class Data, class GatherScatter, bool send> -struct MessageScatterer<Data,GatherScatter,send,VariableSize> -{ -/** @brief The type of the values we send. */ -typedef typename CommPolicy<Data>::IndexedType Type; - -/** -* @brief The type of the functor that does the actual copying -* during the data Scattering. -*/ -typedef GatherScatter Scatterer; - -enum { -/** -* @brief The communication mode -* -* True if this was a forward communication. -*/ -forward=send -}; - -/** -* @brief Copy the message data from the receive buffer to the data. -* @param interface The interface used in the send. -* @param data The data to which we copy the values. -* @param buffer The receive buffer to copy from. -* @param proc The rank of the process the message is from. -*/ -inline void operator()(const InterfaceMap& interface, Data& data, Type* buffer, const int& proc) const; -}; - -/** -* @brief Information about a message to send. -*/ -struct MessageInformation -{ -/** @brief Constructor. */ -MessageInformation() -: start_(0), size_(0) -{} - -/** -* @brief Constructor. -* @param start The start of the message in the global buffer. -* Not in bytes but in number of values from the beginning of -* the buffer -* @param size The size of the message in bytes. -*/ -MessageInformation(size_t start, size_t size) -: start_(start), size_(size) -{} -/** -* @brief Start of the message in the buffer counted in number of value. -*/ -size_t start_; -/** -* @brief Number of bytes in the message. -*/ -size_t size_; -}; - -/** -* @brief Type of the map of information about the messages to send. -* -* The key is the process number to communicate with and the value is -* the pair of information about sending and receiving messages. -*/ -typedef std::map<int,std::pair<MessageInformation,MessageInformation> > -InformationMap; -/** -* @brief Gathered information about the messages to send. -*/ -InformationMap messageInformation_; -/** -* @brief Communication buffers. -*/ -char* buffers_[2]; -/** -* @brief The size of the communication buffers -*/ -size_t bufferSize_[2]; - -enum { -/** -* @brief The tag we use for communication. -*/ -commTag_ -}; - -/** -* @brief The interface we currently work with. -*/ -std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > interfaces_; - -MPI_Comm communicator_; - -/** -* @brief Send and receive Data. -*/ -template<class GatherScatter, bool FORWARD, class Data> -void sendRecv(const Data& source, Data& target); - -}; + /** @defgroup Common_Parallel Parallel Computing based on Indexsets + * @ingroup ParallelCommunication + * @brief Provides classes for syncing distributed indexed + * data structures. + * + * In a parallel representation a container \f$x\f$, + * e.g. a plain C-array, cannot be stored with all entries on each process + * because of limited memory and efficiency reasons. Therefore + * it is represented by individual + * pieces \f$x_p\f$, \f$p=0, \ldots, P-1\f$, where \f$x_p\f$ is the piece stored on + * process \f$p\f$ of the \f$P\f$ processes participating in the calculation. + * Although the global representation of the container is not + * available on any process, a process \f$p\f$ needs to know how the entries + * of it's local piece \f$x_p\f$ correspond to the entries of the global + * container \f$x\f$, which would be used in a sequential program. In this + * module we present classes describing the mapping of the local pieces + * to the global + * view and the communication interfaces. + * + * @section IndexSet Parallel Index Sets + * + * Form an abstract point of view a random access container \f$x: I + * \rightarrow K\f$ provides a + * mapping from an index set \f$I \subset N_0\f$ onto a set of objects + * \f$K\f$. Note that we do not require \f$I\f$ to be consecutive. The piece + * \f$x_p\f$ of the container \f$x\f$ stored on process \f$p\f$ is a mapping \f$x_p:I_p + * \rightarrow K\f$, where \f$I_p \subset I\f$. Due to efficiency the entries + * of \f$x_p\f$ should be stored in consecutive memory. + * + * This means that for the local computation the data must be addressable + * by a consecutive index starting from \f$0\f$. When using adaptive + * discretisation methods there might be the need to reorder the indices + * after adding and/or deleting some of the discretisation + * points. Therefore this index does not have to be persistent. Further + * on we will call this index <em>local index</em>. + * + * For the communication phases of our algorithms these locally stored + * entries must also be addressable by a global identifier to be able to + * store the received values tagged with the global identifiers at the + * correct local index in the consecutive local memory chunk. To ease the + * addition and removal of discretisation points this global identifier has + * to be persistent. Further on we will call this global identifier + * <em>global index</em>. + * + * Classes to build the mapping are ParallelIndexSet and ParallelLocalIndex. + * As these just provide a mapping from the global index to the local index, + * the wrapper class GlobalLookupIndexSet facilitates the reverse lookup. + * + * @section remote Remote Index Information + * + * To setup communication between the processes every process needs to + * know what indices are also known to other processes and what + * attributes are attached to them on the remote side. This information is + * calculated and encapsulated in class RemoteIndices. + * + * @section comm Communication + * + * Based on the information about the distributed index sets, data + * independent interfaces between different sets of the index sets + * can be setup using the class Interface. For the actual communication + * data dependent communicators can be setup using BufferedCommunicator, + * DatatypeCommunicator VariableSizeCommunicator based on the interface + * information. In contrast to the former + * the latter is independent of the class Interface can work on a map + * from process number to a pair of index lists describing which local indices + * are send and received from that processs, respectively. + */ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides utility classes for syncing distributed data via + * MPI communication. + * @author Markus Blatt + */ + + /** + * @brief Flag for marking indexed data structures where data at + * each index is of the same size. + * @see VariableSize + */ + struct SizeOne + {}; + + /** + * @brief Flag for marking indexed data structures where the data at each index may + * be a variable multiple of another type. + * @see SizeOne + */ + struct VariableSize + {}; + + + /** + * @brief Default policy used for communicating an indexed type. + * + * This + */ + template<class V> + struct CommPolicy + { + /** + * @brief The type the policy is for. + * + * It has to provide the mode + * \code Type::IndexedType operator[](int i);\endcode + * for + * the access of the value at index i and a typedef IndexedType. + * It is assumed + * that only one entry is at each index (as in scalar + * vector. + */ + typedef V Type; + + /** + * @brief The type we get at each index with operator[]. + * + * The default is the value_type typedef of the container. + */ + typedef typename V::value_type IndexedType; + + /** + * @brief Whether the indexed type has variable size or there + * is always one value at each index. + */ + typedef SizeOne IndexedTypeFlag; + + /** + * @brief Get the address of entry at an index. + * + * The default implementation uses operator[] to + * get the address. + * @param v An existing representation of the type that has more elements than index. + * @param index The index of the entry. + */ + static const void* getAddress(const V& v, int index); + + /** + * @brief Get the number of primitve elements at that index. + * + * The default always returns 1. + */ + static int getSize(const V&, int index); + }; + + template<class K, int n> class FieldVector; + + template<class B, class A> class VariableBlockVector; + + template<class K, class A, int n> + struct CommPolicy<VariableBlockVector<FieldVector<K, n>, A> > + { + typedef VariableBlockVector<FieldVector<K, n>, A> Type; + + typedef typename Type::B IndexedType; + + typedef VariableSize IndexedTypeFlag; + + static const void* getAddress(const Type& v, int i); + + static int getSize(const Type& v, int i); + }; + + /** + * @brief Error thrown if there was a problem with the communication. + */ + class CommunicationError : public IOError + {}; + + /** + * @brief GatherScatter default implementation that just copies data. + */ + template<class T> + struct CopyGatherScatter + { + typedef typename CommPolicy<T>::IndexedType IndexedType; + + static const IndexedType& gather(const T& vec, std::size_t i); + + static void scatter(T& vec, const IndexedType& v, std::size_t i); + + }; + + /** + * @brief An utility class for communicating distributed data structures via MPI datatypes. + * + * This communicator creates special MPI datatypes that address the non contiguous elements + * to be send and received. The idea was to prevent the copying to an additional buffer and + * the mpi implementation decide whether to allocate buffers or use buffers offered by the + * interconnection network. + * + * Unfortunately the implementation of MPI datatypes seems to be poor. Therefore for most MPI + * implementations using a BufferedCommunicator will be more efficient. + */ + template<typename T> + class DatatypeCommunicator : public InterfaceBuilder + { + public: + + /** + * @brief Type of the index set. + */ + typedef T ParallelIndexSet; + + /** + * @brief Type of the underlying remote indices class. + */ + typedef Dune::RemoteIndices<ParallelIndexSet> RemoteIndices; + + /** + * @brief The type of the global index. + */ + typedef typename RemoteIndices::GlobalIndex GlobalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename RemoteIndices::Attribute Attribute; + + /** + * @brief The type of the local index. + */ + typedef typename RemoteIndices::LocalIndex LocalIndex; + + /** + * @brief Creates a new DatatypeCommunicator. + */ + DatatypeCommunicator(); + + /** + * @brief Destructor. + */ + ~DatatypeCommunicator(); + + /** + * @brief Builds the interface between the index sets. + * + * Has to be called before the actual communication by forward or backward + * can be called. Nonpublic indices will be ignored! + * + * + * The types T1 and T2 are classes representing a set of + * enumeration values of type DatatypeCommunicator::Attribute. + * They have to provide + * a (static) method + * \code + * bool contains(Attribute flag) const; + * \endcode + * for checking whether the set contains a specific flag. + * This functionality is for example provided the classes + * EnumItem, EnumRange and Combine. + * + * @param remoteIndices The indices present on remote processes. + * @param sourceFlags The set of attributes which mark indices we send to other + * processes. + * @param sendData The indexed data structure whose data will be send. + * @param destFlags The set of attributes which mark the indices we might + * receive values from. + * @param receiveData The indexed data structure for which we receive data. + */ + template<class T1, class T2, class V> + void build(const RemoteIndices& remoteIndices, const T1& sourceFlags, V& sendData, const T2& destFlags, V& receiveData); + + /** + * @brief Sends the primitive values from the source to the destination. + */ + void forward(); + + /** + * @brief Sends the primitive values from the destination to the source. + */ + void backward(); + + /** + * @brief Deallocates the MPI requests and data types. + */ + void free(); + private: + enum { + /** + * @brief Tag for the MPI communication. + */ + commTag_ = 234 + }; + + /** + * @brief The indices also known at other processes. + */ + const RemoteIndices* remoteIndices_; + + typedef std::map<int,std::pair<MPI_Datatype,MPI_Datatype> > + MessageTypeMap; + + /** + * @brief The datatypes built according to the communication interface. + */ + MessageTypeMap messageTypes; + + /** + * @brief The pointer to the data whose entries we communicate. + */ + void* data_; + + MPI_Request* requests_[2]; + + /** + * @brief True if the request and data types were created. + */ + bool created_; + + /** + * @brief Creates the MPI_Requests for the forward communication. + */ + template<class V, bool FORWARD> + void createRequests(V& sendData, V& receiveData); + + /** + * @brief Creates the data types needed for the unbuffered receive. + */ + template<class T1, class T2, class V, bool send> + void createDataTypes(const T1& source, const T2& destination, V& data); + + /** + * @brief Initiates the sending and receive. + */ + void sendRecv(MPI_Request* req); + + /** + * @brief Information used for setting up the MPI Datatypes. + */ + struct IndexedTypeInformation + { + /** + * @brief Allocate space for setting up the MPI datatype. + * + * @param i The number of values the datatype will have. + */ + void build(int i) + { + length = new int[i]; + displ = new MPI_Aint[i]; + size = i; + } + + /** + * @brief Free the allocated space. + */ + void free() + { + delete[] length; + delete[] displ; + } + /** @brief The number of values at each index. */ + int* length; + /** @brief The displacement at each index. */ + MPI_Aint* displ; + /** + * @brief The number of elements we send. + * In case of variable sizes this will differ from + * size. + */ + int elements; + /** + * @param The number of indices in the data type. + */ + int size; + }; + + /** + * @brief Functor for the InterfaceBuilder. + * + * It will record the information needed to build the MPI_Datatypes. + */ + template<class V> + struct MPIDatatypeInformation + { + /** + * @brief Constructor. + * @param data The data we construct an MPI data type for. + */ + MPIDatatypeInformation(const V& data) : data_(data) + {} + + /** + * @brief Reserver space for the information about the datatype. + * @param proc The rank of the process this information is for. + * @param size The number of indices the datatype will contain. + */ + void reserve(int proc, int size) + { + information_[proc].build(size); + } + /** + * @brief Add a new index to the datatype. + * @param proc The rank of the process this index is send to + * or received from. + * @param local The index to add. + */ + void add(int proc, int local) + { + IndexedTypeInformation& info=information_[proc]; + assert((info.elements)<info.size); + MPI_Get_address( const_cast<void*>(CommPolicy<V>::getAddress(data_, local)), + info.displ+info.elements); + info.length[info.elements]=CommPolicy<V>::getSize(data_, local); + info.elements++; + } + + /** + * @brief The information about the datatypes to send to or + * receive from each process. + */ + std::map<int,IndexedTypeInformation> information_; + /** + * @brief A representative of the indexed data we send. + */ + const V& data_; + + }; + + }; + + /** + * @brief A communicator that uses buffers to gather and scatter + * the data to be send or received. + * + * Before the data is sent it is copied to a consecutive buffer and + * then that buffer is sent. + * The data is received in another buffer and then copied to the actual + * position. + */ + class BufferedCommunicator + { + + public: + /** + * @brief Constructor. + */ + BufferedCommunicator(); + + /** + * @brief Build the buffers and information for the communication process. + * + * + * @param interface The interface that defines what indices are to be communicated. + */ + template<class Data, class Interface> + typename std::enable_if<std::is_same<SizeOne,typename CommPolicy<Data>::IndexedTypeFlag>::value, void>::type + build(const Interface& interface); + + /** + * @brief Build the buffers and information for the communication process. + * + * @param source The source in a forward send. The values will be copied from here to the send buffers. + * @param target The target in a forward send. The received values will be copied to here. + * @param interface The interface that defines what indices are to be communicated. + */ + template<class Data, class Interface> + void build(const Data& source, const Data& target, const Interface& interface); + + /** + * @brief Send from source to target. + * + * The template parameter GatherScatter (e.g. CopyGatherScatter) has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @warning The source and target data have to have the same layout as the ones given + * to the build function in case of variable size values at the indices. + * @param source The values will be copied from here to the send buffers. + * @param dest The received values will be copied to here. + */ + template<class GatherScatter, class Data> + void forward(const Data& source, Data& dest); + + /** + * @brief Communicate in the reverse direction, i.e. send from target to source. + * + * The template parameter GatherScatter (e.g. CopyGatherScatter) has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @warning The source and target data have to have the same layout as the ones given + * to the build function in case of variable size values at the indices. + * @param dest The values will be copied from here to the send buffers. + * @param source The received values will be copied to here. + */ + template<class GatherScatter, class Data> + void backward(Data& source, const Data& dest); + + /** + * @brief Forward send where target and source are the same. + * + * The template parameter GatherScatter has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @param data Source and target of the communication. + */ + template<class GatherScatter, class Data> + void forward(Data& data); + + /** + * @brief Backward send where target and source are the same. + * + * The template parameter GatherScatter has to have a static method + * \code + * // Gather the data at index index of data + * static const typename CommPolicy<Data>::IndexedType>& gather(Data& data, int index); + * + * // Scatter the value at a index of data + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is SizeOne + * and + * + * \code + * static const typename CommPolicy<Data>::IndexedType> gather(Data& data, int index, int subindex); + * + * static void scatter(Data& data, typename CommPolicy<Data>::IndexedType> value, + * int index, int subindex); + * \endcode + * in the case where CommPolicy<Data>::IndexedTypeFlag is VariableSize. Here subindex is the + * subindex of the block at index. + * @param data Source and target of the communication. + */ + template<class GatherScatter, class Data> + void backward(Data& data); + + /** + * @brief Free the allocated memory (i.e. buffers and message information. + */ + void free(); + + /** + * @brief Destructor. + */ + ~BufferedCommunicator(); + + private: + + /** + * @brief The type of the map that maps interface information to processors. + */ + typedef std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > + InterfaceMap; + + + /** + * @brief Functors for message size caculation + */ + template<class Data, typename IndexedTypeFlag> + struct MessageSizeCalculator + {}; + + /** + * @brief Functor for message size caculation for datatypes + * where at each index is only one value. + */ + template<class Data> + struct MessageSizeCalculator<Data,SizeOne> + { + /** + * @brief Calculate the number of values in message + * @param info The information about the interface corresponding + * to the message. + * @return The number of values in th message. + */ + inline int operator()(const InterfaceInformation& info) const; + /** + * @brief Calculate the number of values in message + * + * @param info The information about the interface corresponding + * to the message. + * @param data ignored. + * @return The number of values in th message. + */ + inline int operator()(const Data& data, const InterfaceInformation& info) const; + }; + + /** + * @brief Functor for message size caculation for datatypes + * where at each index can be a variable number of values. + */ + template<class Data> + struct MessageSizeCalculator<Data,VariableSize> + { + /** + * @brief Calculate the number of values in message + * + * @param info The information about the interface corresponding + * to the message. + * @param data A representative of the data we send. + * @return The number of values in th message. + */ + inline int operator()(const Data& data, const InterfaceInformation& info) const; + }; + + /** + * @brief Functors for message data gathering. + */ + template<class Data, class GatherScatter, bool send, typename IndexedTypeFlag> + struct MessageGatherer + {}; + + /** + * @brief Functor for message data gathering for datatypes + * where at each index is only one value. + */ + template<class Data, class GatherScatter, bool send> + struct MessageGatherer<Data,GatherScatter,send,SizeOne> + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy<Data>::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Gatherer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copies the values to send into the buffer. + * @param interface The interface used in the send. + * @param data The data from which we copy the values. + * @param buffer The send buffer to copy to. + * @param bufferSize The size of the buffer in bytes. For checks. + */ + inline void operator()(const InterfaceMap& interface, const Data& data, Type* buffer, size_t bufferSize) const; + }; + + /** + * @brief Functor for message data scattering for datatypes + * where at each index can be a variable size of values + */ + template<class Data, class GatherScatter, bool send> + struct MessageGatherer<Data,GatherScatter,send,VariableSize> + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy<Data>::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Gatherer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copies the values to send into the buffer. + * @param interface The interface used in the send. + * @param data The data from which we copy the values. + * @param buffer The send buffer to copy to. + * @param bufferSize The size of the buffer in bytes. For checks. + */ + inline void operator()(const InterfaceMap& interface, const Data& data, Type* buffer, size_t bufferSize) const; + }; + + /** + * @brief Functors for message data scattering. + */ + template<class Data, class GatherScatter, bool send, typename IndexedTypeFlag> + struct MessageScatterer + {}; + + /** + * @brief Functor for message data gathering for datatypes + * where at each index is only one value. + */ + template<class Data, class GatherScatter, bool send> + struct MessageScatterer<Data,GatherScatter,send,SizeOne> + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy<Data>::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Scatterer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copy the message data from the receive buffer to the data. + * @param interface The interface used in the send. + * @param data The data to which we copy the values. + * @param buffer The receive buffer to copy from. + * @param proc The rank of the process the message is from. + */ + inline void operator()(const InterfaceMap& interface, Data& data, Type* buffer, const int& proc) const; + }; + /** + * @brief Functor for message data scattering for datatypes + * where at each index can be a variable size of values + */ + template<class Data, class GatherScatter, bool send> + struct MessageScatterer<Data,GatherScatter,send,VariableSize> + { + /** @brief The type of the values we send. */ + typedef typename CommPolicy<Data>::IndexedType Type; + + /** + * @brief The type of the functor that does the actual copying + * during the data Scattering. + */ + typedef GatherScatter Scatterer; + + enum { + /** + * @brief The communication mode + * + * True if this was a forward communication. + */ + forward=send + }; + + /** + * @brief Copy the message data from the receive buffer to the data. + * @param interface The interface used in the send. + * @param data The data to which we copy the values. + * @param buffer The receive buffer to copy from. + * @param proc The rank of the process the message is from. + */ + inline void operator()(const InterfaceMap& interface, Data& data, Type* buffer, const int& proc) const; + }; + + /** + * @brief Information about a message to send. + */ + struct MessageInformation + { + /** @brief Constructor. */ + MessageInformation() + : start_(0), size_(0) + {} + + /** + * @brief Constructor. + * @param start The start of the message in the global buffer. + * Not in bytes but in number of values from the beginning of + * the buffer + * @param size The size of the message in bytes. + */ + MessageInformation(size_t start, size_t size) + : start_(start), size_(size) + {} + /** + * @brief Start of the message in the buffer counted in number of value. + */ + size_t start_; + /** + * @brief Number of bytes in the message. + */ + size_t size_; + }; + + /** + * @brief Type of the map of information about the messages to send. + * + * The key is the process number to communicate with and the value is + * the pair of information about sending and receiving messages. + */ + typedef std::map<int,std::pair<MessageInformation,MessageInformation> > + InformationMap; + /** + * @brief Gathered information about the messages to send. + */ + InformationMap messageInformation_; + /** + * @brief Communication buffers. + */ + char* buffers_[2]; + /** + * @brief The size of the communication buffers + */ + size_t bufferSize_[2]; + + enum { + /** + * @brief The tag we use for communication. + */ + commTag_ + }; + + /** + * @brief The interface we currently work with. + */ + std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > interfaces_; + + MPI_Comm communicator_; + + /** + * @brief Send and receive Data. + */ + template<class GatherScatter, bool FORWARD, class Data> + void sendRecv(const Data& source, Data& target); + + }; #ifndef DOXYGEN -template<class V> -inline const void* CommPolicy<V>::getAddress(const V& v, int index) -{ -return &(v[index]); -} - -template<class V> -inline int CommPolicy<V>::getSize(const V& v, int index) -{ -DUNE_UNUSED_PARAMETER(v); -DUNE_UNUSED_PARAMETER(index); -return 1; -} - -template<class K, class A, int n> -inline const void* CommPolicy<VariableBlockVector<FieldVector<K, n>, A> >::getAddress(const Type& v, int index) -{ -return &(v[index][0]); -} - -template<class K, class A, int n> -inline int CommPolicy<VariableBlockVector<FieldVector<K, n>, A> >::getSize(const Type& v, int index) -{ -return v[index].getsize(); -} - -template<class T> -inline const typename CopyGatherScatter<T>::IndexedType& CopyGatherScatter<T>::gather(const T & vec, std::size_t i) -{ -return vec[i]; -} - -template<class T> -inline void CopyGatherScatter<T>::scatter(T& vec, const IndexedType& v, std::size_t i) -{ -vec[i]=v; -} - -template<typename T> -DatatypeCommunicator<T>::DatatypeCommunicator() -: remoteIndices_(0), created_(false) -{ -requests_[0]=0; -requests_[1]=0; -} - - - -template<typename T> -DatatypeCommunicator<T>::~DatatypeCommunicator() -{ -free(); -} - -template<typename T> -template<class T1, class T2, class V> -inline void DatatypeCommunicator<T>::build(const RemoteIndices& remoteIndices, -const T1& source, V& sendData, -const T2& destination, V& receiveData) -{ -remoteIndices_ = &remoteIndices; -free(); -createDataTypes<T1,T2,V,false>(source,destination, receiveData); -createDataTypes<T1,T2,V,true>(source,destination, sendData); -createRequests<V,true>(sendData, receiveData); -createRequests<V,false>(receiveData, sendData); -created_=true; -} - -template<typename T> -void DatatypeCommunicator<T>::free() -{ -if(created_) { -delete[] requests_[0]; -delete[] requests_[1]; -typedef MessageTypeMap::iterator iterator; -typedef MessageTypeMap::const_iterator const_iterator; - -const const_iterator end=messageTypes.end(); - -for(iterator process = messageTypes.begin(); process != end; ++process) { -MPI_Datatype *type = &(process->second.first); -int finalized=0; -MPI_Finalized(&finalized); -if(*type!=MPI_DATATYPE_NULL && !finalized) -MPI_Type_free(type); -type = &(process->second.second); -if(*type!=MPI_DATATYPE_NULL && !finalized) -MPI_Type_free(type); -} -messageTypes.clear(); -created_=false; -} - -} - -template<typename T> -template<class T1, class T2, class V, bool send> -void DatatypeCommunicator<T>::createDataTypes(const T1& sourceFlags, const T2& destFlags, V& data) -{ - -MPIDatatypeInformation<V> dataInfo(data); -this->template buildInterface<RemoteIndices,T1,T2,MPIDatatypeInformation<V>,send>(*remoteIndices_,sourceFlags, destFlags, dataInfo); - -typedef typename RemoteIndices::RemoteIndexMap::const_iterator const_iterator; -const const_iterator end=this->remoteIndices_->end(); - -// Allocate MPI_Datatypes and deallocate memory for the type construction. -for(const_iterator process=this->remoteIndices_->begin(); process != end; ++process) { -IndexedTypeInformation& info=dataInfo.information_[process->first]; -// Shift the displacement -MPI_Aint base; -MPI_Get_address(const_cast<void *>(CommPolicy<V>::getAddress(data, 0)), &base); - -for(int i=0; i< info.elements; i++) { -info.displ[i]-=base; -} - -// Create data type -MPI_Datatype* type = &( send ? messageTypes[process->first].first : messageTypes[process->first].second); -MPI_Type_create_hindexed(info.elements, info.length, info.displ, -MPITraits<typename CommPolicy<V>::IndexedType>::getType(), type); -MPI_Type_commit(type); -// Deallocate memory -info.free(); -} -} - -template<typename T> -template<class V, bool createForward> -void DatatypeCommunicator<T>::createRequests(V& sendData, V& receiveData) -{ -typedef std::map<int,std::pair<MPI_Datatype,MPI_Datatype> >::const_iterator MapIterator; -int rank; -static int index = createForward ? 1 : 0; -int noMessages = messageTypes.size(); -// allocate request handles -requests_[index] = new MPI_Request[2*noMessages]; -const MapIterator end = messageTypes.end(); -int request=0; -MPI_Comm_rank(MPI_COMM_WORLD, &rank); - -// Set up the requests for receiving first -for(MapIterator process = messageTypes.begin(); process != end; -++process, ++request) { -MPI_Datatype type = createForward ? process->second.second : process->second.first; -void* address = const_cast<void*>(CommPolicy<V>::getAddress(receiveData,0)); -MPI_Recv_init(address, 1, type, process->first, commTag_, this->remoteIndices_->communicator(), requests_[index]+request); -} - -// And now the send requests - -for(MapIterator process = messageTypes.begin(); process != end; -++process, ++request) { -MPI_Datatype type = createForward ? process->second.first : process->second.second; -void* address = const_cast<void*>(CommPolicy<V>::getAddress(sendData, 0)); -MPI_Ssend_init(address, 1, type, process->first, commTag_, this->remoteIndices_->communicator(), requests_[index]+request); -} -} - -template<typename T> -void DatatypeCommunicator<T>::forward() -{ -sendRecv(requests_[1]); -} - -template<typename T> -void DatatypeCommunicator<T>::backward() -{ -sendRecv(requests_[0]); -} - -template<typename T> -void DatatypeCommunicator<T>::sendRecv(MPI_Request* requests) -{ -int noMessages = messageTypes.size(); -// Start the receive calls first -MPI_Startall(noMessages, requests); -// Now the send calls -MPI_Startall(noMessages, requests+noMessages); - -// Wait for completion of the communication send first then receive -MPI_Status* status=new MPI_Status[2*noMessages]; -for(int i=0; i<2*noMessages; i++) -status[i].MPI_ERROR=MPI_SUCCESS; - -int send = MPI_Waitall(noMessages, requests+noMessages, status+noMessages); -int receive = MPI_Waitall(noMessages, requests, status); - -// Error checks -int success=1, globalSuccess=0; -if(send==MPI_ERR_IN_STATUS) { -int rank; -MPI_Comm_rank(this->remoteIndices_->communicator(), &rank); -std::cerr<<rank<<": Error in sending :"<<std::endl; -// Search for the error -for(int i=noMessages; i< 2*noMessages; i++) -if(status[i].MPI_ERROR!=MPI_SUCCESS) { -char message[300]; -int messageLength; -MPI_Error_string(status[i].MPI_ERROR, message, &messageLength); -std::cerr<<" source="<<status[i].MPI_SOURCE<<" message: "; -for(int j = 0; j < messageLength; j++) -std::cout << message[j]; -} -std::cerr<<std::endl; -success=0; -} - -if(receive==MPI_ERR_IN_STATUS) { -int rank; -MPI_Comm_rank(this->remoteIndices_->communicator(), &rank); -std::cerr<<rank<<": Error in receiving!"<<std::endl; -// Search for the error -for(int i=0; i< noMessages; i++) -if(status[i].MPI_ERROR!=MPI_SUCCESS) { -char message[300]; -int messageLength; -MPI_Error_string(status[i].MPI_ERROR, message, &messageLength); -std::cerr<<" source="<<status[i].MPI_SOURCE<<" message: "; -for(int j = 0; j < messageLength; j++) -std::cerr << message[j]; -} -std::cerr<<std::endl; -success=0; -} - -MPI_Allreduce(&success, &globalSuccess, 1, MPI_INT, MPI_MIN, this->remoteIndices_->communicator()); - -delete[] status; - -if(!globalSuccess) -DUNE_THROW(CommunicationError, "A communication error occurred!"); - -} - -inline BufferedCommunicator::BufferedCommunicator() -{ -buffers_[0]=0; -buffers_[1]=0; -bufferSize_[0]=0; -bufferSize_[1]=0; -} - -template<class Data, class Interface> -typename std::enable_if<std::is_same<SizeOne, typename CommPolicy<Data>::IndexedTypeFlag>::value, void>::type -BufferedCommunicator::build(const Interface& interface) -{ -interfaces_=interface.interfaces(); -communicator_=interface.communicator(); -typedef typename std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > -::const_iterator const_iterator; -typedef typename CommPolicy<Data>::IndexedTypeFlag Flag; -const const_iterator end = interfaces_.end(); -int lrank; -MPI_Comm_rank(communicator_, &lrank); - -bufferSize_[0]=0; -bufferSize_[1]=0; - -for(const_iterator interfacePair = interfaces_.begin(); -interfacePair != end; ++interfacePair) { -int noSend = MessageSizeCalculator<Data,Flag>() (interfacePair->second.first); -int noRecv = MessageSizeCalculator<Data,Flag>() (interfacePair->second.second); -if (noSend + noRecv > 0) -messageInformation_.insert(std::make_pair(interfacePair->first, -std::make_pair(MessageInformation(bufferSize_[0], -noSend*sizeof(typename CommPolicy<Data>::IndexedType)), -MessageInformation(bufferSize_[1], -noRecv*sizeof(typename CommPolicy<Data>::IndexedType))))); -bufferSize_[0] += noSend; -bufferSize_[1] += noRecv; -} - -// allocate the buffers -bufferSize_[0] *= sizeof(typename CommPolicy<Data>::IndexedType); -bufferSize_[1] *= sizeof(typename CommPolicy<Data>::IndexedType); - -buffers_[0] = new char[bufferSize_[0]]; -buffers_[1] = new char[bufferSize_[1]]; -} - -template<class Data, class Interface> -void BufferedCommunicator::build(const Data& source, const Data& dest, const Interface& interface) -{ - -interfaces_=interface.interfaces(); -communicator_=interface.communicator(); -typedef typename std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > -::const_iterator const_iterator; -typedef typename CommPolicy<Data>::IndexedTypeFlag Flag; -const const_iterator end = interfaces_.end(); - -bufferSize_[0]=0; -bufferSize_[1]=0; - -for(const_iterator interfacePair = interfaces_.begin(); -interfacePair != end; ++interfacePair) { -int noSend = MessageSizeCalculator<Data,Flag>() (source, interfacePair->second.first); -int noRecv = MessageSizeCalculator<Data,Flag>() (dest, interfacePair->second.second); -if (noSend + noRecv > 0) -messageInformation_.insert(std::make_pair(interfacePair->first, -std::make_pair(MessageInformation(bufferSize_[0], -noSend*sizeof(typename CommPolicy<Data>::IndexedType)), -MessageInformation(bufferSize_[1], -noRecv*sizeof(typename CommPolicy<Data>::IndexedType))))); -bufferSize_[0] += noSend; -bufferSize_[1] += noRecv; -} - -bufferSize_[0] *= sizeof(typename CommPolicy<Data>::IndexedType); -bufferSize_[1] *= sizeof(typename CommPolicy<Data>::IndexedType); -// allocate the buffers -buffers_[0] = new char[bufferSize_[0]]; -buffers_[1] = new char[bufferSize_[1]]; -} - -inline void BufferedCommunicator::free() -{ -messageInformation_.clear(); -if(buffers_[0]) -delete[] buffers_[0]; - -if(buffers_[1]) -delete[] buffers_[1]; -buffers_[0]=buffers_[1]=0; -} - -inline BufferedCommunicator::~BufferedCommunicator() -{ -free(); -} - -template<class Data> -inline int BufferedCommunicator::MessageSizeCalculator<Data,SizeOne>::operator() -(const InterfaceInformation& info) const -{ -return info.size(); -} - - -template<class Data> -inline int BufferedCommunicator::MessageSizeCalculator<Data,SizeOne>::operator() -(const Data&, const InterfaceInformation& info) const -{ -return operator()(info); -} - - -template<class Data> -inline int BufferedCommunicator::MessageSizeCalculator<Data, VariableSize>::operator() -(const Data& data, const InterfaceInformation& info) const -{ -int entries=0; - -for(size_t i=0; i < info.size(); i++) -entries += CommPolicy<Data>::getSize(data,info[i]); - -return entries; -} - - -template<class Data, class GatherScatter, bool FORWARD> -inline void BufferedCommunicator::MessageGatherer<Data,GatherScatter,FORWARD,VariableSize>::operator()(const InterfaceMap& interfaces,const Data& data, Type* buffer, size_t bufferSize) const -{ -DUNE_UNUSED_PARAMETER(bufferSize); -typedef typename InterfaceMap::const_iterator -const_iterator; - -int rank; -MPI_Comm_rank(MPI_COMM_WORLD, &rank); -const const_iterator end = interfaces.end(); -size_t index=0; - -for(const_iterator interfacePair = interfaces.begin(); -interfacePair != end; ++interfacePair) { -int size = forward ? interfacePair->second.first.size() : -interfacePair->second.second.size(); - -for(int i=0; i < size; i++) { -int local = forward ? interfacePair->second.first[i] : -interfacePair->second.second[i]; -for(std::size_t j=0; j < CommPolicy<Data>::getSize(data, local); j++, index++) { + template<class V> + inline const void* CommPolicy<V>::getAddress(const V& v, int index) + { + return &(v[index]); + } + + template<class V> + inline int CommPolicy<V>::getSize(const V& v, int index) + { + DUNE_UNUSED_PARAMETER(v); + DUNE_UNUSED_PARAMETER(index); + return 1; + } + + template<class K, class A, int n> + inline const void* CommPolicy<VariableBlockVector<FieldVector<K, n>, A> >::getAddress(const Type& v, int index) + { + return &(v[index][0]); + } + + template<class K, class A, int n> + inline int CommPolicy<VariableBlockVector<FieldVector<K, n>, A> >::getSize(const Type& v, int index) + { + return v[index].getsize(); + } + + template<class T> + inline const typename CopyGatherScatter<T>::IndexedType& CopyGatherScatter<T>::gather(const T & vec, std::size_t i) + { + return vec[i]; + } + + template<class T> + inline void CopyGatherScatter<T>::scatter(T& vec, const IndexedType& v, std::size_t i) + { + vec[i]=v; + } + + template<typename T> + DatatypeCommunicator<T>::DatatypeCommunicator() + : remoteIndices_(0), created_(false) + { + requests_[0]=0; + requests_[1]=0; + } + + + + template<typename T> + DatatypeCommunicator<T>::~DatatypeCommunicator() + { + free(); + } + + template<typename T> + template<class T1, class T2, class V> + inline void DatatypeCommunicator<T>::build(const RemoteIndices& remoteIndices, + const T1& source, V& sendData, + const T2& destination, V& receiveData) + { + remoteIndices_ = &remoteIndices; + free(); + createDataTypes<T1,T2,V,false>(source,destination, receiveData); + createDataTypes<T1,T2,V,true>(source,destination, sendData); + createRequests<V,true>(sendData, receiveData); + createRequests<V,false>(receiveData, sendData); + created_=true; + } + + template<typename T> + void DatatypeCommunicator<T>::free() + { + if(created_) { + delete[] requests_[0]; + delete[] requests_[1]; + typedef MessageTypeMap::iterator iterator; + typedef MessageTypeMap::const_iterator const_iterator; + + const const_iterator end=messageTypes.end(); + + for(iterator process = messageTypes.begin(); process != end; ++process) { + MPI_Datatype *type = &(process->second.first); + int finalized=0; + MPI_Finalized(&finalized); + if(*type!=MPI_DATATYPE_NULL && !finalized) + MPI_Type_free(type); + type = &(process->second.second); + if(*type!=MPI_DATATYPE_NULL && !finalized) + MPI_Type_free(type); + } + messageTypes.clear(); + created_=false; + } + + } + + template<typename T> + template<class T1, class T2, class V, bool send> + void DatatypeCommunicator<T>::createDataTypes(const T1& sourceFlags, const T2& destFlags, V& data) + { + + MPIDatatypeInformation<V> dataInfo(data); + this->template buildInterface<RemoteIndices,T1,T2,MPIDatatypeInformation<V>,send>(*remoteIndices_,sourceFlags, destFlags, dataInfo); + + typedef typename RemoteIndices::RemoteIndexMap::const_iterator const_iterator; + const const_iterator end=this->remoteIndices_->end(); + + // Allocate MPI_Datatypes and deallocate memory for the type construction. + for(const_iterator process=this->remoteIndices_->begin(); process != end; ++process) { + IndexedTypeInformation& info=dataInfo.information_[process->first]; + // Shift the displacement + MPI_Aint base; + MPI_Get_address(const_cast<void *>(CommPolicy<V>::getAddress(data, 0)), &base); + + for(int i=0; i< info.elements; i++) { + info.displ[i]-=base; + } + + // Create data type + MPI_Datatype* type = &( send ? messageTypes[process->first].first : messageTypes[process->first].second); + MPI_Type_create_hindexed(info.elements, info.length, info.displ, + MPITraits<typename CommPolicy<V>::IndexedType>::getType(), type); + MPI_Type_commit(type); + // Deallocate memory + info.free(); + } + } + + template<typename T> + template<class V, bool createForward> + void DatatypeCommunicator<T>::createRequests(V& sendData, V& receiveData) + { + typedef std::map<int,std::pair<MPI_Datatype,MPI_Datatype> >::const_iterator MapIterator; + int rank; + static int index = createForward ? 1 : 0; + int noMessages = messageTypes.size(); + // allocate request handles + requests_[index] = new MPI_Request[2*noMessages]; + const MapIterator end = messageTypes.end(); + int request=0; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + // Set up the requests for receiving first + for(MapIterator process = messageTypes.begin(); process != end; + ++process, ++request) { + MPI_Datatype type = createForward ? process->second.second : process->second.first; + void* address = const_cast<void*>(CommPolicy<V>::getAddress(receiveData,0)); + MPI_Recv_init(address, 1, type, process->first, commTag_, this->remoteIndices_->communicator(), requests_[index]+request); + } + + // And now the send requests + + for(MapIterator process = messageTypes.begin(); process != end; + ++process, ++request) { + MPI_Datatype type = createForward ? process->second.first : process->second.second; + void* address = const_cast<void*>(CommPolicy<V>::getAddress(sendData, 0)); + MPI_Ssend_init(address, 1, type, process->first, commTag_, this->remoteIndices_->communicator(), requests_[index]+request); + } + } + + template<typename T> + void DatatypeCommunicator<T>::forward() + { + sendRecv(requests_[1]); + } + + template<typename T> + void DatatypeCommunicator<T>::backward() + { + sendRecv(requests_[0]); + } + + template<typename T> + void DatatypeCommunicator<T>::sendRecv(MPI_Request* requests) + { + int noMessages = messageTypes.size(); + // Start the receive calls first + MPI_Startall(noMessages, requests); + // Now the send calls + MPI_Startall(noMessages, requests+noMessages); + + // Wait for completion of the communication send first then receive + MPI_Status* status=new MPI_Status[2*noMessages]; + for(int i=0; i<2*noMessages; i++) + status[i].MPI_ERROR=MPI_SUCCESS; + + int send = MPI_Waitall(noMessages, requests+noMessages, status+noMessages); + int receive = MPI_Waitall(noMessages, requests, status); + + // Error checks + int success=1, globalSuccess=0; + if(send==MPI_ERR_IN_STATUS) { + int rank; + MPI_Comm_rank(this->remoteIndices_->communicator(), &rank); + std::cerr<<rank<<": Error in sending :"<<std::endl; + // Search for the error + for(int i=noMessages; i< 2*noMessages; i++) + if(status[i].MPI_ERROR!=MPI_SUCCESS) { + char message[300]; + int messageLength; + MPI_Error_string(status[i].MPI_ERROR, message, &messageLength); + std::cerr<<" source="<<status[i].MPI_SOURCE<<" message: "; + for(int j = 0; j < messageLength; j++) + std::cout << message[j]; + } + std::cerr<<std::endl; + success=0; + } + + if(receive==MPI_ERR_IN_STATUS) { + int rank; + MPI_Comm_rank(this->remoteIndices_->communicator(), &rank); + std::cerr<<rank<<": Error in receiving!"<<std::endl; + // Search for the error + for(int i=0; i< noMessages; i++) + if(status[i].MPI_ERROR!=MPI_SUCCESS) { + char message[300]; + int messageLength; + MPI_Error_string(status[i].MPI_ERROR, message, &messageLength); + std::cerr<<" source="<<status[i].MPI_SOURCE<<" message: "; + for(int j = 0; j < messageLength; j++) + std::cerr << message[j]; + } + std::cerr<<std::endl; + success=0; + } + + MPI_Allreduce(&success, &globalSuccess, 1, MPI_INT, MPI_MIN, this->remoteIndices_->communicator()); + + delete[] status; + + if(!globalSuccess) + DUNE_THROW(CommunicationError, "A communication error occurred!"); + + } + + inline BufferedCommunicator::BufferedCommunicator() + { + buffers_[0]=0; + buffers_[1]=0; + bufferSize_[0]=0; + bufferSize_[1]=0; + } + + template<class Data, class Interface> + typename std::enable_if<std::is_same<SizeOne, typename CommPolicy<Data>::IndexedTypeFlag>::value, void>::type + BufferedCommunicator::build(const Interface& interface) + { + interfaces_=interface.interfaces(); + communicator_=interface.communicator(); + typedef typename std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > + ::const_iterator const_iterator; + typedef typename CommPolicy<Data>::IndexedTypeFlag Flag; + const const_iterator end = interfaces_.end(); + int lrank; + MPI_Comm_rank(communicator_, &lrank); + + bufferSize_[0]=0; + bufferSize_[1]=0; + + for(const_iterator interfacePair = interfaces_.begin(); + interfacePair != end; ++interfacePair) { + int noSend = MessageSizeCalculator<Data,Flag>() (interfacePair->second.first); + int noRecv = MessageSizeCalculator<Data,Flag>() (interfacePair->second.second); + if (noSend + noRecv > 0) + messageInformation_.insert(std::make_pair(interfacePair->first, + std::make_pair(MessageInformation(bufferSize_[0], + noSend*sizeof(typename CommPolicy<Data>::IndexedType)), + MessageInformation(bufferSize_[1], + noRecv*sizeof(typename CommPolicy<Data>::IndexedType))))); + bufferSize_[0] += noSend; + bufferSize_[1] += noRecv; + } + + // allocate the buffers + bufferSize_[0] *= sizeof(typename CommPolicy<Data>::IndexedType); + bufferSize_[1] *= sizeof(typename CommPolicy<Data>::IndexedType); + + buffers_[0] = new char[bufferSize_[0]]; + buffers_[1] = new char[bufferSize_[1]]; + } + + template<class Data, class Interface> + void BufferedCommunicator::build(const Data& source, const Data& dest, const Interface& interface) + { + + interfaces_=interface.interfaces(); + communicator_=interface.communicator(); + typedef typename std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > + ::const_iterator const_iterator; + typedef typename CommPolicy<Data>::IndexedTypeFlag Flag; + const const_iterator end = interfaces_.end(); + + bufferSize_[0]=0; + bufferSize_[1]=0; + + for(const_iterator interfacePair = interfaces_.begin(); + interfacePair != end; ++interfacePair) { + int noSend = MessageSizeCalculator<Data,Flag>() (source, interfacePair->second.first); + int noRecv = MessageSizeCalculator<Data,Flag>() (dest, interfacePair->second.second); + if (noSend + noRecv > 0) + messageInformation_.insert(std::make_pair(interfacePair->first, + std::make_pair(MessageInformation(bufferSize_[0], + noSend*sizeof(typename CommPolicy<Data>::IndexedType)), + MessageInformation(bufferSize_[1], + noRecv*sizeof(typename CommPolicy<Data>::IndexedType))))); + bufferSize_[0] += noSend; + bufferSize_[1] += noRecv; + } + + bufferSize_[0] *= sizeof(typename CommPolicy<Data>::IndexedType); + bufferSize_[1] *= sizeof(typename CommPolicy<Data>::IndexedType); + // allocate the buffers + buffers_[0] = new char[bufferSize_[0]]; + buffers_[1] = new char[bufferSize_[1]]; + } + + inline void BufferedCommunicator::free() + { + messageInformation_.clear(); + if(buffers_[0]) + delete[] buffers_[0]; + + if(buffers_[1]) + delete[] buffers_[1]; + buffers_[0]=buffers_[1]=0; + } + + inline BufferedCommunicator::~BufferedCommunicator() + { + free(); + } + + template<class Data> + inline int BufferedCommunicator::MessageSizeCalculator<Data,SizeOne>::operator() + (const InterfaceInformation& info) const + { + return info.size(); + } + + + template<class Data> + inline int BufferedCommunicator::MessageSizeCalculator<Data,SizeOne>::operator() + (const Data&, const InterfaceInformation& info) const + { + return operator()(info); + } + + + template<class Data> + inline int BufferedCommunicator::MessageSizeCalculator<Data, VariableSize>::operator() + (const Data& data, const InterfaceInformation& info) const + { + int entries=0; + + for(size_t i=0; i < info.size(); i++) + entries += CommPolicy<Data>::getSize(data,info[i]); + + return entries; + } + + + template<class Data, class GatherScatter, bool FORWARD> + inline void BufferedCommunicator::MessageGatherer<Data,GatherScatter,FORWARD,VariableSize>::operator()(const InterfaceMap& interfaces,const Data& data, Type* buffer, size_t bufferSize) const + { + DUNE_UNUSED_PARAMETER(bufferSize); + typedef typename InterfaceMap::const_iterator + const_iterator; + + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + const const_iterator end = interfaces.end(); + size_t index=0; + + for(const_iterator interfacePair = interfaces.begin(); + interfacePair != end; ++interfacePair) { + int size = forward ? interfacePair->second.first.size() : + interfacePair->second.second.size(); + + for(int i=0; i < size; i++) { + int local = forward ? interfacePair->second.first[i] : + interfacePair->second.second[i]; + for(std::size_t j=0; j < CommPolicy<Data>::getSize(data, local); j++, index++) { #ifdef DUNE_ISTL_WITH_CHECKING -assert(bufferSize>=(index+1)*sizeof(typename CommPolicy<Data>::IndexedType)); + assert(bufferSize>=(index+1)*sizeof(typename CommPolicy<Data>::IndexedType)); #endif -buffer[index]=GatherScatter::gather(data, local, j); -} + buffer[index]=GatherScatter::gather(data, local, j); + } -} -} + } + } -} + } -template<class Data, class GatherScatter, bool FORWARD> -inline void BufferedCommunicator::MessageGatherer<Data,GatherScatter,FORWARD,SizeOne>::operator()(const InterfaceMap& interfaces, const Data& data, Type* buffer, size_t bufferSize) const -{ -DUNE_UNUSED_PARAMETER(bufferSize); -typedef typename InterfaceMap::const_iterator -const_iterator; -const const_iterator end = interfaces.end(); -size_t index = 0; + template<class Data, class GatherScatter, bool FORWARD> + inline void BufferedCommunicator::MessageGatherer<Data,GatherScatter,FORWARD,SizeOne>::operator()(const InterfaceMap& interfaces, const Data& data, Type* buffer, size_t bufferSize) const + { + DUNE_UNUSED_PARAMETER(bufferSize); + typedef typename InterfaceMap::const_iterator + const_iterator; + const const_iterator end = interfaces.end(); + size_t index = 0; -int rank; -MPI_Comm_rank(MPI_COMM_WORLD, &rank); + int rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); -for(const_iterator interfacePair = interfaces.begin(); -interfacePair != end; ++interfacePair) { -size_t size = FORWARD ? interfacePair->second.first.size() : -interfacePair->second.second.size(); + for(const_iterator interfacePair = interfaces.begin(); + interfacePair != end; ++interfacePair) { + size_t size = FORWARD ? interfacePair->second.first.size() : + interfacePair->second.second.size(); -for(size_t i=0; i < size; i++) { + for(size_t i=0; i < size; i++) { #ifdef DUNE_ISTL_WITH_CHECKING -assert(bufferSize>=(index+1)*sizeof(typename CommPolicy<Data>::IndexedType)); + assert(bufferSize>=(index+1)*sizeof(typename CommPolicy<Data>::IndexedType)); #endif -buffer[index++] = GatherScatter::gather(data, FORWARD ? interfacePair->second.first[i] : -interfacePair->second.second[i]); -} -} + buffer[index++] = GatherScatter::gather(data, FORWARD ? interfacePair->second.first[i] : + interfacePair->second.second[i]); + } + } -} + } -template<class Data, class GatherScatter, bool FORWARD> -inline void BufferedCommunicator::MessageScatterer<Data,GatherScatter,FORWARD,VariableSize>::operator()(const InterfaceMap& interfaces, Data& data, Type* buffer, const int& proc) const -{ -typedef typename InterfaceMap::value_type::second_type::first_type Information; -const typename InterfaceMap::const_iterator infoPair = interfaces.find(proc); + template<class Data, class GatherScatter, bool FORWARD> + inline void BufferedCommunicator::MessageScatterer<Data,GatherScatter,FORWARD,VariableSize>::operator()(const InterfaceMap& interfaces, Data& data, Type* buffer, const int& proc) const + { + typedef typename InterfaceMap::value_type::second_type::first_type Information; + const typename InterfaceMap::const_iterator infoPair = interfaces.find(proc); -assert(infoPair!=interfaces.end()); + assert(infoPair!=interfaces.end()); -const Information& info = FORWARD ? infoPair->second.second : -infoPair->second.first; + const Information& info = FORWARD ? infoPair->second.second : + infoPair->second.first; -for(size_t i=0, index=0; i < info.size(); i++) { -for(size_t j=0; j < CommPolicy<Data>::getSize(data, info[i]); j++) -GatherScatter::scatter(data, buffer[index++], info[i], j); -} -} + for(size_t i=0, index=0; i < info.size(); i++) { + for(size_t j=0; j < CommPolicy<Data>::getSize(data, info[i]); j++) + GatherScatter::scatter(data, buffer[index++], info[i], j); + } + } -template<class Data, class GatherScatter, bool FORWARD> -inline void BufferedCommunicator::MessageScatterer<Data,GatherScatter,FORWARD,SizeOne>::operator()(const InterfaceMap& interfaces, Data& data, Type* buffer, const int& proc) const -{ -typedef typename InterfaceMap::value_type::second_type::first_type Information; -const typename InterfaceMap::const_iterator infoPair = interfaces.find(proc); + template<class Data, class GatherScatter, bool FORWARD> + inline void BufferedCommunicator::MessageScatterer<Data,GatherScatter,FORWARD,SizeOne>::operator()(const InterfaceMap& interfaces, Data& data, Type* buffer, const int& proc) const + { + typedef typename InterfaceMap::value_type::second_type::first_type Information; + const typename InterfaceMap::const_iterator infoPair = interfaces.find(proc); -assert(infoPair!=interfaces.end()); + assert(infoPair!=interfaces.end()); -const Information& info = FORWARD ? infoPair->second.second : -infoPair->second.first; + const Information& info = FORWARD ? infoPair->second.second : + infoPair->second.first; -for(size_t i=0; i < info.size(); i++) { -GatherScatter::scatter(data, buffer[i], info[i]); -} -} + for(size_t i=0; i < info.size(); i++) { + GatherScatter::scatter(data, buffer[i], info[i]); + } + } -template<class GatherScatter,class Data> -void BufferedCommunicator::forward(Data& data) -{ -this->template sendRecv<GatherScatter,true>(data, data); -} + template<class GatherScatter,class Data> + void BufferedCommunicator::forward(Data& data) + { + this->template sendRecv<GatherScatter,true>(data, data); + } -template<class GatherScatter, class Data> -void BufferedCommunicator::backward(Data& data) -{ -this->template sendRecv<GatherScatter,false>(data, data); -} + template<class GatherScatter, class Data> + void BufferedCommunicator::backward(Data& data) + { + this->template sendRecv<GatherScatter,false>(data, data); + } -template<class GatherScatter, class Data> -void BufferedCommunicator::forward(const Data& source, Data& dest) -{ -this->template sendRecv<GatherScatter,true>(source, dest); -} + template<class GatherScatter, class Data> + void BufferedCommunicator::forward(const Data& source, Data& dest) + { + this->template sendRecv<GatherScatter,true>(source, dest); + } -template<class GatherScatter, class Data> -void BufferedCommunicator::backward(Data& source, const Data& dest) -{ -this->template sendRecv<GatherScatter,false>(dest, source); -} + template<class GatherScatter, class Data> + void BufferedCommunicator::backward(Data& source, const Data& dest) + { + this->template sendRecv<GatherScatter,false>(dest, source); + } -template<class GatherScatter, bool FORWARD, class Data> -void BufferedCommunicator::sendRecv(const Data& source, Data& dest) -{ -int rank, lrank; + template<class GatherScatter, bool FORWARD, class Data> + void BufferedCommunicator::sendRecv(const Data& source, Data& dest) + { + int rank, lrank; -MPI_Comm_rank(MPI_COMM_WORLD,&rank); -MPI_Comm_rank(MPI_COMM_WORLD,&lrank); + MPI_Comm_rank(MPI_COMM_WORLD,&rank); + MPI_Comm_rank(MPI_COMM_WORLD,&lrank); -typedef typename CommPolicy<Data>::IndexedType Type; -Type *sendBuffer, *recvBuffer; -size_t sendBufferSize; + typedef typename CommPolicy<Data>::IndexedType Type; + Type *sendBuffer, *recvBuffer; + size_t sendBufferSize; #ifndef NDEBUG -size_t recvBufferSize; + size_t recvBufferSize; #endif -if(FORWARD) { -sendBuffer = reinterpret_cast<Type*>(buffers_[0]); -sendBufferSize = bufferSize_[0]; -recvBuffer = reinterpret_cast<Type*>(buffers_[1]); + if(FORWARD) { + sendBuffer = reinterpret_cast<Type*>(buffers_[0]); + sendBufferSize = bufferSize_[0]; + recvBuffer = reinterpret_cast<Type*>(buffers_[1]); #ifndef NDEBUG -recvBufferSize = bufferSize_[1]; + recvBufferSize = bufferSize_[1]; #endif -}else{ -sendBuffer = reinterpret_cast<Type*>(buffers_[1]); -sendBufferSize = bufferSize_[1]; -recvBuffer = reinterpret_cast<Type*>(buffers_[0]); + }else{ + sendBuffer = reinterpret_cast<Type*>(buffers_[1]); + sendBufferSize = bufferSize_[1]; + recvBuffer = reinterpret_cast<Type*>(buffers_[0]); #ifndef NDEBUG -recvBufferSize = bufferSize_[0]; + recvBufferSize = bufferSize_[0]; #endif -} -typedef typename CommPolicy<Data>::IndexedTypeFlag Flag; - -MessageGatherer<Data,GatherScatter,FORWARD,Flag>() (interfaces_, source, sendBuffer, sendBufferSize); - -MPI_Request* sendRequests = new MPI_Request[messageInformation_.size()]; -MPI_Request* recvRequests = new MPI_Request[messageInformation_.size()]; -/* Number of recvRequests that are not MPI_REQUEST_NULL */ -size_t numberOfRealRecvRequests = 0; - -// Setup receive first -typedef typename InformationMap::const_iterator const_iterator; - -const const_iterator end = messageInformation_.end(); -size_t i=0; -int* processMap = new int[messageInformation_.size()]; - -for(const_iterator info = messageInformation_.begin(); info != end; ++info, ++i) { -processMap[i]=info->first; -if(FORWARD) { -assert(info->second.second.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.second.size_ <= recvBufferSize ); -Dune::dvverb<<rank<<": receiving "<<info->second.second.size_<<" from "<<info->first<<std::endl; -if(info->second.second.size_) { -MPI_Irecv(recvBuffer+info->second.second.start_, info->second.second.size_, -MPI_BYTE, info->first, commTag_, communicator_, -recvRequests+i); -numberOfRealRecvRequests += 1; -} else { -// Nothing to receive -> set request to inactive -recvRequests[i]=MPI_REQUEST_NULL; -} -}else{ -assert(info->second.first.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.first.size_ <= recvBufferSize ); -Dune::dvverb<<rank<<": receiving "<<info->second.first.size_<<" to "<<info->first<<std::endl; -if(info->second.first.size_) { -MPI_Irecv(recvBuffer+info->second.first.start_, info->second.first.size_, -MPI_BYTE, info->first, commTag_, communicator_, -recvRequests+i); -numberOfRealRecvRequests += 1; -} else { -// Nothing to receive -> set request to inactive -recvRequests[i]=MPI_REQUEST_NULL; -} -} -} - -// now the send requests -i=0; -for(const_iterator info = messageInformation_.begin(); info != end; ++info, ++i) -if(FORWARD) { -assert(info->second.second.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.second.size_ <= recvBufferSize ); -Dune::dvverb<<rank<<": sending "<<info->second.first.size_<<" to "<<info->first<<std::endl; -assert(info->second.first.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.first.size_ <= sendBufferSize ); -if(info->second.first.size_) -MPI_Issend(sendBuffer+info->second.first.start_, info->second.first.size_, -MPI_BYTE, info->first, commTag_, communicator_, -sendRequests+i); -else -// Nothing to send -> set request to inactive -sendRequests[i]=MPI_REQUEST_NULL; -}else{ -assert(info->second.second.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.second.size_ <= sendBufferSize ); -Dune::dvverb<<rank<<": sending "<<info->second.second.size_<<" to "<<info->first<<std::endl; -if(info->second.second.size_) -MPI_Issend(sendBuffer+info->second.second.start_, info->second.second.size_, -MPI_BYTE, info->first, commTag_, communicator_, -sendRequests+i); -else -// Nothing to send -> set request to inactive -sendRequests[i]=MPI_REQUEST_NULL; -} - -// Wait for completion of receive and immediately start scatter -i=0; -//int success = 1; -int finished = MPI_UNDEFINED; -MPI_Status status; //[messageInformation_.size()]; -//MPI_Waitall(messageInformation_.size(), recvRequests, status); - -for(i=0; i< numberOfRealRecvRequests; i++) { -status.MPI_ERROR=MPI_SUCCESS; -MPI_Waitany(messageInformation_.size(), recvRequests, &finished, &status); -assert(finished != MPI_UNDEFINED); - -if(status.MPI_ERROR==MPI_SUCCESS) { -int& proc = processMap[finished]; -typename InformationMap::const_iterator infoIter = messageInformation_.find(proc); -assert(infoIter != messageInformation_.end()); - -MessageInformation info = (FORWARD) ? infoIter->second.second : infoIter->second.first; -assert(info.start_+info.size_ <= recvBufferSize); - -MessageScatterer<Data,GatherScatter,FORWARD,Flag>() (interfaces_, dest, recvBuffer+info.start_, proc); -}else{ -std::cerr<<rank<<": MPI_Error occurred while receiving message from "<<processMap[finished]<<std::endl; -//success=0; -} -} - -MPI_Status recvStatus; - -// Wait for completion of sends -for(i=0; i< messageInformation_.size(); i++) -if(MPI_SUCCESS!=MPI_Wait(sendRequests+i, &recvStatus)) { -std::cerr<<rank<<": MPI_Error occurred while sending message to "<<processMap[finished]<<std::endl; -//success=0; -} -/* -int globalSuccess; -MPI_Allreduce(&success, &globalSuccess, 1, MPI_INT, MPI_MIN, interface_->communicator()); - -if(!globalSuccess) -DUNE_THROW(CommunicationError, "A communication error occurred!"); -*/ -delete[] processMap; -delete[] sendRequests; -delete[] recvRequests; - -} + } + typedef typename CommPolicy<Data>::IndexedTypeFlag Flag; + + MessageGatherer<Data,GatherScatter,FORWARD,Flag>() (interfaces_, source, sendBuffer, sendBufferSize); + + MPI_Request* sendRequests = new MPI_Request[messageInformation_.size()]; + MPI_Request* recvRequests = new MPI_Request[messageInformation_.size()]; + /* Number of recvRequests that are not MPI_REQUEST_NULL */ + size_t numberOfRealRecvRequests = 0; + + // Setup receive first + typedef typename InformationMap::const_iterator const_iterator; + + const const_iterator end = messageInformation_.end(); + size_t i=0; + int* processMap = new int[messageInformation_.size()]; + + for(const_iterator info = messageInformation_.begin(); info != end; ++info, ++i) { + processMap[i]=info->first; + if(FORWARD) { + assert(info->second.second.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.second.size_ <= recvBufferSize ); + Dune::dvverb<<rank<<": receiving "<<info->second.second.size_<<" from "<<info->first<<std::endl; + if(info->second.second.size_) { + MPI_Irecv(recvBuffer+info->second.second.start_, info->second.second.size_, + MPI_BYTE, info->first, commTag_, communicator_, + recvRequests+i); + numberOfRealRecvRequests += 1; + } else { + // Nothing to receive -> set request to inactive + recvRequests[i]=MPI_REQUEST_NULL; + } + }else{ + assert(info->second.first.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.first.size_ <= recvBufferSize ); + Dune::dvverb<<rank<<": receiving "<<info->second.first.size_<<" to "<<info->first<<std::endl; + if(info->second.first.size_) { + MPI_Irecv(recvBuffer+info->second.first.start_, info->second.first.size_, + MPI_BYTE, info->first, commTag_, communicator_, + recvRequests+i); + numberOfRealRecvRequests += 1; + } else { + // Nothing to receive -> set request to inactive + recvRequests[i]=MPI_REQUEST_NULL; + } + } + } + + // now the send requests + i=0; + for(const_iterator info = messageInformation_.begin(); info != end; ++info, ++i) + if(FORWARD) { + assert(info->second.second.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.second.size_ <= recvBufferSize ); + Dune::dvverb<<rank<<": sending "<<info->second.first.size_<<" to "<<info->first<<std::endl; + assert(info->second.first.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.first.size_ <= sendBufferSize ); + if(info->second.first.size_) + MPI_Issend(sendBuffer+info->second.first.start_, info->second.first.size_, + MPI_BYTE, info->first, commTag_, communicator_, + sendRequests+i); + else + // Nothing to send -> set request to inactive + sendRequests[i]=MPI_REQUEST_NULL; + }else{ + assert(info->second.second.start_*sizeof(typename CommPolicy<Data>::IndexedType)+info->second.second.size_ <= sendBufferSize ); + Dune::dvverb<<rank<<": sending "<<info->second.second.size_<<" to "<<info->first<<std::endl; + if(info->second.second.size_) + MPI_Issend(sendBuffer+info->second.second.start_, info->second.second.size_, + MPI_BYTE, info->first, commTag_, communicator_, + sendRequests+i); + else + // Nothing to send -> set request to inactive + sendRequests[i]=MPI_REQUEST_NULL; + } + + // Wait for completion of receive and immediately start scatter + i=0; + //int success = 1; + int finished = MPI_UNDEFINED; + MPI_Status status; //[messageInformation_.size()]; + //MPI_Waitall(messageInformation_.size(), recvRequests, status); + + for(i=0; i< numberOfRealRecvRequests; i++) { + status.MPI_ERROR=MPI_SUCCESS; + MPI_Waitany(messageInformation_.size(), recvRequests, &finished, &status); + assert(finished != MPI_UNDEFINED); + + if(status.MPI_ERROR==MPI_SUCCESS) { + int& proc = processMap[finished]; + typename InformationMap::const_iterator infoIter = messageInformation_.find(proc); + assert(infoIter != messageInformation_.end()); + + MessageInformation info = (FORWARD) ? infoIter->second.second : infoIter->second.first; + assert(info.start_+info.size_ <= recvBufferSize); + + MessageScatterer<Data,GatherScatter,FORWARD,Flag>() (interfaces_, dest, recvBuffer+info.start_, proc); + }else{ + std::cerr<<rank<<": MPI_Error occurred while receiving message from "<<processMap[finished]<<std::endl; + //success=0; + } + } + + MPI_Status recvStatus; + + // Wait for completion of sends + for(i=0; i< messageInformation_.size(); i++) + if(MPI_SUCCESS!=MPI_Wait(sendRequests+i, &recvStatus)) { + std::cerr<<rank<<": MPI_Error occurred while sending message to "<<processMap[finished]<<std::endl; + //success=0; + } + /* + int globalSuccess; + MPI_Allreduce(&success, &globalSuccess, 1, MPI_INT, MPI_MIN, interface_->communicator()); + + if(!globalSuccess) + DUNE_THROW(CommunicationError, "A communication error occurred!"); + */ + delete[] processMap; + delete[] sendRequests; + delete[] recvRequests; + + } #endif // DOXYGEN -/** @} */ + /** @} */ } #endif // HAVE_MPI diff --git a/dune/common/parallel/future.hh b/dune/common/parallel/future.hh index 3587ce135a9c376ac687e0d21a77f521ad1eb7cc..709e8583573520dd9836339725743e3fa1abe146 100644 --- a/dune/common/parallel/future.hh +++ b/dune/common/parallel/future.hh @@ -9,180 +9,180 @@ namespace Dune{ -/*! \brief This exception is thrown when `ready()`, `wait()` or `get()` is -called on an invalid future. A future is valid until `get()` is called and -if it is not default-constructed and it was not moved from. -*/ -class InvalidFutureException : public InvalidStateException -{}; - -// forward declaration -template<class T> -class PseudoFuture; - -/*! \brief Type-erasure for future-like objects. A future-like object is a -object satisfying the interface of FutureBase. -*/ -template<class T> -class Future{ -// Future interface: -class FutureBase{ -public: -virtual ~FutureBase() = default; -virtual void wait() = 0; -virtual bool ready() const = 0; -virtual bool valid() const = 0; -virtual T get() = 0; -}; - -// model class -template<class F> -class FutureModel -: public FutureBase -{ -F _future; -public: -FutureModel(F&& f) -: _future(std::forward<F>(f)) -{} - -virtual void wait() override -{ -_future.wait(); -} - -virtual bool ready() const override -{ -return _future.ready(); -} - -virtual bool valid() const override -{ -return _future.valid(); -} - -virtual T get() override{ -return (T)_future.get(); -} -}; - -std::unique_ptr<FutureBase> _future; -public: -template<class F> -Future(F&& f) -: _future(std::make_unique<FutureModel<F>>(std::forward<F>(f))) -{} - -template<class U, std::enable_if_t<std::is_same<U,T>::value && !std::is_same<T,void>::value>> -Future(U&& data) -: _future(std::make_unique<FutureModel<PseudoFuture<T>>>(PseudoFuture<T>(std::forward<U>(data)))) -{} - -Future() = default; - -/*! \brief wait until the future is ready -\throws InvalidFutureException -*/ -void wait(){ -_future->wait(); -} - -/*! \brief Waits until the future is ready and returns the resulting value -\returns The contained value -\throws InvalidFutureException -*/ -T get() { -return _future->get(); -} - -/*! \brief -\returns true is the future is ready, otherwise false -\throws InvalidFutureException -*/ -bool ready() const { -return _future->ready(); -} - -/*! \brief Checks whether the future is valid. I.e. `get()' was not called -on that future and when it was not default-constructed and not moved -from. -\returns true is the future is valid, otherwise false -*/ -bool valid() const { -if(_future) -return _future->valid(); -return false; -} -}; - -/*! \brief A wrapper-class for a object which is ready immediately. -*/ -template<class T> -class PseudoFuture{ -bool valid_; -T data_; -public: -PseudoFuture() : -valid_(false) -{} - -template<class U> -PseudoFuture(U&& u) : -valid_(true), -data_(std::forward<U>(u)) -{} - -void wait() { -if(!valid_) -DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); -} - -bool ready() const { -if(!valid_) -DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); -return true; -} - -T get() { -if(!valid_) -DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); -valid_ = false; -return std::forward<T>(data_); -} - -bool valid() const { -return valid_; -} -}; - -template<> -class PseudoFuture<void>{ -bool valid_; -public: -PseudoFuture(bool valid = false) : -valid_(valid) -{} - -void wait(){ -if(!valid_) -DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); -} -bool ready() const{ -if(!valid_) -DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); -return true; -} - -void get(){ -if(!valid_) -DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); -valid_ = false; -} - -bool valid() const{ -return valid_; -} -}; + /*! \brief This exception is thrown when `ready()`, `wait()` or `get()` is + called on an invalid future. A future is valid until `get()` is called and + if it is not default-constructed and it was not moved from. + */ + class InvalidFutureException : public InvalidStateException + {}; + + // forward declaration + template<class T> + class PseudoFuture; + + /*! \brief Type-erasure for future-like objects. A future-like object is a + object satisfying the interface of FutureBase. + */ + template<class T> + class Future{ + // Future interface: + class FutureBase{ + public: + virtual ~FutureBase() = default; + virtual void wait() = 0; + virtual bool ready() const = 0; + virtual bool valid() const = 0; + virtual T get() = 0; + }; + + // model class + template<class F> + class FutureModel + : public FutureBase + { + F _future; + public: + FutureModel(F&& f) + : _future(std::forward<F>(f)) + {} + + virtual void wait() override + { + _future.wait(); + } + + virtual bool ready() const override + { + return _future.ready(); + } + + virtual bool valid() const override + { + return _future.valid(); + } + + virtual T get() override{ + return (T)_future.get(); + } + }; + + std::unique_ptr<FutureBase> _future; + public: + template<class F> + Future(F&& f) + : _future(std::make_unique<FutureModel<F>>(std::forward<F>(f))) + {} + + template<class U, std::enable_if_t<std::is_same<U,T>::value && !std::is_same<T,void>::value>> + Future(U&& data) + : _future(std::make_unique<FutureModel<PseudoFuture<T>>>(PseudoFuture<T>(std::forward<U>(data)))) + {} + + Future() = default; + + /*! \brief wait until the future is ready + \throws InvalidFutureException + */ + void wait(){ + _future->wait(); + } + + /*! \brief Waits until the future is ready and returns the resulting value + \returns The contained value + \throws InvalidFutureException + */ + T get() { + return _future->get(); + } + + /*! \brief + \returns true is the future is ready, otherwise false + \throws InvalidFutureException + */ + bool ready() const { + return _future->ready(); + } + + /*! \brief Checks whether the future is valid. I.e. `get()' was not called + on that future and when it was not default-constructed and not moved + from. + \returns true is the future is valid, otherwise false + */ + bool valid() const { + if(_future) + return _future->valid(); + return false; + } + }; + + /*! \brief A wrapper-class for a object which is ready immediately. + */ + template<class T> + class PseudoFuture{ + bool valid_; + T data_; + public: + PseudoFuture() : + valid_(false) + {} + + template<class U> + PseudoFuture(U&& u) : + valid_(true), + data_(std::forward<U>(u)) + {} + + void wait() { + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + } + + bool ready() const { + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + return true; + } + + T get() { + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + valid_ = false; + return std::forward<T>(data_); + } + + bool valid() const { + return valid_; + } + }; + + template<> + class PseudoFuture<void>{ + bool valid_; + public: + PseudoFuture(bool valid = false) : + valid_(valid) + {} + + void wait(){ + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + } + bool ready() const{ + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + return true; + } + + void get(){ + if(!valid_) + DUNE_THROW(InvalidFutureException, "The PseudoFuture is not valid"); + valid_ = false; + } + + bool valid() const{ + return valid_; + } + }; } #endif diff --git a/dune/common/parallel/indexset.hh b/dune/common/parallel/indexset.hh index 65263390d0f535c0ed874230aba1ee4f0312bb83..32d55517a800f74f00a7a0a6d09b93f66da84f77 100644 --- a/dune/common/parallel/indexset.hh +++ b/dune/common/parallel/indexset.hh @@ -16,1146 +16,1146 @@ namespace Dune { -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Provides a map between global and local indices. -* @author Markus Blatt -*/ -// forward declarations - -template<class TG, class TL> -class IndexPair; - -/** -* @brief Print an index pair. -* @param os The outputstream to print to. -* @param pair The index pair to print. -*/ -template<class TG, class TL> -std::ostream& operator<<(std::ostream& os, const IndexPair<TG,TL>& pair); - -template<class TG, class TL> -bool operator==(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); - -template<class TG, class TL> -bool operator!=(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); - -template<class TG, class TL> -bool operator<(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); - -template<class TG, class TL> -bool operator>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); - -template<class TG, class TL> -bool operator<=(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); - -template<class TG, class TL> -bool operator >=(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); - -template<class TG, class TL> -bool operator==(const IndexPair<TG,TL>&, const TG&); - -template<class TG, class TL> -bool operator!=(const IndexPair<TG,TL>&, const TG&); - -template<class TG, class TL> -bool operator<(const IndexPair<TG,TL>&, const TG&); - -template<class TG, class TL> -bool operator>(const IndexPair<TG,TL>&, const TG&); - -template<class TG, class TL> -bool operator<=(const IndexPair<TG,TL>&, const TG&); - -template<class TG, class TL> -bool operator >=(const IndexPair<TG,TL>&, const TG&); - -template<typename T> -struct MPITraits; - -/** -* @brief A pair consisting of a global and local index. -*/ -template<class TG, class TL> -class IndexPair -{ -friend std::ostream& operator<<<>(std::ostream&, const IndexPair<TG,TL>&); -friend bool operator==<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); -friend bool operator!=<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); -friend bool operator< <>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); -friend bool operator><>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); -friend bool operator<=<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); -friend bool operator>=<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); -friend bool operator==<>(const IndexPair<TG,TL>&, const TG &); -friend bool operator!=<>(const IndexPair<TG,TL>&, const TG &); -friend bool operator< <>(const IndexPair<TG,TL>&, const TG &); -friend bool operator> <>(const IndexPair<TG,TL>&, const TG &); -friend bool operator<=<>(const IndexPair<TG,TL>&, const TG &); -friend bool operator>=<>(const IndexPair<TG,TL>&, const TG &); -friend struct MPITraits<IndexPair<TG,TL> >; - -public: -/** -* @brief the type of the global index. -* -* This type has to provide at least a operator< for sorting. -*/ -typedef TG GlobalIndex; - -/** -* @brief the type of the local index. -* -* This class to provide the following functions: -* \code -* LocalIndex operator=(int); -* operator int() const; -* LocalIndexState state() const; -* void setState(LocalIndexState); -* \endcode -*/ -typedef TL LocalIndex; - -/** -* @brief Constructs a new Pair. -* -* @param global The global index. -* @param local The local index. -*/ -IndexPair(const GlobalIndex& global, const LocalIndex& local); - -/** -* @brief Construct a new Pair. -*/ -IndexPair(); -/** -* @brief Constructs a new Pair. -* -* The local index will be 0. -* @param global The global index. -*/ -IndexPair(const GlobalIndex& global); - -/** -* @brief Get the global index. -* -* @return The global index. -*/ -inline const GlobalIndex& global() const; - -/** -* @brief Get the local index. -* -* @return The local index. -*/ -inline LocalIndex& local(); - -/** -* @brief Get the local index. -* -* @return The local index. -*/ -inline const LocalIndex& local() const; - -/** -* @brief Set the local index. -* -* @param index The index to set it to. -*/ -inline void setLocal(int index); -private: -/** @brief The global index. */ -GlobalIndex global_; -/** @brief The local index. */ -LocalIndex local_; -}; - -/** -* @brief The states the index set can be in. -* @see ParallelIndexSet::state_ -*/ -enum ParallelIndexSetState -{ -/** -* @brief The default mode. -* Indicates that the index set is ready to be used. -*/ -GROUND, -/** -* @brief Indicates that the index set is currently being resized. -*/ -RESIZE -/** -* @brief Indicates that all previously deleted indices are now deleted. -* -CLEAN, -** -* @brief Indicates that the index set is currently being reordered. -* -REORDER -*/ -}; - -/** -* @brief Exception indicating that the index set is not in the expected state. -*/ -class InvalidIndexSetState : public InvalidStateException {}; - -// Forward declaration -template<class I> class GlobalLookupIndexSet; - -/** -* @brief Manager class for the mapping between local indices and globally unique indices. -* -* The mapping is between a globally unique id and local index. The local index is consecutive -* and non persistent while the global id might not be consecutive but definitely is persistent. -*/ -template<typename TG, typename TL, int N=100> -class ParallelIndexSet -{ -friend class GlobalLookupIndexSet<ParallelIndexSet<TG,TL,N> >; - -public: -/** -* @brief the type of the global index. -* This type has to provide at least a operator< for sorting. -*/ -typedef TG GlobalIndex; - -/** -* @brief The type of the local index, e.g. ParallelLocalIndex. -* -* This class to provide the following functions: -* \code -* LocalIndex operator=(int); -* operator int() const; -* LocalIndexState state() const; -* void setState(LocalIndexState); -* \endcode -*/ -typedef TL LocalIndex; - -/** -* @brief The type of the pair stored. -*/ -typedef Dune::IndexPair<GlobalIndex,LocalIndex> IndexPair; - -enum { -/** -* @brief The size of the individual arrays in the underlying ArrayList. -* -* The default value is 100. -* @see ArrayList::size -*/ -arraySize= (N>0) ? N : 1 -}; - -/** @brief The iterator over the pairs. */ -class iterator : -public ArrayList<IndexPair,N>::iterator -{ -typedef typename ArrayList<IndexPair,N>::iterator -Father; -friend class ParallelIndexSet<GlobalIndex,LocalIndex,N>; -public: -iterator(ParallelIndexSet<TG,TL,N>& indexSet, const Father& father) -: Father(father), indexSet_(&indexSet) -{} - -private: -/** -* @brief Mark the index as deleted. -* -* The deleted flag will be set in the local index. -* The index will be removed in the endResize method of the -* index set. -* -* @exception InvalidIndexSetState only when NDEBUG is not defined -*/ -inline void markAsDeleted() const -{ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides a map between global and local indices. + * @author Markus Blatt + */ + // forward declarations + + template<class TG, class TL> + class IndexPair; + + /** + * @brief Print an index pair. + * @param os The outputstream to print to. + * @param pair The index pair to print. + */ + template<class TG, class TL> + std::ostream& operator<<(std::ostream& os, const IndexPair<TG,TL>& pair); + + template<class TG, class TL> + bool operator==(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + + template<class TG, class TL> + bool operator!=(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + + template<class TG, class TL> + bool operator<(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + + template<class TG, class TL> + bool operator>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + + template<class TG, class TL> + bool operator<=(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + + template<class TG, class TL> + bool operator >=(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + + template<class TG, class TL> + bool operator==(const IndexPair<TG,TL>&, const TG&); + + template<class TG, class TL> + bool operator!=(const IndexPair<TG,TL>&, const TG&); + + template<class TG, class TL> + bool operator<(const IndexPair<TG,TL>&, const TG&); + + template<class TG, class TL> + bool operator>(const IndexPair<TG,TL>&, const TG&); + + template<class TG, class TL> + bool operator<=(const IndexPair<TG,TL>&, const TG&); + + template<class TG, class TL> + bool operator >=(const IndexPair<TG,TL>&, const TG&); + + template<typename T> + struct MPITraits; + + /** + * @brief A pair consisting of a global and local index. + */ + template<class TG, class TL> + class IndexPair + { + friend std::ostream& operator<<<>(std::ostream&, const IndexPair<TG,TL>&); + friend bool operator==<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + friend bool operator!=<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + friend bool operator< <>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + friend bool operator><>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + friend bool operator<=<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + friend bool operator>=<>(const IndexPair<TG,TL>&, const IndexPair<TG,TL>&); + friend bool operator==<>(const IndexPair<TG,TL>&, const TG &); + friend bool operator!=<>(const IndexPair<TG,TL>&, const TG &); + friend bool operator< <>(const IndexPair<TG,TL>&, const TG &); + friend bool operator> <>(const IndexPair<TG,TL>&, const TG &); + friend bool operator<=<>(const IndexPair<TG,TL>&, const TG &); + friend bool operator>=<>(const IndexPair<TG,TL>&, const TG &); + friend struct MPITraits<IndexPair<TG,TL> >; + + public: + /** + * @brief the type of the global index. + * + * This type has to provide at least a operator< for sorting. + */ + typedef TG GlobalIndex; + + /** + * @brief the type of the local index. + * + * This class to provide the following functions: + * \code + * LocalIndex operator=(int); + * operator int() const; + * LocalIndexState state() const; + * void setState(LocalIndexState); + * \endcode + */ + typedef TL LocalIndex; + + /** + * @brief Constructs a new Pair. + * + * @param global The global index. + * @param local The local index. + */ + IndexPair(const GlobalIndex& global, const LocalIndex& local); + + /** + * @brief Construct a new Pair. + */ + IndexPair(); + /** + * @brief Constructs a new Pair. + * + * The local index will be 0. + * @param global The global index. + */ + IndexPair(const GlobalIndex& global); + + /** + * @brief Get the global index. + * + * @return The global index. + */ + inline const GlobalIndex& global() const; + + /** + * @brief Get the local index. + * + * @return The local index. + */ + inline LocalIndex& local(); + + /** + * @brief Get the local index. + * + * @return The local index. + */ + inline const LocalIndex& local() const; + + /** + * @brief Set the local index. + * + * @param index The index to set it to. + */ + inline void setLocal(int index); + private: + /** @brief The global index. */ + GlobalIndex global_; + /** @brief The local index. */ + LocalIndex local_; + }; + + /** + * @brief The states the index set can be in. + * @see ParallelIndexSet::state_ + */ + enum ParallelIndexSetState + { + /** + * @brief The default mode. + * Indicates that the index set is ready to be used. + */ + GROUND, + /** + * @brief Indicates that the index set is currently being resized. + */ + RESIZE + /** + * @brief Indicates that all previously deleted indices are now deleted. + * + CLEAN, + ** + * @brief Indicates that the index set is currently being reordered. + * + REORDER + */ + }; + + /** + * @brief Exception indicating that the index set is not in the expected state. + */ + class InvalidIndexSetState : public InvalidStateException {}; + + // Forward declaration + template<class I> class GlobalLookupIndexSet; + + /** + * @brief Manager class for the mapping between local indices and globally unique indices. + * + * The mapping is between a globally unique id and local index. The local index is consecutive + * and non persistent while the global id might not be consecutive but definitely is persistent. + */ + template<typename TG, typename TL, int N=100> + class ParallelIndexSet + { + friend class GlobalLookupIndexSet<ParallelIndexSet<TG,TL,N> >; + + public: + /** + * @brief the type of the global index. + * This type has to provide at least a operator< for sorting. + */ + typedef TG GlobalIndex; + + /** + * @brief The type of the local index, e.g. ParallelLocalIndex. + * + * This class to provide the following functions: + * \code + * LocalIndex operator=(int); + * operator int() const; + * LocalIndexState state() const; + * void setState(LocalIndexState); + * \endcode + */ + typedef TL LocalIndex; + + /** + * @brief The type of the pair stored. + */ + typedef Dune::IndexPair<GlobalIndex,LocalIndex> IndexPair; + + enum { + /** + * @brief The size of the individual arrays in the underlying ArrayList. + * + * The default value is 100. + * @see ArrayList::size + */ + arraySize= (N>0) ? N : 1 + }; + + /** @brief The iterator over the pairs. */ + class iterator : + public ArrayList<IndexPair,N>::iterator + { + typedef typename ArrayList<IndexPair,N>::iterator + Father; + friend class ParallelIndexSet<GlobalIndex,LocalIndex,N>; + public: + iterator(ParallelIndexSet<TG,TL,N>& indexSet, const Father& father) + : Father(father), indexSet_(&indexSet) + {} + + private: + /** + * @brief Mark the index as deleted. + * + * The deleted flag will be set in the local index. + * The index will be removed in the endResize method of the + * index set. + * + * @exception InvalidIndexSetState only when NDEBUG is not defined + */ + inline void markAsDeleted() const + { #ifndef NDEBUG -if(indexSet_->state_ != RESIZE) -DUNE_THROW(InvalidIndexSetState, "Indices can only be removed " -<<"while in RESIZE state!"); + if(indexSet_->state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be removed " + <<"while in RESIZE state!"); #endif -Father::operator*().local().setState(DELETED); -} - -/** @brief The index set we are an iterator of. */ -ParallelIndexSet<TG,TL,N>* indexSet_; - -}; - - - -/** @brief The constant iterator over the pairs. */ -typedef typename -ArrayList<IndexPair,N>::const_iterator -const_iterator; - -/** -* @brief Constructor. -*/ -ParallelIndexSet(); - -/** -* @brief Get the state the index set is in. -* @return The state of the index set. -*/ -inline const ParallelIndexSetState& state() -{ -return state_; -} - -/** -* @brief Indicate that the index set is to be resized. -* @exception InvalidState If index set was not in -* ParallelIndexSetState::GROUND mode. -*/ -void beginResize(); - -/** -* @brief Add an new index to the set. -* -* The local index is created by the default constructor. -* @param global The globally unique id of the index. -* @exception InvalidState If index set is not in -* ParallelIndexSetState::RESIZE mode. -*/ -inline void add(const GlobalIndex& global); - -/** -* @brief Add an new index to the set. -* -* @param global The globally unique id of the index. -* @param local The local index. -* @exception InvalidState If index set is not in -* ParallelIndexSetState::RESIZE mode. -*/ -inline void add(const GlobalIndex& global, const LocalIndex& local); - -/** -* @brief Mark an index as deleted. -* -* The index will be deleted during endResize(). -* @param position An iterator at the position we want to delete. -* @exception InvalidState If index set is not in ParallelIndexSetState::RESIZE mode. -*/ -inline void markAsDeleted(const iterator& position); - -/** -* @brief Indicate that the resizing finishes. -* -* @warning Invalidates all pointers stored to the elements of this index set. -* The local indices will be ordered -* according to the global indices: -* Let \f$(g_i,l_i)_{i=0}^N \f$ be the set of all indices then \f$l_i < l_j\f$ -* if and -* only if \f$g_i < g_j\f$ for arbitrary \f$i \neq j\f$. -* @exception InvalidState If index set was not in -* ParallelIndexSetState::RESIZE mode. -*/ -void endResize(); - -/** -* @brief Find the index pair with a specific global id. -* -* This starts a binary search for the entry and therefore has complexity -* log(N). -* @param global The globally unique id of the pair. -* @return The pair of indices for the id. -* @warning If the global index is not in the set a wrong or even a -* null reference might be returned. To be save use the throwing alternative at. -*/ -inline IndexPair& -operator[](const GlobalIndex& global); - -/** -* @brief Find the index pair with a specific global id. -* -* This starts a binary search for the entry and therefore has complexity -* log(N). -* @param global The globally unique id of the pair. -* @return The pair of indices for the id. -* @exception RangeError Thrown if the global id is not known. -*/ -inline IndexPair& -at(const GlobalIndex& global); - -/** -* @brief Find the index pair with a specific global id. -* -* This starts a binary search for the entry and therefore has complexity -* log(N). -* @param global The globally unique id of the pair. -* @return The pair of indices for the id. -* @exception RangeError Thrown if the global id is not known. -*/ -inline bool -exists (const GlobalIndex& global) const; - -/** -* @brief Find the index pair with a specific global id. -* -* This starts a binary search for the entry and therefore has complexity -* log(N). -* @param global The globally unique id of the pair. -* @return The pair of indices for the id. -* @warning If the global index is not in the set a wrong or even a -* null reference might be returned. To be save use the throwing alternative at. -*/ -inline const IndexPair& -operator[](const GlobalIndex& global) const; - -/** -* @brief Find the index pair with a specific global id. -* -* This starts a binary search for the entry and therefore has complexity -* log(N). -* @param global The globally unique id of the pair. -* @return The pair of indices for the id. -* @exception RangeError Thrown if the global id is not known. -*/ -inline const IndexPair& -at(const GlobalIndex& global) const; - -/** -* @brief Get an iterator over the indices positioned at the first index. -* @return Iterator over the local indices. -*/ -inline iterator begin(); - -/** -* @brief Get an iterator over the indices positioned after the last index. -* @return Iterator over the local indices. -*/ -inline iterator end(); - -/** -* @brief Get an iterator over the indices positioned at the first index. -* @return Iterator over the local indices. -*/ -inline const_iterator begin() const; - -/** -* @brief Get an iterator over the indices positioned after the last index. -* @return Iterator over the local indices. -*/ -inline const_iterator end() const; - -/** -* @brief Renumbers the local index numbers. -* -* After this function returns the indices are -* consecutively numbered beginning from 0. Let -* $(g_i,l_i)$, $(g_j,l_j)$ be two arbitrary index -* pairs with $g_i<g_j$ then after renumbering -* $l_i<l_j$ will hold. -*/ -inline void renumberLocal(); - -/** -* @brief Get the internal sequence number. -* -* Is initially 0 is incremented for each resize. -* @return The sequence number. -*/ -inline int seqNo() const; - -/** -* @brief Get the total number (public and nonpublic) indices. -* @return The total number (public and nonpublic) indices. -*/ -inline size_t size() const; - -private: -/** @brief The index pairs. */ -ArrayList<IndexPair,N> localIndices_; -/** @brief The new indices for the RESIZE state. */ -ArrayList<IndexPair,N> newIndices_; -/** @brief The state of the index set. */ -ParallelIndexSetState state_; -/** @brief Number to keep track of the number of resizes. */ -int seqNo_; -/** @brief Whether entries were deleted in resize mode. */ -bool deletedEntries_; -/** -* @brief Merges the _localIndices and newIndices arrays and creates a new -* localIndices array. -*/ -inline void merge(); -}; - - -/** -* @brief Print an index set. -* @param os The outputstream to print to. -* @param indexSet The index set to print. -*/ -template<class TG, class TL, int N> -std::ostream& operator<<(std::ostream& os, const ParallelIndexSet<TG,TL,N>& indexSet); - -/** -* @brief Decorates an index set with the possibility to find a global index -* that is mapped to a specific local. -* -*/ -template<class I> -class GlobalLookupIndexSet -{ -public: -/** -* @brief The type of the index set. -*/ -typedef I ParallelIndexSet; - -/** -* @brief The type of the local index. -*/ -typedef typename ParallelIndexSet::LocalIndex LocalIndex; - -/** -* @brief The type of the global index. -*/ -typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; - -/** -* @brief The iterator over the index pairs. -*/ -typedef typename ParallelIndexSet::const_iterator const_iterator; - -typedef Dune::IndexPair<typename I::GlobalIndex, typename I::LocalIndex> IndexPair; - -/** -* @brief Constructor. -* @param indexset The index set we want to be able to lookup the corresponding -* global index of a local index. -* @param size The number of indices present, i.e. one more than the maximum local index. -*/ -GlobalLookupIndexSet(const ParallelIndexSet& indexset, std::size_t size); - -/** -* @brief Constructor. -* @param indexset The index set we want to be able to lookup the corresponding -* global index of a local index. -*/ -GlobalLookupIndexSet(const ParallelIndexSet& indexset); - -/** -* @brief Destructor. -*/ -~GlobalLookupIndexSet(); - -/** -* @brief Find the index pair with a specific global id. -* -* This starts a binary search for the entry and therefore has complexity -* log(N). This method is forwarded to the underlying index set. -* @param global The globally unique id of the pair. -* @return The pair of indices for the id. -* @exception RangeError Thrown if the global id is not known. -*/ -inline const IndexPair& -operator[](const GlobalIndex& global) const; - -/** -* @brief Get the index pair corresponding to a local index. -*/ -inline const IndexPair* -pair(const std::size_t& local) const; - -/** -* @brief Get an iterator over the indices positioned at the first index. -* @return Iterator over the local indices. -*/ -inline const_iterator begin() const; - -/** -* @brief Get an iterator over the indices positioned after the last index. -* @return Iterator over the local indices. -*/ -inline const_iterator end() const; - -/** -* @brief Get the internal sequence number. -* -* Is initially 0 is incremented for each resize. -* @return The sequence number. -*/ -inline int seqNo() const; - -/** -* @brief Get the total number (public and nonpublic) indices. -* @return The total number (public and nonpublic) indices. -*/ -inline size_t size() const; -private: -/** -* @brief The index set we lookup in. -*/ -const ParallelIndexSet& indexSet_; - -/** -* @brief The number of indices. -*/ -std::size_t size_; - -/** -* @brief Array with the positions of the corresponding index pair of the index set. -*/ -std::vector<const IndexPair*> indices_; - -}; - - -template<typename T> -struct LocalIndexComparator -{ -static bool compare(const T& t1, const T& t2){ -DUNE_UNUSED_PARAMETER(t1); -DUNE_UNUSED_PARAMETER(t2); -return false; -} -}; - -template<class TG, class TL> -struct IndexSetSortFunctor -{ -bool operator()(const IndexPair<TG,TL>& i1, const IndexPair<TG,TL>& i2) -{ -return i1.global()<i2.global() || (i1.global()==i2.global() && -LocalIndexComparator<TL>::compare(i1.local(), -i2.local())); -} -}; - - - -template<class TG, class TL> -inline std::ostream& operator<<(std::ostream& os, const IndexPair<TG,TL>& pair) -{ -os<<"{global="<<pair.global_<<", local="<<pair.local_<<"}"; -return os; -} - -template<class TG, class TL, int N> -inline std::ostream& operator<<(std::ostream& os, const ParallelIndexSet<TG,TL,N>& indexSet) -{ -typedef typename ParallelIndexSet<TG,TL,N>::const_iterator Iterator; -Iterator end = indexSet.end(); -os<<"{"; -for(Iterator index = indexSet.begin(); index != end; ++index) -os<<*index<<" "; -os<<"}"; -return os; - -} - -template<class TG, class TL> -inline bool operator==(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) -{ -return a.global_==b.global_; -} - -template<class TG, class TL> -inline bool operator!=(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) -{ -return a.global_!=b.global_; -} - -template<class TG, class TL> -inline bool operator<(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) -{ -return a.global_<b.global_; -} - -template<class TG, class TL> -inline bool operator>(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) -{ -return a.global_>b.global_; -} - -template<class TG, class TL> -inline bool operator<=(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) -{ -return a.global_<=b.global_; -} - -template<class TG, class TL> -inline bool operator >=(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) -{ -return a.global_>=b.global_; -} - -template<class TG, class TL> -inline bool operator==(const IndexPair<TG,TL>& a, const TG& b) -{ -return a.global_==b; -} - -template<class TG, class TL> -inline bool operator!=(const IndexPair<TG,TL>& a, const TG& b) -{ -return a.global_!=b; -} - -template<class TG, class TL> -inline bool operator<(const IndexPair<TG,TL>& a, const TG& b) -{ -return a.global_<b; -} - -template<class TG, class TL> -inline bool operator>(const IndexPair<TG,TL>& a, const TG& b) -{ -return a.global_>b; -} - -template<class TG, class TL> -inline bool operator<=(const IndexPair<TG,TL>& a, const TG& b) -{ -return a.global_<=b; -} - -template<class TG, class TL> -inline bool operator >=(const IndexPair<TG,TL>& a, const TG& b) -{ -return a.global_>=b; -} + Father::operator*().local().setState(DELETED); + } + + /** @brief The index set we are an iterator of. */ + ParallelIndexSet<TG,TL,N>* indexSet_; + + }; + + + + /** @brief The constant iterator over the pairs. */ + typedef typename + ArrayList<IndexPair,N>::const_iterator + const_iterator; + + /** + * @brief Constructor. + */ + ParallelIndexSet(); + + /** + * @brief Get the state the index set is in. + * @return The state of the index set. + */ + inline const ParallelIndexSetState& state() + { + return state_; + } + + /** + * @brief Indicate that the index set is to be resized. + * @exception InvalidState If index set was not in + * ParallelIndexSetState::GROUND mode. + */ + void beginResize(); + + /** + * @brief Add an new index to the set. + * + * The local index is created by the default constructor. + * @param global The globally unique id of the index. + * @exception InvalidState If index set is not in + * ParallelIndexSetState::RESIZE mode. + */ + inline void add(const GlobalIndex& global); + + /** + * @brief Add an new index to the set. + * + * @param global The globally unique id of the index. + * @param local The local index. + * @exception InvalidState If index set is not in + * ParallelIndexSetState::RESIZE mode. + */ + inline void add(const GlobalIndex& global, const LocalIndex& local); + + /** + * @brief Mark an index as deleted. + * + * The index will be deleted during endResize(). + * @param position An iterator at the position we want to delete. + * @exception InvalidState If index set is not in ParallelIndexSetState::RESIZE mode. + */ + inline void markAsDeleted(const iterator& position); + + /** + * @brief Indicate that the resizing finishes. + * + * @warning Invalidates all pointers stored to the elements of this index set. + * The local indices will be ordered + * according to the global indices: + * Let \f$(g_i,l_i)_{i=0}^N \f$ be the set of all indices then \f$l_i < l_j\f$ + * if and + * only if \f$g_i < g_j\f$ for arbitrary \f$i \neq j\f$. + * @exception InvalidState If index set was not in + * ParallelIndexSetState::RESIZE mode. + */ + void endResize(); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @warning If the global index is not in the set a wrong or even a + * null reference might be returned. To be save use the throwing alternative at. + */ + inline IndexPair& + operator[](const GlobalIndex& global); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline IndexPair& + at(const GlobalIndex& global); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline bool + exists (const GlobalIndex& global) const; + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @warning If the global index is not in the set a wrong or even a + * null reference might be returned. To be save use the throwing alternative at. + */ + inline const IndexPair& + operator[](const GlobalIndex& global) const; + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline const IndexPair& + at(const GlobalIndex& global) const; + + /** + * @brief Get an iterator over the indices positioned at the first index. + * @return Iterator over the local indices. + */ + inline iterator begin(); + + /** + * @brief Get an iterator over the indices positioned after the last index. + * @return Iterator over the local indices. + */ + inline iterator end(); + + /** + * @brief Get an iterator over the indices positioned at the first index. + * @return Iterator over the local indices. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator over the indices positioned after the last index. + * @return Iterator over the local indices. + */ + inline const_iterator end() const; + + /** + * @brief Renumbers the local index numbers. + * + * After this function returns the indices are + * consecutively numbered beginning from 0. Let + * $(g_i,l_i)$, $(g_j,l_j)$ be two arbitrary index + * pairs with $g_i<g_j$ then after renumbering + * $l_i<l_j$ will hold. + */ + inline void renumberLocal(); + + /** + * @brief Get the internal sequence number. + * + * Is initially 0 is incremented for each resize. + * @return The sequence number. + */ + inline int seqNo() const; + + /** + * @brief Get the total number (public and nonpublic) indices. + * @return The total number (public and nonpublic) indices. + */ + inline size_t size() const; + + private: + /** @brief The index pairs. */ + ArrayList<IndexPair,N> localIndices_; + /** @brief The new indices for the RESIZE state. */ + ArrayList<IndexPair,N> newIndices_; + /** @brief The state of the index set. */ + ParallelIndexSetState state_; + /** @brief Number to keep track of the number of resizes. */ + int seqNo_; + /** @brief Whether entries were deleted in resize mode. */ + bool deletedEntries_; + /** + * @brief Merges the _localIndices and newIndices arrays and creates a new + * localIndices array. + */ + inline void merge(); + }; + + + /** + * @brief Print an index set. + * @param os The outputstream to print to. + * @param indexSet The index set to print. + */ + template<class TG, class TL, int N> + std::ostream& operator<<(std::ostream& os, const ParallelIndexSet<TG,TL,N>& indexSet); + + /** + * @brief Decorates an index set with the possibility to find a global index + * that is mapped to a specific local. + * + */ + template<class I> + class GlobalLookupIndexSet + { + public: + /** + * @brief The type of the index set. + */ + typedef I ParallelIndexSet; + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** + * @brief The iterator over the index pairs. + */ + typedef typename ParallelIndexSet::const_iterator const_iterator; + + typedef Dune::IndexPair<typename I::GlobalIndex, typename I::LocalIndex> IndexPair; + + /** + * @brief Constructor. + * @param indexset The index set we want to be able to lookup the corresponding + * global index of a local index. + * @param size The number of indices present, i.e. one more than the maximum local index. + */ + GlobalLookupIndexSet(const ParallelIndexSet& indexset, std::size_t size); + + /** + * @brief Constructor. + * @param indexset The index set we want to be able to lookup the corresponding + * global index of a local index. + */ + GlobalLookupIndexSet(const ParallelIndexSet& indexset); + + /** + * @brief Destructor. + */ + ~GlobalLookupIndexSet(); + + /** + * @brief Find the index pair with a specific global id. + * + * This starts a binary search for the entry and therefore has complexity + * log(N). This method is forwarded to the underlying index set. + * @param global The globally unique id of the pair. + * @return The pair of indices for the id. + * @exception RangeError Thrown if the global id is not known. + */ + inline const IndexPair& + operator[](const GlobalIndex& global) const; + + /** + * @brief Get the index pair corresponding to a local index. + */ + inline const IndexPair* + pair(const std::size_t& local) const; + + /** + * @brief Get an iterator over the indices positioned at the first index. + * @return Iterator over the local indices. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator over the indices positioned after the last index. + * @return Iterator over the local indices. + */ + inline const_iterator end() const; + + /** + * @brief Get the internal sequence number. + * + * Is initially 0 is incremented for each resize. + * @return The sequence number. + */ + inline int seqNo() const; + + /** + * @brief Get the total number (public and nonpublic) indices. + * @return The total number (public and nonpublic) indices. + */ + inline size_t size() const; + private: + /** + * @brief The index set we lookup in. + */ + const ParallelIndexSet& indexSet_; + + /** + * @brief The number of indices. + */ + std::size_t size_; + + /** + * @brief Array with the positions of the corresponding index pair of the index set. + */ + std::vector<const IndexPair*> indices_; + + }; + + + template<typename T> + struct LocalIndexComparator + { + static bool compare(const T& t1, const T& t2){ + DUNE_UNUSED_PARAMETER(t1); + DUNE_UNUSED_PARAMETER(t2); + return false; + } + }; + + template<class TG, class TL> + struct IndexSetSortFunctor + { + bool operator()(const IndexPair<TG,TL>& i1, const IndexPair<TG,TL>& i2) + { + return i1.global()<i2.global() || (i1.global()==i2.global() && + LocalIndexComparator<TL>::compare(i1.local(), + i2.local())); + } + }; + + + + template<class TG, class TL> + inline std::ostream& operator<<(std::ostream& os, const IndexPair<TG,TL>& pair) + { + os<<"{global="<<pair.global_<<", local="<<pair.local_<<"}"; + return os; + } + + template<class TG, class TL, int N> + inline std::ostream& operator<<(std::ostream& os, const ParallelIndexSet<TG,TL,N>& indexSet) + { + typedef typename ParallelIndexSet<TG,TL,N>::const_iterator Iterator; + Iterator end = indexSet.end(); + os<<"{"; + for(Iterator index = indexSet.begin(); index != end; ++index) + os<<*index<<" "; + os<<"}"; + return os; + + } + + template<class TG, class TL> + inline bool operator==(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) + { + return a.global_==b.global_; + } + + template<class TG, class TL> + inline bool operator!=(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) + { + return a.global_!=b.global_; + } + + template<class TG, class TL> + inline bool operator<(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) + { + return a.global_<b.global_; + } + + template<class TG, class TL> + inline bool operator>(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) + { + return a.global_>b.global_; + } + + template<class TG, class TL> + inline bool operator<=(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) + { + return a.global_<=b.global_; + } + + template<class TG, class TL> + inline bool operator >=(const IndexPair<TG,TL>& a, const IndexPair<TG,TL>& b) + { + return a.global_>=b.global_; + } + + template<class TG, class TL> + inline bool operator==(const IndexPair<TG,TL>& a, const TG& b) + { + return a.global_==b; + } + + template<class TG, class TL> + inline bool operator!=(const IndexPair<TG,TL>& a, const TG& b) + { + return a.global_!=b; + } + + template<class TG, class TL> + inline bool operator<(const IndexPair<TG,TL>& a, const TG& b) + { + return a.global_<b; + } + + template<class TG, class TL> + inline bool operator>(const IndexPair<TG,TL>& a, const TG& b) + { + return a.global_>b; + } + + template<class TG, class TL> + inline bool operator<=(const IndexPair<TG,TL>& a, const TG& b) + { + return a.global_<=b; + } + + template<class TG, class TL> + inline bool operator >=(const IndexPair<TG,TL>& a, const TG& b) + { + return a.global_>=b; + } #ifndef DOXYGEN -template<class TG, class TL> -IndexPair<TG,TL>::IndexPair(const TG& global, const TL& local) -: global_(global), local_(local){} + template<class TG, class TL> + IndexPair<TG,TL>::IndexPair(const TG& global, const TL& local) + : global_(global), local_(local){} -template<class TG, class TL> -IndexPair<TG,TL>::IndexPair(const TG& global) -: global_(global), local_(){} + template<class TG, class TL> + IndexPair<TG,TL>::IndexPair(const TG& global) + : global_(global), local_(){} -template<class TG, class TL> -IndexPair<TG,TL>::IndexPair() -: global_(), local_(){} + template<class TG, class TL> + IndexPair<TG,TL>::IndexPair() + : global_(), local_(){} -template<class TG, class TL> -inline const TG& IndexPair<TG,TL>::global() const { -return global_; -} + template<class TG, class TL> + inline const TG& IndexPair<TG,TL>::global() const { + return global_; + } -template<class TG, class TL> -inline TL& IndexPair<TG,TL>::local() { -return local_; -} + template<class TG, class TL> + inline TL& IndexPair<TG,TL>::local() { + return local_; + } -template<class TG, class TL> -inline const TL& IndexPair<TG,TL>::local() const { -return local_; -} + template<class TG, class TL> + inline const TL& IndexPair<TG,TL>::local() const { + return local_; + } -template<class TG, class TL> -inline void IndexPair<TG,TL>::setLocal(int local){ -local_=local; -} + template<class TG, class TL> + inline void IndexPair<TG,TL>::setLocal(int local){ + local_=local; + } -template<class TG, class TL, int N> -ParallelIndexSet<TG,TL,N>::ParallelIndexSet() -: state_(GROUND), seqNo_(0) -{} + template<class TG, class TL, int N> + ParallelIndexSet<TG,TL,N>::ParallelIndexSet() + : state_(GROUND), seqNo_(0) + {} -template<class TG, class TL, int N> -void ParallelIndexSet<TG,TL,N>::beginResize() -{ + template<class TG, class TL, int N> + void ParallelIndexSet<TG,TL,N>::beginResize() + { -// Checks in unproductive code + // Checks in unproductive code #ifndef NDEBUG -if(state_!=GROUND) -DUNE_THROW(InvalidIndexSetState, -"IndexSet has to be in GROUND state, when " -<< "beginResize() is called!"); + if(state_!=GROUND) + DUNE_THROW(InvalidIndexSetState, + "IndexSet has to be in GROUND state, when " + << "beginResize() is called!"); #endif -state_ = RESIZE; -deletedEntries_ = false; -} + state_ = RESIZE; + deletedEntries_ = false; + } -template<class TG, class TL, int N> -inline void ParallelIndexSet<TG,TL,N>::add(const GlobalIndex& global) -{ -// Checks in unproductive code + template<class TG, class TL, int N> + inline void ParallelIndexSet<TG,TL,N>::add(const GlobalIndex& global) + { + // Checks in unproductive code #ifndef NDEBUG -if(state_ != RESIZE) -DUNE_THROW(InvalidIndexSetState, "Indices can only be added " -<<"while in RESIZE state!"); + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be added " + <<"while in RESIZE state!"); #endif -newIndices_.push_back(IndexPair(global)); -} + newIndices_.push_back(IndexPair(global)); + } -template<class TG, class TL, int N> -inline void ParallelIndexSet<TG,TL,N>::add(const TG& global, const TL& local) -{ -// Checks in unproductive code + template<class TG, class TL, int N> + inline void ParallelIndexSet<TG,TL,N>::add(const TG& global, const TL& local) + { + // Checks in unproductive code #ifndef NDEBUG -if(state_ != RESIZE) -DUNE_THROW(InvalidIndexSetState, "Indices can only be added " -<<"while in RESIZE state!"); + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be added " + <<"while in RESIZE state!"); #endif -newIndices_.push_back(IndexPair(global,local)); -} + newIndices_.push_back(IndexPair(global,local)); + } -template<class TG, class TL, int N> -inline void ParallelIndexSet<TG,TL,N>::markAsDeleted(const iterator& global) -{ -// Checks in unproductive code + template<class TG, class TL, int N> + inline void ParallelIndexSet<TG,TL,N>::markAsDeleted(const iterator& global) + { + // Checks in unproductive code #ifndef NDEBUG -if(state_ != RESIZE) -DUNE_THROW(InvalidIndexSetState, "Indices can only be removed " -<<"while in RESIZE state!"); + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "Indices can only be removed " + <<"while in RESIZE state!"); #endif -deletedEntries_ = true; + deletedEntries_ = true; -global.markAsDeleted(); -} + global.markAsDeleted(); + } -template<class TG, class TL, int N> -void ParallelIndexSet<TG,TL,N>::endResize() { -// Checks in unproductive code + template<class TG, class TL, int N> + void ParallelIndexSet<TG,TL,N>::endResize() { + // Checks in unproductive code #ifndef NDEBUG -if(state_ != RESIZE) -DUNE_THROW(InvalidIndexSetState, "endResize called while not " -<<"in RESIZE state!"); + if(state_ != RESIZE) + DUNE_THROW(InvalidIndexSetState, "endResize called while not " + <<"in RESIZE state!"); #endif -std::sort(newIndices_.begin(), newIndices_.end(), IndexSetSortFunctor<TG,TL>()); -merge(); -seqNo_++; -state_ = GROUND; -} - - -template<class TG, class TL, int N> -inline void ParallelIndexSet<TG,TL,N>::merge(){ -if(localIndices_.size()==0) -{ -localIndices_=newIndices_; -newIndices_.clear(); -} -else if(newIndices_.size()>0 || deletedEntries_) -{ -ArrayList<IndexPair,N> tempPairs; -typedef typename ArrayList<IndexPair,N>::iterator iterator; -typedef typename ArrayList<IndexPair,N>::const_iterator const_iterator; - -iterator old=localIndices_.begin(); -iterator added=newIndices_.begin(); -const const_iterator endold=localIndices_.end(); -const const_iterator endadded=newIndices_.end(); - -while(old != endold && added!= endadded) -{ -if(old->local().state()==DELETED) { -old.eraseToHere(); -} -else -{ -if(old->global() < added->global() || -(old->global() == added->global() -&& LocalIndexComparator<TL>::compare(old->local(),added->local()))) -{ -tempPairs.push_back(*old); -old.eraseToHere(); -continue; -}else -{ -tempPairs.push_back(*added); -added.eraseToHere(); -} -} -} - -while(old != endold) -{ -if(old->local().state()!=DELETED) { -tempPairs.push_back(*old); -} -old.eraseToHere(); -} - -while(added!= endadded) -{ -tempPairs.push_back(*added); -added.eraseToHere(); -} -localIndices_ = tempPairs; -} -} - - -template<class TG, class TL, int N> -inline const IndexPair<TG,TL>& -ParallelIndexSet<TG,TL,N>::at(const TG& global) const -{ -// perform a binary search -int low=0, high=localIndices_.size()-1, probe=-1; - -while(low<high) -{ -probe = (high + low) / 2; -if(global <= localIndices_[probe].global()) -high = probe; -else -low = probe+1; -} - -if(probe==-1) -DUNE_THROW(RangeError, "No entries!"); - -if( localIndices_[low].global() != global) -DUNE_THROW(RangeError, "Could not find entry of "<<global); -else -return localIndices_[low]; -} - -template<class TG, class TL, int N> -inline const IndexPair<TG,TL>& -ParallelIndexSet<TG,TL,N>::operator[](const TG& global) const -{ -// perform a binary search -int low=0, high=localIndices_.size()-1, probe=-1; - -while(low<high) -{ -probe = (high + low) / 2; -if(global <= localIndices_[probe].global()) -high = probe; -else -low = probe+1; -} - -return localIndices_[low]; -} -template<class TG, class TL, int N> -inline IndexPair<TG,TL>& ParallelIndexSet<TG,TL,N>::at(const TG& global) -{ -// perform a binary search -int low=0, high=localIndices_.size()-1, probe=-1; - -while(low<high) -{ -probe = (high + low) / 2; -if(localIndices_[probe].global() >= global) -high = probe; -else -low = probe+1; -} - -if(probe==-1) -DUNE_THROW(RangeError, "No entries!"); - -if( localIndices_[low].global() != global) -DUNE_THROW(RangeError, "Could not find entry of "<<global); -else -return localIndices_[low]; -} - -template<class TG, class TL, int N> -inline bool ParallelIndexSet<TG,TL,N>::exists (const TG& global) const -{ -// perform a binary search -int low=0, high=localIndices_.size()-1, probe=-1; - -while(low<high) -{ -probe = (high + low) / 2; -if(localIndices_[probe].global() >= global) -high = probe; -else -low = probe+1; -} - -if(probe==-1) -return false; - -if( localIndices_[low].global() != global) -return false; -return true; -} - -template<class TG, class TL, int N> -inline IndexPair<TG,TL>& ParallelIndexSet<TG,TL,N>::operator[](const TG& global) -{ -// perform a binary search -int low=0, high=localIndices_.size()-1, probe=-1; - -while(low<high) -{ -probe = (high + low) / 2; -if(localIndices_[probe].global() >= global) -high = probe; -else -low = probe+1; -} - -return localIndices_[low]; -} -template<class TG, class TL, int N> -inline typename ParallelIndexSet<TG,TL,N>::iterator -ParallelIndexSet<TG,TL,N>::begin() -{ -return iterator(*this, localIndices_.begin()); -} - - -template<class TG, class TL, int N> -inline typename ParallelIndexSet<TG,TL,N>::iterator -ParallelIndexSet<TG,TL,N>::end() -{ -return iterator(*this,localIndices_.end()); -} - -template<class TG, class TL, int N> -inline typename ParallelIndexSet<TG,TL,N>::const_iterator -ParallelIndexSet<TG,TL,N>::begin() const -{ -return localIndices_.begin(); -} - - -template<class TG, class TL, int N> -inline typename ParallelIndexSet<TG,TL,N>::const_iterator -ParallelIndexSet<TG,TL,N>::end() const -{ -return localIndices_.end(); -} - -template<class TG, class TL, int N> -void ParallelIndexSet<TG,TL,N>::renumberLocal(){ + std::sort(newIndices_.begin(), newIndices_.end(), IndexSetSortFunctor<TG,TL>()); + merge(); + seqNo_++; + state_ = GROUND; + } + + + template<class TG, class TL, int N> + inline void ParallelIndexSet<TG,TL,N>::merge(){ + if(localIndices_.size()==0) + { + localIndices_=newIndices_; + newIndices_.clear(); + } + else if(newIndices_.size()>0 || deletedEntries_) + { + ArrayList<IndexPair,N> tempPairs; + typedef typename ArrayList<IndexPair,N>::iterator iterator; + typedef typename ArrayList<IndexPair,N>::const_iterator const_iterator; + + iterator old=localIndices_.begin(); + iterator added=newIndices_.begin(); + const const_iterator endold=localIndices_.end(); + const const_iterator endadded=newIndices_.end(); + + while(old != endold && added!= endadded) + { + if(old->local().state()==DELETED) { + old.eraseToHere(); + } + else + { + if(old->global() < added->global() || + (old->global() == added->global() + && LocalIndexComparator<TL>::compare(old->local(),added->local()))) + { + tempPairs.push_back(*old); + old.eraseToHere(); + continue; + }else + { + tempPairs.push_back(*added); + added.eraseToHere(); + } + } + } + + while(old != endold) + { + if(old->local().state()!=DELETED) { + tempPairs.push_back(*old); + } + old.eraseToHere(); + } + + while(added!= endadded) + { + tempPairs.push_back(*added); + added.eraseToHere(); + } + localIndices_ = tempPairs; + } + } + + + template<class TG, class TL, int N> + inline const IndexPair<TG,TL>& + ParallelIndexSet<TG,TL,N>::at(const TG& global) const + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low<high) + { + probe = (high + low) / 2; + if(global <= localIndices_[probe].global()) + high = probe; + else + low = probe+1; + } + + if(probe==-1) + DUNE_THROW(RangeError, "No entries!"); + + if( localIndices_[low].global() != global) + DUNE_THROW(RangeError, "Could not find entry of "<<global); + else + return localIndices_[low]; + } + + template<class TG, class TL, int N> + inline const IndexPair<TG,TL>& + ParallelIndexSet<TG,TL,N>::operator[](const TG& global) const + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low<high) + { + probe = (high + low) / 2; + if(global <= localIndices_[probe].global()) + high = probe; + else + low = probe+1; + } + + return localIndices_[low]; + } + template<class TG, class TL, int N> + inline IndexPair<TG,TL>& ParallelIndexSet<TG,TL,N>::at(const TG& global) + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low<high) + { + probe = (high + low) / 2; + if(localIndices_[probe].global() >= global) + high = probe; + else + low = probe+1; + } + + if(probe==-1) + DUNE_THROW(RangeError, "No entries!"); + + if( localIndices_[low].global() != global) + DUNE_THROW(RangeError, "Could not find entry of "<<global); + else + return localIndices_[low]; + } + + template<class TG, class TL, int N> + inline bool ParallelIndexSet<TG,TL,N>::exists (const TG& global) const + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low<high) + { + probe = (high + low) / 2; + if(localIndices_[probe].global() >= global) + high = probe; + else + low = probe+1; + } + + if(probe==-1) + return false; + + if( localIndices_[low].global() != global) + return false; + return true; + } + + template<class TG, class TL, int N> + inline IndexPair<TG,TL>& ParallelIndexSet<TG,TL,N>::operator[](const TG& global) + { + // perform a binary search + int low=0, high=localIndices_.size()-1, probe=-1; + + while(low<high) + { + probe = (high + low) / 2; + if(localIndices_[probe].global() >= global) + high = probe; + else + low = probe+1; + } + + return localIndices_[low]; + } + template<class TG, class TL, int N> + inline typename ParallelIndexSet<TG,TL,N>::iterator + ParallelIndexSet<TG,TL,N>::begin() + { + return iterator(*this, localIndices_.begin()); + } + + + template<class TG, class TL, int N> + inline typename ParallelIndexSet<TG,TL,N>::iterator + ParallelIndexSet<TG,TL,N>::end() + { + return iterator(*this,localIndices_.end()); + } + + template<class TG, class TL, int N> + inline typename ParallelIndexSet<TG,TL,N>::const_iterator + ParallelIndexSet<TG,TL,N>::begin() const + { + return localIndices_.begin(); + } + + + template<class TG, class TL, int N> + inline typename ParallelIndexSet<TG,TL,N>::const_iterator + ParallelIndexSet<TG,TL,N>::end() const + { + return localIndices_.end(); + } + + template<class TG, class TL, int N> + void ParallelIndexSet<TG,TL,N>::renumberLocal(){ #ifndef NDEBUG -if(state_==RESIZE) -DUNE_THROW(InvalidIndexSetState, "IndexSet has to be in " -<<"GROUND state for renumberLocal()"); + if(state_==RESIZE) + DUNE_THROW(InvalidIndexSetState, "IndexSet has to be in " + <<"GROUND state for renumberLocal()"); #endif -typedef typename ArrayList<IndexPair,N>::iterator iterator; -const const_iterator end_ = end(); -uint32_t index=0; - -for(iterator pair=begin(); pair!=end_; index++, ++pair) -pair->local()=index; -} - -template<class TG, class TL, int N> -inline int ParallelIndexSet<TG,TL,N>::seqNo() const -{ -return seqNo_; -} - -template<class TG, class TL, int N> -inline size_t ParallelIndexSet<TG,TL,N>::size() const -{ -return localIndices_.size(); -} - -template<class I> -GlobalLookupIndexSet<I>::GlobalLookupIndexSet(const I& indexset, -std::size_t size) -: indexSet_(indexset), size_(size), -indices_(size_, static_cast<const IndexPair*>(0)) -{ -const_iterator end_ = indexSet_.end(); - -for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) { -assert(pair->local()<size_); -indices_[pair->local()] = &(*pair); -} -} - -template<class I> -GlobalLookupIndexSet<I>::GlobalLookupIndexSet(const I& indexset) -: indexSet_(indexset), size_(0) -{ -const_iterator end_ = indexSet_.end(); -for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) -size_=std::max(size_,static_cast<std::size_t>(pair->local())); - -indices_.resize(++size_, 0); - -for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) -indices_[pair->local()] = &(*pair); -} - -template<class I> -GlobalLookupIndexSet<I>::~GlobalLookupIndexSet() -{} - -template<class I> -inline const IndexPair<typename I::GlobalIndex, typename I::LocalIndex>* -GlobalLookupIndexSet<I>::pair(const std::size_t& local) const -{ -return indices_[local]; -} - -template<class I> -inline const IndexPair<typename I::GlobalIndex, typename I::LocalIndex>& -GlobalLookupIndexSet<I>::operator[](const GlobalIndex& global) const -{ -return indexSet_[global]; -} - -template<class I> -typename I::const_iterator GlobalLookupIndexSet<I>::begin() const -{ -return indexSet_.begin(); -} - -template<class I> -typename I::const_iterator GlobalLookupIndexSet<I>::end() const -{ -return indexSet_.end(); -} - -template<class I> -inline size_t GlobalLookupIndexSet<I>::size() const -{ -return size_; -} - -template<class I> -inline int GlobalLookupIndexSet<I>::seqNo() const -{ -return indexSet_.seqNo(); -} - -template<typename TG, typename TL, int N, typename TG1, typename TL1, int N1> -bool operator==(const ParallelIndexSet<TG,TL,N>& idxset, -const ParallelIndexSet<TG1,TL1,N1>& idxset1) -{ -if(idxset.size()!=idxset1.size()) -return false; -typedef typename ParallelIndexSet<TG,TL,N>::const_iterator Iter; -typedef typename ParallelIndexSet<TG1,TL1,N1>::const_iterator Iter1; -Iter iter=idxset.begin(); -for(Iter1 iter1=idxset1.begin(); iter1 != idxset1.end(); ++iter, ++iter1) { -if(iter1->global()!=iter->global()) -return false; -typedef typename ParallelIndexSet<TG,TL,N>::LocalIndex PI; -const PI& pi=iter->local(), pi1=iter1->local(); - -if(pi!=pi1) -return false; -} -return true; -} - -template<typename TG, typename TL, int N, typename TG1, typename TL1, int N1> -bool operator!=(const ParallelIndexSet<TG,TL,N>& idxset, -const ParallelIndexSet<TG1,TL1,N1>& idxset1) -{ -return !(idxset==idxset1); -} + typedef typename ArrayList<IndexPair,N>::iterator iterator; + const const_iterator end_ = end(); + uint32_t index=0; + + for(iterator pair=begin(); pair!=end_; index++, ++pair) + pair->local()=index; + } + + template<class TG, class TL, int N> + inline int ParallelIndexSet<TG,TL,N>::seqNo() const + { + return seqNo_; + } + + template<class TG, class TL, int N> + inline size_t ParallelIndexSet<TG,TL,N>::size() const + { + return localIndices_.size(); + } + + template<class I> + GlobalLookupIndexSet<I>::GlobalLookupIndexSet(const I& indexset, + std::size_t size) + : indexSet_(indexset), size_(size), + indices_(size_, static_cast<const IndexPair*>(0)) + { + const_iterator end_ = indexSet_.end(); + + for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) { + assert(pair->local()<size_); + indices_[pair->local()] = &(*pair); + } + } + + template<class I> + GlobalLookupIndexSet<I>::GlobalLookupIndexSet(const I& indexset) + : indexSet_(indexset), size_(0) + { + const_iterator end_ = indexSet_.end(); + for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) + size_=std::max(size_,static_cast<std::size_t>(pair->local())); + + indices_.resize(++size_, 0); + + for(const_iterator pair = indexSet_.begin(); pair!=end_; ++pair) + indices_[pair->local()] = &(*pair); + } + + template<class I> + GlobalLookupIndexSet<I>::~GlobalLookupIndexSet() + {} + + template<class I> + inline const IndexPair<typename I::GlobalIndex, typename I::LocalIndex>* + GlobalLookupIndexSet<I>::pair(const std::size_t& local) const + { + return indices_[local]; + } + + template<class I> + inline const IndexPair<typename I::GlobalIndex, typename I::LocalIndex>& + GlobalLookupIndexSet<I>::operator[](const GlobalIndex& global) const + { + return indexSet_[global]; + } + + template<class I> + typename I::const_iterator GlobalLookupIndexSet<I>::begin() const + { + return indexSet_.begin(); + } + + template<class I> + typename I::const_iterator GlobalLookupIndexSet<I>::end() const + { + return indexSet_.end(); + } + + template<class I> + inline size_t GlobalLookupIndexSet<I>::size() const + { + return size_; + } + + template<class I> + inline int GlobalLookupIndexSet<I>::seqNo() const + { + return indexSet_.seqNo(); + } + + template<typename TG, typename TL, int N, typename TG1, typename TL1, int N1> + bool operator==(const ParallelIndexSet<TG,TL,N>& idxset, + const ParallelIndexSet<TG1,TL1,N1>& idxset1) + { + if(idxset.size()!=idxset1.size()) + return false; + typedef typename ParallelIndexSet<TG,TL,N>::const_iterator Iter; + typedef typename ParallelIndexSet<TG1,TL1,N1>::const_iterator Iter1; + Iter iter=idxset.begin(); + for(Iter1 iter1=idxset1.begin(); iter1 != idxset1.end(); ++iter, ++iter1) { + if(iter1->global()!=iter->global()) + return false; + typedef typename ParallelIndexSet<TG,TL,N>::LocalIndex PI; + const PI& pi=iter->local(), pi1=iter1->local(); + + if(pi!=pi1) + return false; + } + return true; + } + + template<typename TG, typename TL, int N, typename TG1, typename TL1, int N1> + bool operator!=(const ParallelIndexSet<TG,TL,N>& idxset, + const ParallelIndexSet<TG1,TL1,N1>& idxset1) + { + return !(idxset==idxset1); + } #endif // DOXYGEN diff --git a/dune/common/parallel/indicessyncer.hh b/dune/common/parallel/indicessyncer.hh index 2bb1bb2e2ae53faba1ed3d33cb0b954d8af4da9a..71f83faa1ff7d1a7143185b235e5dbd4aaebc013 100644 --- a/dune/common/parallel/indicessyncer.hh +++ b/dune/common/parallel/indicessyncer.hh @@ -20,1207 +20,1207 @@ #if HAVE_MPI namespace Dune { -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Class for adding missing indices of a distributed index set in a local -* communication. -* @author Markus Blatt -*/ - -/** -* @brief Class for recomputing missing indices of a distributed index set. -* -* Missing local and remote indices will be added. -*/ -template<typename T> -class IndicesSyncer -{ -public: - -/** @brief The type of the index set. */ -typedef T ParallelIndexSet; - -/** @brief The type of the index pair */ -typedef typename ParallelIndexSet::IndexPair IndexPair; - -/** @brief Type of the global index used in the index set. */ -typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; - -/** @brief Type of the attribute used in the index set. */ -typedef typename ParallelIndexSet::LocalIndex::Attribute Attribute; - -/** -* @brief Type of the remote indices. -*/ -typedef Dune::RemoteIndices<ParallelIndexSet> RemoteIndices; - -/** -* @brief Constructor. -* -* The source as well as the target index set of the remote -* indices have to be the same as the provided index set. -* @param indexSet The index set with the information -* of the locally present indices. -* @param remoteIndices The remoteIndices. -*/ -IndicesSyncer(ParallelIndexSet& indexSet, -RemoteIndices& remoteIndices); - -/** -* @brief Sync the index set. -* -* Computes the missing indices in the local and the remote index list and adds them. -* No global communication is necessary! -* All indices added to the index will become the local index -* std::numeric_limits<size_t>::max() -* -*/ -void sync(); - -/** -* @brief Synce the index set and assign local numbers to new indices -* -* Computes the missing indices in the local and the remote index list and adds them. -* No global communication is necessary! -* @param numberer Functor providing the local indices for the added global indices. -* has to provide a function size_t operator()(const TG& global) that provides the -* local index to a global one. It will be called for ascending global indices. -* -*/ -template<typename T1> -void sync(T1& numberer); - -private: - -/** @brief The set of locally present indices.*/ -ParallelIndexSet& indexSet_; - -/** @brief The remote indices. */ -RemoteIndices& remoteIndices_; - -/** @brief The send buffers for the neighbour processes. */ -char** sendBuffers_; - -/** @brief The receive buffer. */ -char* receiveBuffer_; - -/** @brief The size of the send buffers. */ -std::size_t* sendBufferSizes_; - -/** @brief The size of the receive buffer in bytes. */ -int receiveBufferSize_; // int because of MPI - -/** -* @brief Information about the messages to send to a neighbouring process. -*/ -struct MessageInformation -{ -MessageInformation() -: publish(), pairs() -{} -/** @brief The number of indices we publish for the other process. */ -int publish; -/** -* @brief The number of pairs (attribute and process number) -* we publish to the neighbour process. -*/ -int pairs; -}; - -/** -* @brief Default numberer for sync(). -*/ -class DefaultNumberer -{ -public: -/** -* @brief Provide the lcoal index, always -* std::numeric_limits<size_t>::max() -* @param global The global index (ignored). -*/ -std::size_t operator()(const GlobalIndex& global) -{ -DUNE_UNUSED_PARAMETER(global); -return std::numeric_limits<size_t>::max(); -} -}; - -/** @brief The mpi datatype for the MessageInformation */ -MPI_Datatype datatype_; - -/** @brief Our rank. */ -int rank_; - -/** -* @brief List type for temporarily storing the global indices of the -* remote indices. -*/ -typedef SLList<std::pair<GlobalIndex,Attribute>, typename RemoteIndices::Allocator> GlobalIndexList; - -/** @brief The modifying iterator for the global index list. */ -typedef typename GlobalIndexList::ModifyIterator GlobalIndexModifier; - -/** -* @brief The type of the iterator of GlobalIndexList -*/ -typedef typename SLList<GlobalIndex, typename RemoteIndices::Allocator>::iterator -GlobalIndexIterator; - -/** @brief Type of the map of ranks onto GlobalIndexLists. */ -typedef std::map<int, GlobalIndexList> GlobalIndicesMap; - -/** -* @brief Map of global index lists onto process ranks. -* -* As the pointers in the remote index lists become invalid due to -* resorting the index set entries one has store the corresponding -* global index for each remote index. Thus the pointers can be adjusted -* properly as a last step. -*/ -GlobalIndicesMap globalMap_; - -/** -* @brief The type of the single linked list of bools. -*/ -typedef SLList<bool, typename RemoteIndices::Allocator> BoolList; - -/** -* @brief The mutable iterator of the single linked bool list. -*/ -typedef typename BoolList::iterator BoolIterator; - -/** @brief The type of the modifying iterator for the list of bools. */ -typedef typename BoolList::ModifyIterator BoolListModifier; - -/** @brief The type of the map of bool lists. */ -typedef std::map<int,BoolList> BoolMap; - -/** -* @brief Map of lists of bool indicating whether the remote index was present before -* call of sync. -*/ -BoolMap oldMap_; - -/** @brief Information about the messages we send. */ -std::map<int,MessageInformation> infoSend_; - -/** @brief The type of the remote index list. */ -typedef typename RemoteIndices::RemoteIndexList RemoteIndexList; - -/** @brief The tyoe of the modifying iterator of the remote index list. */ -typedef typename RemoteIndexList::ModifyIterator RemoteIndexModifier; - -/** @brief The type of the remote inde. */ -typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; - -/** @brief The iterator of the remote index list. */ -typedef typename RemoteIndexList::iterator RemoteIndexIterator; - -/** @brief The const iterator of the remote index list. */ -typedef typename RemoteIndexList::const_iterator ConstRemoteIndexIterator; - -/** @brief Type of the tuple of iterators needed for the adding of indices. */ -typedef std::tuple<RemoteIndexModifier,GlobalIndexModifier,BoolListModifier, -const ConstRemoteIndexIterator> IteratorTuple; - -/** -* @brief A tuple of iterators. -* -* Insertion into a single linked list is only possible at the position after the one of the iterator. -* Therefore for each linked list two iterators are needed: One position before the actual entry -* (for insertion) and one positioned at the actual position (for searching). -*/ -class Iterators -{ -friend class IndicesSyncer<T>; -public: -/** -* @brief Constructor. -* -* Initializes all iterator to first entry and the one before the first entry, respectively. -* @param remoteIndices The list of the remote indices. -* @param globalIndices The list of the coresponding global indices. This is needed because the -* the pointers to the local index will become invalid due to the merging of the index sets. -* @param booleans Whether the remote index was there before the sync process started. -*/ -Iterators(RemoteIndexList& remoteIndices, GlobalIndexList& globalIndices, -BoolList& booleans); - -/** -* @brief Default constructor. -*/ -Iterators(); - -/** -* @brief Increment all iteraors. -*/ -Iterators& operator++(); - -/** -* @brief Insert a new remote index to the underlying remote index list. -* @param index The remote index. -* @param global The global index corresponding to the remote index. -*/ -void insert(const RemoteIndex& index, -const std::pair<GlobalIndex,Attribute>& global); - -/** -* @brief Get the remote index at current position. -* @return The current remote index. -*/ -RemoteIndex& remoteIndex() const; - -/** -* @brief Get the global index of the remote index at current position. -* @return The current global index. -*/ -std::pair<GlobalIndex,Attribute>& globalIndexPair() const; - -Attribute& attribute() const; - -/** -* @brief Was this entry already in the remote index list before the sync process? -* @return True if the current index wasalready in the remote index list -* before the sync process. -*/ -bool isOld() const; - -/** -* @brief Reset all the underlying iterators. -* -* Position them to first list entry and the entry before the first entry respectively. -* @param remoteIndices The list of the remote indices. -* @param globalIndices The list of the coresponding global indices. This is needed because the -* the pointers to the local index will become invalid due to the merging of the index sets. -* @param booleans Whether the remote index was there before the sync process started. -*/ -void reset(RemoteIndexList& remoteIndices, GlobalIndexList& globalIndices, -BoolList& booleans); - -/** -* @brief Are we not at the end of the list? -* @return True if the iterators are not positioned at the end of the list -* and the tail of the list respectively. -*/ -bool isNotAtEnd() const; - -/** -* @brief Are we at the end of the list? -* @return True if the iterators are positioned at the end of the list -* and the tail of the list respectively. -*/ -bool isAtEnd() const; - -private: -/** -* @brief The iterator tuple. -* -* The tuple consists of one iterator over a single linked list of remote indices -* initially positioned before the first entry, one over a sll of global indices -* , one over a all of bool values both postioned at the same entry. The another three -* iterators of the same type positioned at the first entry. Last an iterator over the -* sll of remote indices positioned at the end. -*/ -IteratorTuple iterators_; -}; - -/** @brief Type of the map from ranks to iterator tuples. */ -typedef std::map<int,Iterators> IteratorsMap; - -/** -* @brief The iterator tuples mapped on the neighbours. -* -* The key of the map is the rank of the neighbour. -* The first entry in the tuple is an iterator over the remote indices -* initially positioned before the first entry. The second entry is an -* iterator over the corresponding global indices also initially positioned -* before the first entry. The third entry an iterator over remote indices -* initially positioned at the beginning. The last entry is the iterator over -* the remote indices positioned at the end. -*/ -IteratorsMap iteratorsMap_; - -/** @brief Calculates the message sizes to send. */ -void calculateMessageSizes(); - -/** -* @brief Pack and send the message for another process. -* @param destination The rank of the process we send to. -* @param buffer The allocated buffer to use. -* @param bufferSize The size of the buffer. -* @param req The MPI_Request to setup the nonblocking send. -*/ -void packAndSend(int destination, char* buffer, std::size_t bufferSize, MPI_Request& req); - -/** -* @brief Recv and unpack the message from another process and add the indices. -* @param numberer Functor providing local indices for added global indices. -*/ -template<typename T1> -void recvAndUnpack(T1& numberer); - -/** -* @brief Register the MPI datatype for the MessageInformation. -*/ -void registerMessageDatatype(); - -/** -* @brief Insert an entry into the remote index list if not yet present. -*/ -void insertIntoRemoteIndexList(int process, -const std::pair<GlobalIndex,Attribute>& global, -char attribute); - -/** -* @brief Reset the iterator tuples of all neighbouring processes. -*/ -void resetIteratorsMap(); - -/** -* @brief Check whether the iterator tuples of all neighbouring processes -* are reset. -*/ -bool checkReset(); - -/** -* @brief Check whether the iterator tuple is reset. -* -* @param iterators The iterator tuple to check. -* @param rlist The SLList of the remote indices. -* @param gList The SLList of the global indices. -* @param bList The SLList of the bool values. -*/ -bool checkReset(const Iterators& iterators, RemoteIndexList& rlist, GlobalIndexList& gList, -BoolList& bList); -}; - -template<typename TG, typename TA> -bool operator<(const IndexPair<TG,ParallelLocalIndex<TA> >& i1, -const std::pair<TG,TA>& i2) -{ -return i1.global() < i2.first || -(i1.global() == i2.first && i1.local().attribute()<i2.second); -} - -template<typename TG, typename TA> -bool operator<(const std::pair<TG,TA>& i1, -const IndexPair<TG,ParallelLocalIndex<TA> >& i2) -{ -return i1.first < i2.global() || -(i1.first == i2.global() && i1.second<i2.local().attribute()); -} - -template<typename TG, typename TA> -bool operator==(const IndexPair<TG,ParallelLocalIndex<TA> >& i1, -const std::pair<TG,TA>& i2) -{ -return (i1.global() == i2.first && i1.local().attribute()==i2.second); -} - -template<typename TG, typename TA> -bool operator!=(const IndexPair<TG,ParallelLocalIndex<TA> >& i1, -const std::pair<TG,TA>& i2) -{ -return (i1.global() != i2.first || i1.local().attribute()!=i2.second); -} - -template<typename TG, typename TA> -bool operator==(const std::pair<TG,TA>& i2, -const IndexPair<TG,ParallelLocalIndex<TA> >& i1) -{ -return (i1.global() == i2.first && i1.local().attribute()==i2.second); -} - -template<typename TG, typename TA> -bool operator!=(const std::pair<TG,TA>& i2, -const IndexPair<TG,ParallelLocalIndex<TA> >& i1) -{ -return (i1.global() != i2.first || i1.local().attribute()!=i2.second); -} - -/** -* @brief Stores the corresponding global indices of the remote index information. -* -* Whenever a ParallelIndexSet is resized all RemoteIndices that use it will be invalided -* as the pointers to the index set are invalid after calling ParallelIndexSet::Resize() -* One can rebuild them by storing the global indices in a map with this function and later -* repairing the pointers by calling repairLocalIndexPointers. -* -* @warning The RemoteIndices class has to be build with the same index set for both the -* sending and receiving side -* @param globalMap Map to store the corresponding global indices in. -* @param remoteIndices The remote index information we need to store the corresponding global -* indices of. -* @param indexSet The index set that is for both the sending and receiving side of the remote -* index information. -*/ -template<typename T, typename A, typename A1> -void storeGlobalIndicesOfRemoteIndices(std::map<int,SLList<std::pair<typename T::GlobalIndex, typename T::LocalIndex::Attribute>,A> >& globalMap, -const RemoteIndices<T,A1>& remoteIndices) -{ -typedef typename RemoteIndices<T,A1>::const_iterator RemoteIterator; - -for(RemoteIterator remote = remoteIndices.begin(), end =remoteIndices.end(); remote != end; ++remote) { -typedef typename RemoteIndices<T,A1>::RemoteIndexList RemoteIndexList; -typedef typename RemoteIndexList::const_iterator RemoteIndexIterator; -typedef SLList<std::pair<typename T::GlobalIndex, typename T::LocalIndex::Attribute>,A> GlobalIndexList; -GlobalIndexList& global = globalMap[remote->first]; -RemoteIndexList& rList = *(remote->second.first); - -for(RemoteIndexIterator index = rList.begin(), riEnd = rList.end(); -index != riEnd; ++index) { -global.push_back(std::make_pair(index->localIndexPair().global(), -index->localIndexPair().local().attribute())); -} -} -} + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Class for adding missing indices of a distributed index set in a local + * communication. + * @author Markus Blatt + */ + + /** + * @brief Class for recomputing missing indices of a distributed index set. + * + * Missing local and remote indices will be added. + */ + template<typename T> + class IndicesSyncer + { + public: + + /** @brief The type of the index set. */ + typedef T ParallelIndexSet; + + /** @brief The type of the index pair */ + typedef typename ParallelIndexSet::IndexPair IndexPair; + + /** @brief Type of the global index used in the index set. */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** @brief Type of the attribute used in the index set. */ + typedef typename ParallelIndexSet::LocalIndex::Attribute Attribute; + + /** + * @brief Type of the remote indices. + */ + typedef Dune::RemoteIndices<ParallelIndexSet> RemoteIndices; + + /** + * @brief Constructor. + * + * The source as well as the target index set of the remote + * indices have to be the same as the provided index set. + * @param indexSet The index set with the information + * of the locally present indices. + * @param remoteIndices The remoteIndices. + */ + IndicesSyncer(ParallelIndexSet& indexSet, + RemoteIndices& remoteIndices); + + /** + * @brief Sync the index set. + * + * Computes the missing indices in the local and the remote index list and adds them. + * No global communication is necessary! + * All indices added to the index will become the local index + * std::numeric_limits<size_t>::max() + * + */ + void sync(); + + /** + * @brief Synce the index set and assign local numbers to new indices + * + * Computes the missing indices in the local and the remote index list and adds them. + * No global communication is necessary! + * @param numberer Functor providing the local indices for the added global indices. + * has to provide a function size_t operator()(const TG& global) that provides the + * local index to a global one. It will be called for ascending global indices. + * + */ + template<typename T1> + void sync(T1& numberer); + + private: + + /** @brief The set of locally present indices.*/ + ParallelIndexSet& indexSet_; + + /** @brief The remote indices. */ + RemoteIndices& remoteIndices_; + + /** @brief The send buffers for the neighbour processes. */ + char** sendBuffers_; + + /** @brief The receive buffer. */ + char* receiveBuffer_; + + /** @brief The size of the send buffers. */ + std::size_t* sendBufferSizes_; + + /** @brief The size of the receive buffer in bytes. */ + int receiveBufferSize_; // int because of MPI + + /** + * @brief Information about the messages to send to a neighbouring process. + */ + struct MessageInformation + { + MessageInformation() + : publish(), pairs() + {} + /** @brief The number of indices we publish for the other process. */ + int publish; + /** + * @brief The number of pairs (attribute and process number) + * we publish to the neighbour process. + */ + int pairs; + }; + + /** + * @brief Default numberer for sync(). + */ + class DefaultNumberer + { + public: + /** + * @brief Provide the lcoal index, always + * std::numeric_limits<size_t>::max() + * @param global The global index (ignored). + */ + std::size_t operator()(const GlobalIndex& global) + { + DUNE_UNUSED_PARAMETER(global); + return std::numeric_limits<size_t>::max(); + } + }; + + /** @brief The mpi datatype for the MessageInformation */ + MPI_Datatype datatype_; + + /** @brief Our rank. */ + int rank_; + + /** + * @brief List type for temporarily storing the global indices of the + * remote indices. + */ + typedef SLList<std::pair<GlobalIndex,Attribute>, typename RemoteIndices::Allocator> GlobalIndexList; + + /** @brief The modifying iterator for the global index list. */ + typedef typename GlobalIndexList::ModifyIterator GlobalIndexModifier; + + /** + * @brief The type of the iterator of GlobalIndexList + */ + typedef typename SLList<GlobalIndex, typename RemoteIndices::Allocator>::iterator + GlobalIndexIterator; + + /** @brief Type of the map of ranks onto GlobalIndexLists. */ + typedef std::map<int, GlobalIndexList> GlobalIndicesMap; + + /** + * @brief Map of global index lists onto process ranks. + * + * As the pointers in the remote index lists become invalid due to + * resorting the index set entries one has store the corresponding + * global index for each remote index. Thus the pointers can be adjusted + * properly as a last step. + */ + GlobalIndicesMap globalMap_; + + /** + * @brief The type of the single linked list of bools. + */ + typedef SLList<bool, typename RemoteIndices::Allocator> BoolList; + + /** + * @brief The mutable iterator of the single linked bool list. + */ + typedef typename BoolList::iterator BoolIterator; + + /** @brief The type of the modifying iterator for the list of bools. */ + typedef typename BoolList::ModifyIterator BoolListModifier; + + /** @brief The type of the map of bool lists. */ + typedef std::map<int,BoolList> BoolMap; + + /** + * @brief Map of lists of bool indicating whether the remote index was present before + * call of sync. + */ + BoolMap oldMap_; + + /** @brief Information about the messages we send. */ + std::map<int,MessageInformation> infoSend_; + + /** @brief The type of the remote index list. */ + typedef typename RemoteIndices::RemoteIndexList RemoteIndexList; + + /** @brief The tyoe of the modifying iterator of the remote index list. */ + typedef typename RemoteIndexList::ModifyIterator RemoteIndexModifier; + + /** @brief The type of the remote inde. */ + typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; + + /** @brief The iterator of the remote index list. */ + typedef typename RemoteIndexList::iterator RemoteIndexIterator; + + /** @brief The const iterator of the remote index list. */ + typedef typename RemoteIndexList::const_iterator ConstRemoteIndexIterator; + + /** @brief Type of the tuple of iterators needed for the adding of indices. */ + typedef std::tuple<RemoteIndexModifier,GlobalIndexModifier,BoolListModifier, + const ConstRemoteIndexIterator> IteratorTuple; + + /** + * @brief A tuple of iterators. + * + * Insertion into a single linked list is only possible at the position after the one of the iterator. + * Therefore for each linked list two iterators are needed: One position before the actual entry + * (for insertion) and one positioned at the actual position (for searching). + */ + class Iterators + { + friend class IndicesSyncer<T>; + public: + /** + * @brief Constructor. + * + * Initializes all iterator to first entry and the one before the first entry, respectively. + * @param remoteIndices The list of the remote indices. + * @param globalIndices The list of the coresponding global indices. This is needed because the + * the pointers to the local index will become invalid due to the merging of the index sets. + * @param booleans Whether the remote index was there before the sync process started. + */ + Iterators(RemoteIndexList& remoteIndices, GlobalIndexList& globalIndices, + BoolList& booleans); + + /** + * @brief Default constructor. + */ + Iterators(); + + /** + * @brief Increment all iteraors. + */ + Iterators& operator++(); + + /** + * @brief Insert a new remote index to the underlying remote index list. + * @param index The remote index. + * @param global The global index corresponding to the remote index. + */ + void insert(const RemoteIndex& index, + const std::pair<GlobalIndex,Attribute>& global); + + /** + * @brief Get the remote index at current position. + * @return The current remote index. + */ + RemoteIndex& remoteIndex() const; + + /** + * @brief Get the global index of the remote index at current position. + * @return The current global index. + */ + std::pair<GlobalIndex,Attribute>& globalIndexPair() const; + + Attribute& attribute() const; + + /** + * @brief Was this entry already in the remote index list before the sync process? + * @return True if the current index wasalready in the remote index list + * before the sync process. + */ + bool isOld() const; + + /** + * @brief Reset all the underlying iterators. + * + * Position them to first list entry and the entry before the first entry respectively. + * @param remoteIndices The list of the remote indices. + * @param globalIndices The list of the coresponding global indices. This is needed because the + * the pointers to the local index will become invalid due to the merging of the index sets. + * @param booleans Whether the remote index was there before the sync process started. + */ + void reset(RemoteIndexList& remoteIndices, GlobalIndexList& globalIndices, + BoolList& booleans); + + /** + * @brief Are we not at the end of the list? + * @return True if the iterators are not positioned at the end of the list + * and the tail of the list respectively. + */ + bool isNotAtEnd() const; + + /** + * @brief Are we at the end of the list? + * @return True if the iterators are positioned at the end of the list + * and the tail of the list respectively. + */ + bool isAtEnd() const; + + private: + /** + * @brief The iterator tuple. + * + * The tuple consists of one iterator over a single linked list of remote indices + * initially positioned before the first entry, one over a sll of global indices + * , one over a all of bool values both postioned at the same entry. The another three + * iterators of the same type positioned at the first entry. Last an iterator over the + * sll of remote indices positioned at the end. + */ + IteratorTuple iterators_; + }; + + /** @brief Type of the map from ranks to iterator tuples. */ + typedef std::map<int,Iterators> IteratorsMap; + + /** + * @brief The iterator tuples mapped on the neighbours. + * + * The key of the map is the rank of the neighbour. + * The first entry in the tuple is an iterator over the remote indices + * initially positioned before the first entry. The second entry is an + * iterator over the corresponding global indices also initially positioned + * before the first entry. The third entry an iterator over remote indices + * initially positioned at the beginning. The last entry is the iterator over + * the remote indices positioned at the end. + */ + IteratorsMap iteratorsMap_; + + /** @brief Calculates the message sizes to send. */ + void calculateMessageSizes(); + + /** + * @brief Pack and send the message for another process. + * @param destination The rank of the process we send to. + * @param buffer The allocated buffer to use. + * @param bufferSize The size of the buffer. + * @param req The MPI_Request to setup the nonblocking send. + */ + void packAndSend(int destination, char* buffer, std::size_t bufferSize, MPI_Request& req); + + /** + * @brief Recv and unpack the message from another process and add the indices. + * @param numberer Functor providing local indices for added global indices. + */ + template<typename T1> + void recvAndUnpack(T1& numberer); + + /** + * @brief Register the MPI datatype for the MessageInformation. + */ + void registerMessageDatatype(); + + /** + * @brief Insert an entry into the remote index list if not yet present. + */ + void insertIntoRemoteIndexList(int process, + const std::pair<GlobalIndex,Attribute>& global, + char attribute); + + /** + * @brief Reset the iterator tuples of all neighbouring processes. + */ + void resetIteratorsMap(); + + /** + * @brief Check whether the iterator tuples of all neighbouring processes + * are reset. + */ + bool checkReset(); + + /** + * @brief Check whether the iterator tuple is reset. + * + * @param iterators The iterator tuple to check. + * @param rlist The SLList of the remote indices. + * @param gList The SLList of the global indices. + * @param bList The SLList of the bool values. + */ + bool checkReset(const Iterators& iterators, RemoteIndexList& rlist, GlobalIndexList& gList, + BoolList& bList); + }; + + template<typename TG, typename TA> + bool operator<(const IndexPair<TG,ParallelLocalIndex<TA> >& i1, + const std::pair<TG,TA>& i2) + { + return i1.global() < i2.first || + (i1.global() == i2.first && i1.local().attribute()<i2.second); + } + + template<typename TG, typename TA> + bool operator<(const std::pair<TG,TA>& i1, + const IndexPair<TG,ParallelLocalIndex<TA> >& i2) + { + return i1.first < i2.global() || + (i1.first == i2.global() && i1.second<i2.local().attribute()); + } + + template<typename TG, typename TA> + bool operator==(const IndexPair<TG,ParallelLocalIndex<TA> >& i1, + const std::pair<TG,TA>& i2) + { + return (i1.global() == i2.first && i1.local().attribute()==i2.second); + } + + template<typename TG, typename TA> + bool operator!=(const IndexPair<TG,ParallelLocalIndex<TA> >& i1, + const std::pair<TG,TA>& i2) + { + return (i1.global() != i2.first || i1.local().attribute()!=i2.second); + } + + template<typename TG, typename TA> + bool operator==(const std::pair<TG,TA>& i2, + const IndexPair<TG,ParallelLocalIndex<TA> >& i1) + { + return (i1.global() == i2.first && i1.local().attribute()==i2.second); + } + + template<typename TG, typename TA> + bool operator!=(const std::pair<TG,TA>& i2, + const IndexPair<TG,ParallelLocalIndex<TA> >& i1) + { + return (i1.global() != i2.first || i1.local().attribute()!=i2.second); + } + + /** + * @brief Stores the corresponding global indices of the remote index information. + * + * Whenever a ParallelIndexSet is resized all RemoteIndices that use it will be invalided + * as the pointers to the index set are invalid after calling ParallelIndexSet::Resize() + * One can rebuild them by storing the global indices in a map with this function and later + * repairing the pointers by calling repairLocalIndexPointers. + * + * @warning The RemoteIndices class has to be build with the same index set for both the + * sending and receiving side + * @param globalMap Map to store the corresponding global indices in. + * @param remoteIndices The remote index information we need to store the corresponding global + * indices of. + * @param indexSet The index set that is for both the sending and receiving side of the remote + * index information. + */ + template<typename T, typename A, typename A1> + void storeGlobalIndicesOfRemoteIndices(std::map<int,SLList<std::pair<typename T::GlobalIndex, typename T::LocalIndex::Attribute>,A> >& globalMap, + const RemoteIndices<T,A1>& remoteIndices) + { + typedef typename RemoteIndices<T,A1>::const_iterator RemoteIterator; + + for(RemoteIterator remote = remoteIndices.begin(), end =remoteIndices.end(); remote != end; ++remote) { + typedef typename RemoteIndices<T,A1>::RemoteIndexList RemoteIndexList; + typedef typename RemoteIndexList::const_iterator RemoteIndexIterator; + typedef SLList<std::pair<typename T::GlobalIndex, typename T::LocalIndex::Attribute>,A> GlobalIndexList; + GlobalIndexList& global = globalMap[remote->first]; + RemoteIndexList& rList = *(remote->second.first); + + for(RemoteIndexIterator index = rList.begin(), riEnd = rList.end(); + index != riEnd; ++index) { + global.push_back(std::make_pair(index->localIndexPair().global(), + index->localIndexPair().local().attribute())); + } + } + } + + /** + * @brief Repair the pointers to the local indices in the remote indices. + * + * @param globalMap The map of the process number to the list of global indices + * corresponding to the remote index list of the process. + * @param remoteIndices The known remote indices. + * @param indexSet The set of local indices of the current process. + */ + template<typename T, typename A, typename A1> + inline void repairLocalIndexPointers(std::map<int, + SLList<std::pair<typename T::GlobalIndex, + typename T::LocalIndex::Attribute>,A> >& globalMap, + RemoteIndices<T,A1>& remoteIndices, + const T& indexSet) + { + typedef typename RemoteIndices<T,A1>::RemoteIndexMap::iterator RemoteIterator; + typedef typename RemoteIndices<T,A1>::RemoteIndexList::iterator RemoteIndexIterator; + typedef typename T::GlobalIndex GlobalIndex; + typedef typename T::LocalIndex::Attribute Attribute; + typedef std::pair<GlobalIndex,Attribute> GlobalIndexPair; + typedef SLList<GlobalIndexPair,A> GlobalIndexPairList; + typedef typename GlobalIndexPairList::iterator GlobalIndexIterator; + + assert(globalMap.size()==static_cast<std::size_t>(remoteIndices.neighbours())); + // Repair pointers to index set in remote indices. + typename std::map<int,GlobalIndexPairList>::iterator global = globalMap.begin(); + RemoteIterator end = remoteIndices.remoteIndices_.end(); + + for(RemoteIterator remote = remoteIndices.remoteIndices_.begin(); remote != end; ++remote, ++global) { + typedef typename T::const_iterator IndexIterator; + + assert(remote->first==global->first); + assert(remote->second.first->size() == global->second.size()); + + RemoteIndexIterator riEnd = remote->second.first->end(); + RemoteIndexIterator rIndex = remote->second.first->begin(); + GlobalIndexIterator gIndex = global->second.begin(); + IndexIterator index = indexSet.begin(); + + assert(rIndex==riEnd || gIndex != global->second.end()); + while(rIndex != riEnd) { + // Search for the index in the set. + assert(gIndex != global->second.end()); + + while(!(index->global() == gIndex->first + && index->local().attribute() == gIndex->second)) { + ++index; + // this is only needed for ALU, where there may exist + // more entries with the same global index in the remote index set + // than in the index set + if (index->global() > gIndex->first) { + index=indexSet.begin(); + } + } + + assert(index != indexSet.end() && *index == *gIndex); + + rIndex->localIndex_ = &(*index); + ++index; + ++rIndex; + ++gIndex; + } + } + remoteIndices.sourceSeqNo_ = remoteIndices.source_->seqNo(); + remoteIndices.destSeqNo_ = remoteIndices.target_->seqNo(); + } + + template<typename T> + IndicesSyncer<T>::IndicesSyncer(ParallelIndexSet& indexSet, + RemoteIndices& remoteIndices) + : indexSet_(indexSet), remoteIndices_(remoteIndices) + { + // index sets must match. + assert(remoteIndices.source_ == remoteIndices.target_); + assert(remoteIndices.source_ == &indexSet); + MPI_Comm_rank(remoteIndices_.communicator(), &rank_); + } + + template<typename T> + IndicesSyncer<T>::Iterators::Iterators(RemoteIndexList& remoteIndices, + GlobalIndexList& globalIndices, + BoolList& booleans) + : iterators_(remoteIndices.beginModify(), globalIndices.beginModify(), + booleans.beginModify(), remoteIndices.end()) + { } + + template<typename T> + IndicesSyncer<T>::Iterators::Iterators() + : iterators_() + {} + + template<typename T> + inline typename IndicesSyncer<T>::Iterators& IndicesSyncer<T>::Iterators::operator++() + { + ++(std::get<0>(iterators_)); + ++(std::get<1>(iterators_)); + ++(std::get<2>(iterators_)); + return *this; + } + + template<typename T> + inline void IndicesSyncer<T>::Iterators::insert(const RemoteIndex & index, + const std::pair<GlobalIndex,Attribute>& global) + { + std::get<0>(iterators_).insert(index); + std::get<1>(iterators_).insert(global); + std::get<2>(iterators_).insert(false); + } + + template<typename T> + inline typename IndicesSyncer<T>::RemoteIndex& + IndicesSyncer<T>::Iterators::remoteIndex() const + { + return *(std::get<0>(iterators_)); + } + + template<typename T> + inline std::pair<typename IndicesSyncer<T>::GlobalIndex,typename IndicesSyncer<T>::Attribute>& + IndicesSyncer<T>::Iterators::globalIndexPair() const + { + return *(std::get<1>(iterators_)); + } + + template<typename T> + inline bool IndicesSyncer<T>::Iterators::isOld() const + { + return *(std::get<2>(iterators_)); + } + + template<typename T> + inline void IndicesSyncer<T>::Iterators::reset(RemoteIndexList& remoteIndices, + GlobalIndexList& globalIndices, + BoolList& booleans) + { + std::get<0>(iterators_) = remoteIndices.beginModify(); + std::get<1>(iterators_) = globalIndices.beginModify(); + std::get<2>(iterators_) = booleans.beginModify(); + } + + template<typename T> + inline bool IndicesSyncer<T>::Iterators::isNotAtEnd() const + { + return std::get<0>(iterators_) != std::get<3>(iterators_); + } + + template<typename T> + inline bool IndicesSyncer<T>::Iterators::isAtEnd() const + { + return std::get<0>(iterators_) == std::get<3>(iterators_); + } + + template<typename T> + void IndicesSyncer<T>::registerMessageDatatype() + { + MPI_Datatype type[2] = {MPI_INT, MPI_INT}; + int blocklength[2] = {1,1}; + MPI_Aint displacement[2]; + MPI_Aint base; + + // Compute displacement + MessageInformation message; + + MPI_Get_address( &(message.publish), displacement); + MPI_Get_address( &(message.pairs), displacement+1); + + // Make the displacement relative + MPI_Get_address(&message, &base); + displacement[0] -= base; + displacement[1] -= base; + + MPI_Type_create_struct( 2, blocklength, displacement, type, &datatype_); + MPI_Type_commit(&datatype_); + } + + template<typename T> + void IndicesSyncer<T>::calculateMessageSizes() + { + typedef typename ParallelIndexSet::const_iterator IndexIterator; + typedef CollectiveIterator<T,typename RemoteIndices::Allocator> CollectiveIterator; + + IndexIterator iEnd = indexSet_.end(); + CollectiveIterator collIter = remoteIndices_.template iterator<true>(); + + for(IndexIterator index = indexSet_.begin(); index != iEnd; ++index) { + collIter.advance(index->global(), index->local().attribute()); + if(collIter.empty()) + break; + int knownRemote=0; + + typedef typename CollectiveIterator::iterator ValidIterator; + ValidIterator end = collIter.end(); + + // Count the remote indices we know. + for(ValidIterator valid = collIter.begin(); valid != end; ++valid) { + ++knownRemote; + } + + if(knownRemote>0) { + Dune::dverb<<rank_<<": publishing "<<knownRemote<<" for index "<<index->global()<< " for processes "; + + // Update MessageInformation + for(ValidIterator valid = collIter.begin(); valid != end; ++valid) { + ++(infoSend_[valid.process()].publish); + (infoSend_[valid.process()].pairs) += knownRemote; + Dune::dverb<<valid.process()<<" "; + Dune::dverb<<"(publish="<<infoSend_[valid.process()].publish<<", pairs="<<infoSend_[valid.process()].pairs + <<") "; + } + Dune::dverb<<std::endl; + } + } + + typedef typename std::map<int,MessageInformation>::const_iterator + MessageIterator; + + const MessageIterator end = infoSend_.end(); + + // registerMessageDatatype(); + + // Now determine the buffersizes needed for each neighbour using MPI_Pack_size + MessageInformation dummy; + + MessageIterator messageIter= infoSend_.begin(); + + typedef typename RemoteIndices::RemoteIndexMap::const_iterator RemoteIterator; + const RemoteIterator rend = remoteIndices_.end(); + int neighbour=0; + + for(RemoteIterator remote = remoteIndices_.begin(); remote != rend; ++remote, ++neighbour) { + MessageInformation* message; + MessageInformation recv; + + if(messageIter != end && messageIter->first==remote->first) { + // We want to send message information to that process + message = const_cast<MessageInformation*>(&(messageIter->second)); + ++messageIter; + }else + // We do not want to send information but the other process might. + message = &dummy; + + sendBufferSizes_[neighbour]=0; + int tsize; + // The number of indices published + MPI_Pack_size(1, MPI_INT,remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + + for(int i=0; i < message->publish; ++i) { + // The global index + MPI_Pack_size(1, MPITraits<GlobalIndex>::getType(), remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + // The attribute in the local index + MPI_Pack_size(1, MPI_CHAR, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + // The number of corresponding remote indices + MPI_Pack_size(1, MPI_INT, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + } + for(int i=0; i < message->pairs; ++i) { + // The process of the remote index + MPI_Pack_size(1, MPI_INT, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + // The attribute of the remote index + MPI_Pack_size(1, MPI_CHAR, remoteIndices_.communicator(), &tsize); + sendBufferSizes_[neighbour] += tsize; + } + + Dune::dverb<<rank_<<": Buffer (neighbour="<<remote->first<<") size is "<< sendBufferSizes_[neighbour]<<" for publish="<<message->publish<<" pairs="<<message->pairs<<std::endl; + } + + } + + template<typename T> + inline void IndicesSyncer<T>::sync() + { + DefaultNumberer numberer; + sync(numberer); + } + + template<typename T> + template<typename T1> + void IndicesSyncer<T>::sync(T1& numberer) + { + + // The pointers to the local indices in the remote indices + // will become invalid due to the resorting of the index set. + // Therefore store the corresponding global indices. + // Mark all indices as not added + + typedef typename RemoteIndices::RemoteIndexMap::const_iterator + RemoteIterator; + + const RemoteIterator end = remoteIndices_.end(); + + // Number of neighbours might change during the syncing. + // save the old neighbours + std::size_t noOldNeighbours = remoteIndices_.neighbours(); + int* oldNeighbours = new int[noOldNeighbours]; + sendBufferSizes_ = new std::size_t[noOldNeighbours]; + std::size_t neighbourI = 0; + + for(RemoteIterator remote = remoteIndices_.begin(); remote != end; ++remote, ++neighbourI) { + typedef typename RemoteIndices::RemoteIndexList::const_iterator + RemoteIndexIterator; + + oldNeighbours[neighbourI] = remote->first; + + // Make sure we only have one remote index list. + assert(remote->second.first==remote->second.second); + + RemoteIndexList& rList = *(remote->second.first); + + // Store the corresponding global indices. + GlobalIndexList& global = globalMap_[remote->first]; + BoolList& added = oldMap_[remote->first]; + RemoteIndexIterator riEnd = rList.end(); + + for(RemoteIndexIterator index = rList.begin(); + index != riEnd; ++index) { + global.push_back(std::make_pair(index->localIndexPair().global(), + index->localIndexPair().local().attribute())); + added.push_back(true); + } + + Iterators iterators(rList, global, added); + iteratorsMap_.insert(std::make_pair(remote->first, iterators)); + assert(checkReset(iteratorsMap_[remote->first], rList,global,added)); + } + + // Exchange indices with each neighbour + calculateMessageSizes(); + + // Allocate the buffers + receiveBufferSize_=1; + sendBuffers_ = new char*[noOldNeighbours]; + + for(std::size_t i=0; i<noOldNeighbours; ++i) { + sendBuffers_[i] = new char[sendBufferSizes_[i]]; + receiveBufferSize_ = std::max(receiveBufferSize_, static_cast<int>(sendBufferSizes_[i])); + } + + receiveBuffer_=new char[receiveBufferSize_]; + + indexSet_.beginResize(); + + Dune::dverb<<rank_<<": Neighbours: "; + + for(std::size_t i = 0; i<noOldNeighbours; ++i) + Dune::dverb<<oldNeighbours[i]<<" "; + + Dune::dverb<<std::endl; + + MPI_Request* requests = new MPI_Request[noOldNeighbours]; + MPI_Status* statuses = new MPI_Status[noOldNeighbours]; + + // Pack Message data and start the sends + for(std::size_t i = 0; i<noOldNeighbours; ++i) + packAndSend(oldNeighbours[i], sendBuffers_[i], sendBufferSizes_[i], requests[i]); + + // Probe for incoming messages, receive and unpack them + for(std::size_t i = 0; i<noOldNeighbours; ++i) + recvAndUnpack(numberer); + // }else{ + // recvAndUnpack(oldNeighbours[i], numberer); + // packAndSend(oldNeighbours[i]); + // } + // } -/** -* @brief Repair the pointers to the local indices in the remote indices. -* -* @param globalMap The map of the process number to the list of global indices -* corresponding to the remote index list of the process. -* @param remoteIndices The known remote indices. -* @param indexSet The set of local indices of the current process. -*/ -template<typename T, typename A, typename A1> -inline void repairLocalIndexPointers(std::map<int, -SLList<std::pair<typename T::GlobalIndex, -typename T::LocalIndex::Attribute>,A> >& globalMap, -RemoteIndices<T,A1>& remoteIndices, -const T& indexSet) -{ -typedef typename RemoteIndices<T,A1>::RemoteIndexMap::iterator RemoteIterator; -typedef typename RemoteIndices<T,A1>::RemoteIndexList::iterator RemoteIndexIterator; -typedef typename T::GlobalIndex GlobalIndex; -typedef typename T::LocalIndex::Attribute Attribute; -typedef std::pair<GlobalIndex,Attribute> GlobalIndexPair; -typedef SLList<GlobalIndexPair,A> GlobalIndexPairList; -typedef typename GlobalIndexPairList::iterator GlobalIndexIterator; - -assert(globalMap.size()==static_cast<std::size_t>(remoteIndices.neighbours())); -// Repair pointers to index set in remote indices. -typename std::map<int,GlobalIndexPairList>::iterator global = globalMap.begin(); -RemoteIterator end = remoteIndices.remoteIndices_.end(); - -for(RemoteIterator remote = remoteIndices.remoteIndices_.begin(); remote != end; ++remote, ++global) { -typedef typename T::const_iterator IndexIterator; - -assert(remote->first==global->first); -assert(remote->second.first->size() == global->second.size()); - -RemoteIndexIterator riEnd = remote->second.first->end(); -RemoteIndexIterator rIndex = remote->second.first->begin(); -GlobalIndexIterator gIndex = global->second.begin(); -IndexIterator index = indexSet.begin(); - -assert(rIndex==riEnd || gIndex != global->second.end()); -while(rIndex != riEnd) { -// Search for the index in the set. -assert(gIndex != global->second.end()); - -while(!(index->global() == gIndex->first -&& index->local().attribute() == gIndex->second)) { -++index; -// this is only needed for ALU, where there may exist -// more entries with the same global index in the remote index set -// than in the index set -if (index->global() > gIndex->first) { -index=indexSet.begin(); -} -} - -assert(index != indexSet.end() && *index == *gIndex); - -rIndex->localIndex_ = &(*index); -++index; -++rIndex; -++gIndex; -} -} -remoteIndices.sourceSeqNo_ = remoteIndices.source_->seqNo(); -remoteIndices.destSeqNo_ = remoteIndices.target_->seqNo(); -} + delete[] receiveBuffer_; -template<typename T> -IndicesSyncer<T>::IndicesSyncer(ParallelIndexSet& indexSet, -RemoteIndices& remoteIndices) -: indexSet_(indexSet), remoteIndices_(remoteIndices) -{ -// index sets must match. -assert(remoteIndices.source_ == remoteIndices.target_); -assert(remoteIndices.source_ == &indexSet); -MPI_Comm_rank(remoteIndices_.communicator(), &rank_); -} + // Wait for the completion of the sends + // Wait for completion of sends + if(MPI_SUCCESS!=MPI_Waitall(noOldNeighbours, requests, statuses)) { + std::cerr<<": MPI_Error occurred while sending message"<<std::endl; + for(std::size_t i=0; i< noOldNeighbours; i++) + if(MPI_SUCCESS!=statuses[i].MPI_ERROR) + std::cerr<<"Destination "<<statuses[i].MPI_SOURCE<<" error code: "<<statuses[i].MPI_ERROR<<std::endl; + } -template<typename T> -IndicesSyncer<T>::Iterators::Iterators(RemoteIndexList& remoteIndices, -GlobalIndexList& globalIndices, -BoolList& booleans) -: iterators_(remoteIndices.beginModify(), globalIndices.beginModify(), -booleans.beginModify(), remoteIndices.end()) -{ } - -template<typename T> -IndicesSyncer<T>::Iterators::Iterators() -: iterators_() -{} - -template<typename T> -inline typename IndicesSyncer<T>::Iterators& IndicesSyncer<T>::Iterators::operator++() -{ -++(std::get<0>(iterators_)); -++(std::get<1>(iterators_)); -++(std::get<2>(iterators_)); -return *this; -} + delete[] statuses; + delete[] requests; -template<typename T> -inline void IndicesSyncer<T>::Iterators::insert(const RemoteIndex & index, -const std::pair<GlobalIndex,Attribute>& global) -{ -std::get<0>(iterators_).insert(index); -std::get<1>(iterators_).insert(global); -std::get<2>(iterators_).insert(false); -} - -template<typename T> -inline typename IndicesSyncer<T>::RemoteIndex& -IndicesSyncer<T>::Iterators::remoteIndex() const -{ -return *(std::get<0>(iterators_)); -} - -template<typename T> -inline std::pair<typename IndicesSyncer<T>::GlobalIndex,typename IndicesSyncer<T>::Attribute>& -IndicesSyncer<T>::Iterators::globalIndexPair() const -{ -return *(std::get<1>(iterators_)); -} - -template<typename T> -inline bool IndicesSyncer<T>::Iterators::isOld() const -{ -return *(std::get<2>(iterators_)); -} - -template<typename T> -inline void IndicesSyncer<T>::Iterators::reset(RemoteIndexList& remoteIndices, -GlobalIndexList& globalIndices, -BoolList& booleans) -{ -std::get<0>(iterators_) = remoteIndices.beginModify(); -std::get<1>(iterators_) = globalIndices.beginModify(); -std::get<2>(iterators_) = booleans.beginModify(); -} + for(std::size_t i=0; i<noOldNeighbours; ++i) + delete[] sendBuffers_[i]; + + delete[] sendBuffers_; + delete[] sendBufferSizes_; -template<typename T> -inline bool IndicesSyncer<T>::Iterators::isNotAtEnd() const -{ -return std::get<0>(iterators_) != std::get<3>(iterators_); -} - -template<typename T> -inline bool IndicesSyncer<T>::Iterators::isAtEnd() const -{ -return std::get<0>(iterators_) == std::get<3>(iterators_); -} - -template<typename T> -void IndicesSyncer<T>::registerMessageDatatype() -{ -MPI_Datatype type[2] = {MPI_INT, MPI_INT}; -int blocklength[2] = {1,1}; -MPI_Aint displacement[2]; -MPI_Aint base; - -// Compute displacement -MessageInformation message; - -MPI_Get_address( &(message.publish), displacement); -MPI_Get_address( &(message.pairs), displacement+1); - -// Make the displacement relative -MPI_Get_address(&message, &base); -displacement[0] -= base; -displacement[1] -= base; - -MPI_Type_create_struct( 2, blocklength, displacement, type, &datatype_); -MPI_Type_commit(&datatype_); -} - -template<typename T> -void IndicesSyncer<T>::calculateMessageSizes() -{ -typedef typename ParallelIndexSet::const_iterator IndexIterator; -typedef CollectiveIterator<T,typename RemoteIndices::Allocator> CollectiveIterator; - -IndexIterator iEnd = indexSet_.end(); -CollectiveIterator collIter = remoteIndices_.template iterator<true>(); - -for(IndexIterator index = indexSet_.begin(); index != iEnd; ++index) { -collIter.advance(index->global(), index->local().attribute()); -if(collIter.empty()) -break; -int knownRemote=0; - -typedef typename CollectiveIterator::iterator ValidIterator; -ValidIterator end = collIter.end(); - -// Count the remote indices we know. -for(ValidIterator valid = collIter.begin(); valid != end; ++valid) { -++knownRemote; -} - -if(knownRemote>0) { -Dune::dverb<<rank_<<": publishing "<<knownRemote<<" for index "<<index->global()<< " for processes "; - -// Update MessageInformation -for(ValidIterator valid = collIter.begin(); valid != end; ++valid) { -++(infoSend_[valid.process()].publish); -(infoSend_[valid.process()].pairs) += knownRemote; -Dune::dverb<<valid.process()<<" "; -Dune::dverb<<"(publish="<<infoSend_[valid.process()].publish<<", pairs="<<infoSend_[valid.process()].pairs -<<") "; -} -Dune::dverb<<std::endl; -} -} - -typedef typename std::map<int,MessageInformation>::const_iterator -MessageIterator; - -const MessageIterator end = infoSend_.end(); - -// registerMessageDatatype(); - -// Now determine the buffersizes needed for each neighbour using MPI_Pack_size -MessageInformation dummy; - -MessageIterator messageIter= infoSend_.begin(); - -typedef typename RemoteIndices::RemoteIndexMap::const_iterator RemoteIterator; -const RemoteIterator rend = remoteIndices_.end(); -int neighbour=0; - -for(RemoteIterator remote = remoteIndices_.begin(); remote != rend; ++remote, ++neighbour) { -MessageInformation* message; -MessageInformation recv; - -if(messageIter != end && messageIter->first==remote->first) { -// We want to send message information to that process -message = const_cast<MessageInformation*>(&(messageIter->second)); -++messageIter; -}else -// We do not want to send information but the other process might. -message = &dummy; - -sendBufferSizes_[neighbour]=0; -int tsize; -// The number of indices published -MPI_Pack_size(1, MPI_INT,remoteIndices_.communicator(), &tsize); -sendBufferSizes_[neighbour] += tsize; - -for(int i=0; i < message->publish; ++i) { -// The global index -MPI_Pack_size(1, MPITraits<GlobalIndex>::getType(), remoteIndices_.communicator(), &tsize); -sendBufferSizes_[neighbour] += tsize; -// The attribute in the local index -MPI_Pack_size(1, MPI_CHAR, remoteIndices_.communicator(), &tsize); -sendBufferSizes_[neighbour] += tsize; -// The number of corresponding remote indices -MPI_Pack_size(1, MPI_INT, remoteIndices_.communicator(), &tsize); -sendBufferSizes_[neighbour] += tsize; -} -for(int i=0; i < message->pairs; ++i) { -// The process of the remote index -MPI_Pack_size(1, MPI_INT, remoteIndices_.communicator(), &tsize); -sendBufferSizes_[neighbour] += tsize; -// The attribute of the remote index -MPI_Pack_size(1, MPI_CHAR, remoteIndices_.communicator(), &tsize); -sendBufferSizes_[neighbour] += tsize; -} - -Dune::dverb<<rank_<<": Buffer (neighbour="<<remote->first<<") size is "<< sendBufferSizes_[neighbour]<<" for publish="<<message->publish<<" pairs="<<message->pairs<<std::endl; -} - -} - -template<typename T> -inline void IndicesSyncer<T>::sync() -{ -DefaultNumberer numberer; -sync(numberer); -} - -template<typename T> -template<typename T1> -void IndicesSyncer<T>::sync(T1& numberer) -{ - -// The pointers to the local indices in the remote indices -// will become invalid due to the resorting of the index set. -// Therefore store the corresponding global indices. -// Mark all indices as not added - -typedef typename RemoteIndices::RemoteIndexMap::const_iterator -RemoteIterator; - -const RemoteIterator end = remoteIndices_.end(); - -// Number of neighbours might change during the syncing. -// save the old neighbours -std::size_t noOldNeighbours = remoteIndices_.neighbours(); -int* oldNeighbours = new int[noOldNeighbours]; -sendBufferSizes_ = new std::size_t[noOldNeighbours]; -std::size_t neighbourI = 0; - -for(RemoteIterator remote = remoteIndices_.begin(); remote != end; ++remote, ++neighbourI) { -typedef typename RemoteIndices::RemoteIndexList::const_iterator -RemoteIndexIterator; - -oldNeighbours[neighbourI] = remote->first; - -// Make sure we only have one remote index list. -assert(remote->second.first==remote->second.second); - -RemoteIndexList& rList = *(remote->second.first); - -// Store the corresponding global indices. -GlobalIndexList& global = globalMap_[remote->first]; -BoolList& added = oldMap_[remote->first]; -RemoteIndexIterator riEnd = rList.end(); - -for(RemoteIndexIterator index = rList.begin(); -index != riEnd; ++index) { -global.push_back(std::make_pair(index->localIndexPair().global(), -index->localIndexPair().local().attribute())); -added.push_back(true); -} - -Iterators iterators(rList, global, added); -iteratorsMap_.insert(std::make_pair(remote->first, iterators)); -assert(checkReset(iteratorsMap_[remote->first], rList,global,added)); -} - -// Exchange indices with each neighbour -calculateMessageSizes(); - -// Allocate the buffers -receiveBufferSize_=1; -sendBuffers_ = new char*[noOldNeighbours]; - -for(std::size_t i=0; i<noOldNeighbours; ++i) { -sendBuffers_[i] = new char[sendBufferSizes_[i]]; -receiveBufferSize_ = std::max(receiveBufferSize_, static_cast<int>(sendBufferSizes_[i])); -} - -receiveBuffer_=new char[receiveBufferSize_]; - -indexSet_.beginResize(); - -Dune::dverb<<rank_<<": Neighbours: "; - -for(std::size_t i = 0; i<noOldNeighbours; ++i) -Dune::dverb<<oldNeighbours[i]<<" "; - -Dune::dverb<<std::endl; - -MPI_Request* requests = new MPI_Request[noOldNeighbours]; -MPI_Status* statuses = new MPI_Status[noOldNeighbours]; - -// Pack Message data and start the sends -for(std::size_t i = 0; i<noOldNeighbours; ++i) -packAndSend(oldNeighbours[i], sendBuffers_[i], sendBufferSizes_[i], requests[i]); - -// Probe for incoming messages, receive and unpack them -for(std::size_t i = 0; i<noOldNeighbours; ++i) -recvAndUnpack(numberer); -// }else{ -// recvAndUnpack(oldNeighbours[i], numberer); -// packAndSend(oldNeighbours[i]); -// } -// } - -delete[] receiveBuffer_; - -// Wait for the completion of the sends -// Wait for completion of sends -if(MPI_SUCCESS!=MPI_Waitall(noOldNeighbours, requests, statuses)) { -std::cerr<<": MPI_Error occurred while sending message"<<std::endl; -for(std::size_t i=0; i< noOldNeighbours; i++) -if(MPI_SUCCESS!=statuses[i].MPI_ERROR) -std::cerr<<"Destination "<<statuses[i].MPI_SOURCE<<" error code: "<<statuses[i].MPI_ERROR<<std::endl; -} - -delete[] statuses; -delete[] requests; - -for(std::size_t i=0; i<noOldNeighbours; ++i) -delete[] sendBuffers_[i]; - -delete[] sendBuffers_; -delete[] sendBufferSizes_; - -// No need for the iterator tuples any more -iteratorsMap_.clear(); - -indexSet_.endResize(); - -delete[] oldNeighbours; - -repairLocalIndexPointers(globalMap_, remoteIndices_, indexSet_); - -oldMap_.clear(); -globalMap_.clear(); - -// update the sequence number -remoteIndices_.sourceSeqNo_ = remoteIndices_.destSeqNo_ = indexSet_.seqNo(); -} - -template<typename T> -void IndicesSyncer<T>::packAndSend(int destination, char* buffer, std::size_t bufferSize, MPI_Request& request) -{ -typedef typename ParallelIndexSet::const_iterator IndexIterator; - -IndexIterator iEnd = indexSet_.end(); -int bpos = 0; -int published = 0; -int pairs = 0; - -assert(checkReset()); - -// Pack the number of indices we publish -MPI_Pack(&(infoSend_[destination].publish), 1, MPI_INT, buffer, bufferSize, &bpos, -remoteIndices_.communicator()); - -for(IndexIterator index = indexSet_.begin(); index != iEnd; ++index) { -// Search for corresponding remote indices in all iterator tuples -typedef typename IteratorsMap::iterator Iterator; -Iterator iteratorsEnd = iteratorsMap_.end(); - -// advance all iterators to a position with global index >= index->global() -for(Iterator iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) { -while(iterators->second.isNotAtEnd() && -iterators->second.globalIndexPair().first < index->global()) -++(iterators->second); -assert(!iterators->second.isNotAtEnd() || iterators->second.globalIndexPair().first >= index->global()); -} - -// Add all remote indices positioned at global which were already present before calling sync -// to the message. -// Count how many remote indices we will send -int indices = 0; -bool knownRemote = false; // Is the remote process supposed to know this index? - -for(Iterator iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) -{ -std::pair<GlobalIndex,Attribute> p; -if (iterators->second.isNotAtEnd()) -{ -p = iterators->second.globalIndexPair(); -} - -if(iterators->second.isNotAtEnd() && iterators->second.isOld() -&& iterators->second.globalIndexPair().first == index->global()) { -indices++; -if(destination == iterators->first) -knownRemote = true; -} -} - -if(!knownRemote) -// We do not need to send any indices -continue; - -Dune::dverb<<rank_<<": sending "<<indices<<" for index "<<index->global()<<" to "<<destination<<std::endl; - - -// Pack the global index, the attribute and the number -MPI_Pack(const_cast<GlobalIndex*>(&(index->global())), 1, MPITraits<GlobalIndex>::getType(), buffer, bufferSize, &bpos, -remoteIndices_.communicator()); - -char attr = index->local().attribute(); -MPI_Pack(&attr, 1, MPI_CHAR, buffer, bufferSize, &bpos, -remoteIndices_.communicator()); - -// Pack the number of remote indices we send. -MPI_Pack(&indices, 1, MPI_INT, buffer, bufferSize, &bpos, -remoteIndices_.communicator()); - -// Pack the information about the remote indices -for(Iterator iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) -if(iterators->second.isNotAtEnd() && iterators->second.isOld() -&& iterators->second.globalIndexPair().first == index->global()) { -int process = iterators->first; - -++pairs; -assert(pairs <= infoSend_[destination].pairs); -MPI_Pack(&process, 1, MPI_INT, buffer, bufferSize, &bpos, -remoteIndices_.communicator()); -char attr2 = iterators->second.remoteIndex().attribute(); - -MPI_Pack(&attr2, 1, MPI_CHAR, buffer, bufferSize, &bpos, -remoteIndices_.communicator()); ---indices; -} -assert(indices==0); -++published; -Dune::dvverb<<" (publish="<<published<<", pairs="<<pairs<<")"<<std::endl; -assert(published <= infoSend_[destination].publish); -} - -// Make sure we send all expected entries -assert(published == infoSend_[destination].publish); -assert(pairs == infoSend_[destination].pairs); -resetIteratorsMap(); - -Dune::dverb << rank_<<": Sending message of "<<bpos<<" bytes to "<<destination<<std::endl; - -MPI_Issend(buffer, bpos, MPI_PACKED, destination, 345, remoteIndices_.communicator(),&request); -} - -template<typename T> -inline void IndicesSyncer<T>::insertIntoRemoteIndexList(int process, -const std::pair<GlobalIndex,Attribute>& globalPair, -char attribute) -{ -Dune::dverb<<"Inserting from "<<process<<" "<<globalPair.first<<", "<< -globalPair.second<<" "<<attribute<<std::endl; - -resetIteratorsMap(); - -// There might be cases where there no remote indices for that process yet -typename IteratorsMap::iterator found = iteratorsMap_.find(process); - -if( found == iteratorsMap_.end() ) { -Dune::dverb<<"Discovered new neighbour "<<process<<std::endl; -RemoteIndexList* rlist = new RemoteIndexList(); -remoteIndices_.remoteIndices_.insert(std::make_pair(process,std::make_pair(rlist,rlist))); -Iterators iterators = Iterators(*rlist, globalMap_[process], oldMap_[process]); -found = iteratorsMap_.insert(std::make_pair(process, iterators)).first; -} - -Iterators& iterators = found->second; - -// Search for the remote index -while(iterators.isNotAtEnd() && iterators.globalIndexPair() < globalPair) { -// Increment all iterators -++iterators; - -} - -if(iterators.isAtEnd() || iterators.globalIndexPair() != globalPair) { -// The entry is not yet known -// Insert in the list and do not change the first iterator. -iterators.insert(RemoteIndex(Attribute(attribute)),globalPair); -return; -} - -// Global indices match -bool indexIsThere=false; -for(Iterators tmpIterators = iterators; -!tmpIterators.isAtEnd() && tmpIterators.globalIndexPair() == globalPair; -++tmpIterators) -//entry already exists with the same attribute -if(tmpIterators.globalIndexPair().second == attribute) { -indexIsThere=true; -break; -} - -if(!indexIsThere) -// The entry is not yet known -// Insert in the list and do not change the first iterator. -iterators.insert(RemoteIndex(Attribute(attribute)),globalPair); -} - -template<typename T> -template<typename T1> -void IndicesSyncer<T>::recvAndUnpack(T1& numberer) -{ -typedef typename ParallelIndexSet::const_iterator IndexIterator; - -IndexIterator iEnd = indexSet_.end(); -IndexIterator index = indexSet_.begin(); -int bpos = 0; -int publish; - -assert(checkReset()); - -MPI_Status status; - -// We have to determine the message size and source before the receive -MPI_Probe(MPI_ANY_SOURCE, 345, remoteIndices_.communicator(), &status); - -int source=status.MPI_SOURCE; -int count; -MPI_Get_count(&status, MPI_PACKED, &count); - -Dune::dvverb<<rank_<<": Receiving message from "<< source<<" with "<<count<<" bytes"<<std::endl; - -if(count>receiveBufferSize_) { -receiveBufferSize_=count; -delete[] receiveBuffer_; -receiveBuffer_ = new char[receiveBufferSize_]; -} - -MPI_Recv(receiveBuffer_, count, MPI_PACKED, source, 345, remoteIndices_.communicator(), &status); - -// How many global entries were published? -MPI_Unpack(receiveBuffer_, count, &bpos, &publish, 1, MPI_INT, remoteIndices_.communicator()); - -// Now unpack the remote indices and add them. -while(publish>0) { - -// Unpack information about the local index on the source process -GlobalIndex global; // global index of the current entry -char sourceAttribute; // Attribute on the source process -int pairs; - -MPI_Unpack(receiveBuffer_, count, &bpos, &global, 1, MPITraits<GlobalIndex>::getType(), -remoteIndices_.communicator()); -MPI_Unpack(receiveBuffer_, count, &bpos, &sourceAttribute, 1, MPI_CHAR, -remoteIndices_.communicator()); -MPI_Unpack(receiveBuffer_, count, &bpos, &pairs, 1, MPI_INT, -remoteIndices_.communicator()); - -// Insert the entry on the remote process to our -// remote index list -SLList<std::pair<int,Attribute> > sourceAttributeList; -sourceAttributeList.push_back(std::make_pair(source,Attribute(sourceAttribute))); + // No need for the iterator tuples any more + iteratorsMap_.clear(); + + indexSet_.endResize(); + + delete[] oldNeighbours; + + repairLocalIndexPointers(globalMap_, remoteIndices_, indexSet_); + + oldMap_.clear(); + globalMap_.clear(); + + // update the sequence number + remoteIndices_.sourceSeqNo_ = remoteIndices_.destSeqNo_ = indexSet_.seqNo(); + } + + template<typename T> + void IndicesSyncer<T>::packAndSend(int destination, char* buffer, std::size_t bufferSize, MPI_Request& request) + { + typedef typename ParallelIndexSet::const_iterator IndexIterator; + + IndexIterator iEnd = indexSet_.end(); + int bpos = 0; + int published = 0; + int pairs = 0; + + assert(checkReset()); + + // Pack the number of indices we publish + MPI_Pack(&(infoSend_[destination].publish), 1, MPI_INT, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + for(IndexIterator index = indexSet_.begin(); index != iEnd; ++index) { + // Search for corresponding remote indices in all iterator tuples + typedef typename IteratorsMap::iterator Iterator; + Iterator iteratorsEnd = iteratorsMap_.end(); + + // advance all iterators to a position with global index >= index->global() + for(Iterator iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) { + while(iterators->second.isNotAtEnd() && + iterators->second.globalIndexPair().first < index->global()) + ++(iterators->second); + assert(!iterators->second.isNotAtEnd() || iterators->second.globalIndexPair().first >= index->global()); + } + + // Add all remote indices positioned at global which were already present before calling sync + // to the message. + // Count how many remote indices we will send + int indices = 0; + bool knownRemote = false; // Is the remote process supposed to know this index? + + for(Iterator iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) + { + std::pair<GlobalIndex,Attribute> p; + if (iterators->second.isNotAtEnd()) + { + p = iterators->second.globalIndexPair(); + } + + if(iterators->second.isNotAtEnd() && iterators->second.isOld() + && iterators->second.globalIndexPair().first == index->global()) { + indices++; + if(destination == iterators->first) + knownRemote = true; + } + } + + if(!knownRemote) + // We do not need to send any indices + continue; + + Dune::dverb<<rank_<<": sending "<<indices<<" for index "<<index->global()<<" to "<<destination<<std::endl; + + + // Pack the global index, the attribute and the number + MPI_Pack(const_cast<GlobalIndex*>(&(index->global())), 1, MPITraits<GlobalIndex>::getType(), buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + char attr = index->local().attribute(); + MPI_Pack(&attr, 1, MPI_CHAR, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + // Pack the number of remote indices we send. + MPI_Pack(&indices, 1, MPI_INT, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + + // Pack the information about the remote indices + for(Iterator iterators = iteratorsMap_.begin(); iteratorsEnd != iterators; ++iterators) + if(iterators->second.isNotAtEnd() && iterators->second.isOld() + && iterators->second.globalIndexPair().first == index->global()) { + int process = iterators->first; + + ++pairs; + assert(pairs <= infoSend_[destination].pairs); + MPI_Pack(&process, 1, MPI_INT, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + char attr2 = iterators->second.remoteIndex().attribute(); + + MPI_Pack(&attr2, 1, MPI_CHAR, buffer, bufferSize, &bpos, + remoteIndices_.communicator()); + --indices; + } + assert(indices==0); + ++published; + Dune::dvverb<<" (publish="<<published<<", pairs="<<pairs<<")"<<std::endl; + assert(published <= infoSend_[destination].publish); + } + + // Make sure we send all expected entries + assert(published == infoSend_[destination].publish); + assert(pairs == infoSend_[destination].pairs); + resetIteratorsMap(); + + Dune::dverb << rank_<<": Sending message of "<<bpos<<" bytes to "<<destination<<std::endl; + + MPI_Issend(buffer, bpos, MPI_PACKED, destination, 345, remoteIndices_.communicator(),&request); + } + + template<typename T> + inline void IndicesSyncer<T>::insertIntoRemoteIndexList(int process, + const std::pair<GlobalIndex,Attribute>& globalPair, + char attribute) + { + Dune::dverb<<"Inserting from "<<process<<" "<<globalPair.first<<", "<< + globalPair.second<<" "<<attribute<<std::endl; + + resetIteratorsMap(); + + // There might be cases where there no remote indices for that process yet + typename IteratorsMap::iterator found = iteratorsMap_.find(process); + + if( found == iteratorsMap_.end() ) { + Dune::dverb<<"Discovered new neighbour "<<process<<std::endl; + RemoteIndexList* rlist = new RemoteIndexList(); + remoteIndices_.remoteIndices_.insert(std::make_pair(process,std::make_pair(rlist,rlist))); + Iterators iterators = Iterators(*rlist, globalMap_[process], oldMap_[process]); + found = iteratorsMap_.insert(std::make_pair(process, iterators)).first; + } + + Iterators& iterators = found->second; + + // Search for the remote index + while(iterators.isNotAtEnd() && iterators.globalIndexPair() < globalPair) { + // Increment all iterators + ++iterators; + + } + + if(iterators.isAtEnd() || iterators.globalIndexPair() != globalPair) { + // The entry is not yet known + // Insert in the list and do not change the first iterator. + iterators.insert(RemoteIndex(Attribute(attribute)),globalPair); + return; + } + + // Global indices match + bool indexIsThere=false; + for(Iterators tmpIterators = iterators; + !tmpIterators.isAtEnd() && tmpIterators.globalIndexPair() == globalPair; + ++tmpIterators) + //entry already exists with the same attribute + if(tmpIterators.globalIndexPair().second == attribute) { + indexIsThere=true; + break; + } + + if(!indexIsThere) + // The entry is not yet known + // Insert in the list and do not change the first iterator. + iterators.insert(RemoteIndex(Attribute(attribute)),globalPair); + } + + template<typename T> + template<typename T1> + void IndicesSyncer<T>::recvAndUnpack(T1& numberer) + { + typedef typename ParallelIndexSet::const_iterator IndexIterator; + + IndexIterator iEnd = indexSet_.end(); + IndexIterator index = indexSet_.begin(); + int bpos = 0; + int publish; + + assert(checkReset()); + + MPI_Status status; + + // We have to determine the message size and source before the receive + MPI_Probe(MPI_ANY_SOURCE, 345, remoteIndices_.communicator(), &status); + + int source=status.MPI_SOURCE; + int count; + MPI_Get_count(&status, MPI_PACKED, &count); + + Dune::dvverb<<rank_<<": Receiving message from "<< source<<" with "<<count<<" bytes"<<std::endl; + + if(count>receiveBufferSize_) { + receiveBufferSize_=count; + delete[] receiveBuffer_; + receiveBuffer_ = new char[receiveBufferSize_]; + } + + MPI_Recv(receiveBuffer_, count, MPI_PACKED, source, 345, remoteIndices_.communicator(), &status); + + // How many global entries were published? + MPI_Unpack(receiveBuffer_, count, &bpos, &publish, 1, MPI_INT, remoteIndices_.communicator()); + + // Now unpack the remote indices and add them. + while(publish>0) { + + // Unpack information about the local index on the source process + GlobalIndex global; // global index of the current entry + char sourceAttribute; // Attribute on the source process + int pairs; + + MPI_Unpack(receiveBuffer_, count, &bpos, &global, 1, MPITraits<GlobalIndex>::getType(), + remoteIndices_.communicator()); + MPI_Unpack(receiveBuffer_, count, &bpos, &sourceAttribute, 1, MPI_CHAR, + remoteIndices_.communicator()); + MPI_Unpack(receiveBuffer_, count, &bpos, &pairs, 1, MPI_INT, + remoteIndices_.communicator()); + + // Insert the entry on the remote process to our + // remote index list + SLList<std::pair<int,Attribute> > sourceAttributeList; + sourceAttributeList.push_back(std::make_pair(source,Attribute(sourceAttribute))); #ifndef NDEBUG -bool foundSelf = false; + bool foundSelf = false; #endif -Attribute myAttribute=Attribute(); - -// Unpack the remote indices -for(; pairs>0; --pairs) { -// Unpack the process id that knows the index -int process; -char attribute; -MPI_Unpack(receiveBuffer_, count, &bpos, &process, 1, MPI_INT, -remoteIndices_.communicator()); -// Unpack the attribute -MPI_Unpack(receiveBuffer_, count, &bpos, &attribute, 1, MPI_CHAR, -remoteIndices_.communicator()); - -if(process==rank_) { + Attribute myAttribute=Attribute(); + + // Unpack the remote indices + for(; pairs>0; --pairs) { + // Unpack the process id that knows the index + int process; + char attribute; + MPI_Unpack(receiveBuffer_, count, &bpos, &process, 1, MPI_INT, + remoteIndices_.communicator()); + // Unpack the attribute + MPI_Unpack(receiveBuffer_, count, &bpos, &attribute, 1, MPI_CHAR, + remoteIndices_.communicator()); + + if(process==rank_) { #ifndef NDEBUG -foundSelf=true; + foundSelf=true; #endif -myAttribute=Attribute(attribute); -// Now we know the local attribute of the global index -//Only add the index if it is unknown. -// Do we know that global index already? -IndexIterator pos = std::lower_bound(index, iEnd, IndexPair(global)); - -if(pos == iEnd || pos->global() != global) { -// no entry with this global index -indexSet_.add(global, -ParallelLocalIndex<Attribute>(numberer(global), -myAttribute, true)); -Dune::dvverb << "Adding "<<global<<" "<<myAttribute<<std::endl; -continue; -} - -// because of above the global indices match. Add only if the attribute is different -bool indexIsThere = false; -index=pos; - -for(; pos->global()==global; ++pos) -if(pos->local().attribute() == myAttribute) { -Dune::dvverb<<"found "<<global<<" "<<myAttribute<<std::endl; -indexIsThere = true; -break; -} - -if(!indexIsThere) { -indexSet_.add(global, -ParallelLocalIndex<Attribute>(numberer(global), -myAttribute, true)); -Dune::dvverb << "Adding "<<global<<" "<<myAttribute<<std::endl; -} - -}else{ -sourceAttributeList.push_back(std::make_pair(process,Attribute(attribute))); -} -} -assert(foundSelf); -// Insert remote indices -typedef typename SLList<std::pair<int,Attribute> >::const_iterator Iter; -for(Iter i=sourceAttributeList.begin(), end=sourceAttributeList.end(); -i!=end; ++i) -insertIntoRemoteIndexList(i->first, std::make_pair(global, myAttribute), -i->second); ---publish; -} - -resetIteratorsMap(); -} - -template<typename T> -void IndicesSyncer<T>::resetIteratorsMap(){ - -// Reset iterators in all tuples. -typedef typename IteratorsMap::iterator Iterator; -typedef typename RemoteIndices::RemoteIndexMap::iterator -RemoteIterator; -typedef typename GlobalIndicesMap::iterator GlobalIterator; -typedef typename BoolMap::iterator BoolIterator; - -const RemoteIterator remoteEnd = remoteIndices_.remoteIndices_.end(); -Iterator iterators = iteratorsMap_.begin(); -GlobalIterator global = globalMap_.begin(); -BoolIterator added = oldMap_.begin(); - -for(RemoteIterator remote = remoteIndices_.remoteIndices_.begin(); -remote != remoteEnd; ++remote, ++global, ++added, ++iterators) { -iterators->second.reset(*(remote->second.first), global->second, added->second); -} -} - -template<typename T> -bool IndicesSyncer<T>::checkReset(const Iterators& iterators, RemoteIndexList& rList, GlobalIndexList& gList, -BoolList& bList){ - -if(std::get<0>(iterators.iterators_) != rList.begin()) -return false; -if(std::get<1>(iterators.iterators_) != gList.begin()) -return false; -if(std::get<2>(iterators.iterators_) != bList.begin()) -return false; -return true; -} - - -template<typename T> -bool IndicesSyncer<T>::checkReset(){ - -// Reset iterators in all tuples. -typedef typename IteratorsMap::iterator Iterator; -typedef typename RemoteIndices::RemoteIndexMap::iterator -RemoteIterator; -typedef typename GlobalIndicesMap::iterator GlobalIterator; -typedef typename BoolMap::iterator BoolIterator; - -const RemoteIterator remoteEnd = remoteIndices_.remoteIndices_.end(); -Iterator iterators = iteratorsMap_.begin(); -GlobalIterator global = globalMap_.begin(); -BoolIterator added = oldMap_.begin(); -bool ret = true; - -for(RemoteIterator remote = remoteIndices_.remoteIndices_.begin(); -remote != remoteEnd; ++remote, ++global, ++added, ++iterators) { -if(!checkReset(iterators->second, *(remote->second.first), global->second, -added->second)) -ret=false; -} -return ret; -} + myAttribute=Attribute(attribute); + // Now we know the local attribute of the global index + //Only add the index if it is unknown. + // Do we know that global index already? + IndexIterator pos = std::lower_bound(index, iEnd, IndexPair(global)); + + if(pos == iEnd || pos->global() != global) { + // no entry with this global index + indexSet_.add(global, + ParallelLocalIndex<Attribute>(numberer(global), + myAttribute, true)); + Dune::dvverb << "Adding "<<global<<" "<<myAttribute<<std::endl; + continue; + } + + // because of above the global indices match. Add only if the attribute is different + bool indexIsThere = false; + index=pos; + + for(; pos->global()==global; ++pos) + if(pos->local().attribute() == myAttribute) { + Dune::dvverb<<"found "<<global<<" "<<myAttribute<<std::endl; + indexIsThere = true; + break; + } + + if(!indexIsThere) { + indexSet_.add(global, + ParallelLocalIndex<Attribute>(numberer(global), + myAttribute, true)); + Dune::dvverb << "Adding "<<global<<" "<<myAttribute<<std::endl; + } + + }else{ + sourceAttributeList.push_back(std::make_pair(process,Attribute(attribute))); + } + } + assert(foundSelf); + // Insert remote indices + typedef typename SLList<std::pair<int,Attribute> >::const_iterator Iter; + for(Iter i=sourceAttributeList.begin(), end=sourceAttributeList.end(); + i!=end; ++i) + insertIntoRemoteIndexList(i->first, std::make_pair(global, myAttribute), + i->second); + --publish; + } + + resetIteratorsMap(); + } + + template<typename T> + void IndicesSyncer<T>::resetIteratorsMap(){ + + // Reset iterators in all tuples. + typedef typename IteratorsMap::iterator Iterator; + typedef typename RemoteIndices::RemoteIndexMap::iterator + RemoteIterator; + typedef typename GlobalIndicesMap::iterator GlobalIterator; + typedef typename BoolMap::iterator BoolIterator; + + const RemoteIterator remoteEnd = remoteIndices_.remoteIndices_.end(); + Iterator iterators = iteratorsMap_.begin(); + GlobalIterator global = globalMap_.begin(); + BoolIterator added = oldMap_.begin(); + + for(RemoteIterator remote = remoteIndices_.remoteIndices_.begin(); + remote != remoteEnd; ++remote, ++global, ++added, ++iterators) { + iterators->second.reset(*(remote->second.first), global->second, added->second); + } + } + + template<typename T> + bool IndicesSyncer<T>::checkReset(const Iterators& iterators, RemoteIndexList& rList, GlobalIndexList& gList, + BoolList& bList){ + + if(std::get<0>(iterators.iterators_) != rList.begin()) + return false; + if(std::get<1>(iterators.iterators_) != gList.begin()) + return false; + if(std::get<2>(iterators.iterators_) != bList.begin()) + return false; + return true; + } + + + template<typename T> + bool IndicesSyncer<T>::checkReset(){ + + // Reset iterators in all tuples. + typedef typename IteratorsMap::iterator Iterator; + typedef typename RemoteIndices::RemoteIndexMap::iterator + RemoteIterator; + typedef typename GlobalIndicesMap::iterator GlobalIterator; + typedef typename BoolMap::iterator BoolIterator; + + const RemoteIterator remoteEnd = remoteIndices_.remoteIndices_.end(); + Iterator iterators = iteratorsMap_.begin(); + GlobalIterator global = globalMap_.begin(); + BoolIterator added = oldMap_.begin(); + bool ret = true; + + for(RemoteIterator remote = remoteIndices_.remoteIndices_.begin(); + remote != remoteEnd; ++remote, ++global, ++added, ++iterators) { + if(!checkReset(iterators->second, *(remote->second.first), global->second, + added->second)) + ret=false; + } + return ret; + } } #endif diff --git a/dune/common/parallel/interface.hh b/dune/common/parallel/interface.hh index 84b53483aa211ee36cb78f74ed42a17f5d3c4d8c..b8894e7ec9ddaf36966017197bb2264c6d1cadbb 100644 --- a/dune/common/parallel/interface.hh +++ b/dune/common/parallel/interface.hh @@ -11,518 +11,518 @@ namespace Dune { -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Provides classes for building the communication -* interface between remote indices. -* @author Markus Blatt -*/ - -/** @} */ -/** -* @brief Base class of all classes representing a communication -* interface. -* -* It provides an generic utility method for building the interface -* for a set of remote indices. -*/ -class InterfaceBuilder -{ -public: -class RemoteIndicesStateError : public InvalidStateException -{}; - -virtual ~InterfaceBuilder() -{} - -protected: -/** -* @brief Not for public use. -*/ -InterfaceBuilder() -{} - -/** -* @brief Builds the interface between remote processes. -* -* -* The types T1 and T2 are classes representing a set of -* enumeration values of type InterfaceBuilder::Attribute. They have to provide -* a (static) method -* \code -* bool contains(Attribute flag) const; -* \endcode -* for checking whether the set contains a specific flag. -* This functionality is for example provided the classes -* EnumItem, EnumRange and Combine. -* -* If the template parameter send is true the sending side of -* the interface will be built, otherwise the information for -* receiving will be built. -* -* -* If the template parameter send is true we create interface for sending -* in a forward communication. -* -* @param remoteIndices The indices known to remote processes. -* @param sourceFlags The set of flags marking source indices. -* @param destFlags The setof flags markig destination indices. -* @param functor A functor for callbacks. It should provide the -* following methods: -* \code -* // Reserve memory for the interface to processor proc. The interface -* // has to hold size entries -* void reserve(int proc, int size); -* -* // Add an entry to the interface -* // We will send/receive size entries at index local to process proc -* void add(int proc, int local); -* \endcode -*/ -template<class R, class T1, class T2, class Op, bool send> -void buildInterface (const R& remoteIndices, -const T1& sourceFlags, const T2& destFlags, -Op& functor) const; -}; - -/** -* @brief Information describing an interface. -* -* This class is used for temporary gathering information -* about the interface needed for actually building it. It -* is used be class Interface as functor for InterfaceBuilder::build. -*/ -class InterfaceInformation -{ - -public: - -/** -* @brief Get the number of entries in the interface. -*/ -size_t size() const -{ -return size_; -} -/** -* @brief Get the local index for an entry. -* @param i The index of the entry. -*/ -std::size_t& operator[](size_t i) -{ -assert(i<size_); -return indices_[i]; -} -/** -* @brief Get the local index for an entry. -* @param i The index of the entry. -*/ -std::size_t operator[](size_t i) const -{ -assert(i<size_); -return indices_[i]; -} -/** -* @brief Reserve space for a number of entries. -* @param size The maximum number of entries to hold. -*/ -void reserve(size_t size) -{ -indices_ = new std::size_t[size]; -maxSize_ = size; - -} -/** -* brief Frees allocated memory. -*/ -void free() -{ -if(indices_) -delete[] indices_; -maxSize_ = 0; -size_=0; -indices_=0; -} -/** -* @brief Add a new index to the interface. -*/ -void add(std::size_t index) -{ -assert(size_<maxSize_); -indices_[size_++]=index; -} - -InterfaceInformation() -: size_(0), maxSize_(0), indices_(0) -{} - -virtual ~InterfaceInformation() -{} - -bool operator!=(const InterfaceInformation& o) const -{ -return !operator==(o); -} - -bool operator==(const InterfaceInformation& o) const -{ -if(size_!=o.size_) -return false; -for(std::size_t i=0; i< size_; ++i) -if(indices_[i]!=o.indices_[i]) -return false; -return true; -} - -private: -/** -* @brief The number of entries in the interface. -*/ -size_t size_; -/** -* @brief The maximum number of indices we can hold. -*/ -size_t maxSize_; -/** -* @brief The local indices of the interface. -*/ -std::size_t* indices_; -}; - -/** @addtogroup Common_Parallel -* -* @{ -*/ - -/** -* @brief Communication interface between remote and local indices. -* -* Describes the communication interface between -* indices on the local process and those on remote processes. -*/ -class Interface : public InterfaceBuilder -{ - -public: -/** -* @brief The type of the map form process number to InterfaceInformation for -* sending and receiving to and from it. -*/ -typedef std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > InformationMap; - -/** -* @brief Builds the interface. -* -* The types T1 and T2 are classes representing a set of -* enumeration values of type Interface::Attribute. They have to provide -* a (static) method -* \code -* bool contains(Attribute flag) const; -* \endcode -* for checking whether the set contains a specific flag. -* This functionality is for example provided the classes -* EnumItem, EnumRange and Combine. -* @param remoteIndices The indices known to remote processes. -* @param sourceFlags The set of flags marking indices we send from. -* @param destFlags The set of flags marking indices we receive for. -*/ -template<typename R, typename T1, typename T2> -void build(const R& remoteIndices, const T1& sourceFlags, -const T2& destFlags); - -/** -* @brief Frees memory allocated during the build. -*/ -void free(); - -/** -* @brief Get the MPI Communicator. -*/ -MPI_Comm communicator() const; - -/** -* @brief Get information about the interfaces. -* -* @return Map of the interfaces. -* The key of the map is the process number and the value -* is the information pair (first the send and then the receive -* information). -*/ -const InformationMap& interfaces() const; - -Interface(MPI_Comm comm) -: communicator_(comm), interfaces_() -{} - -Interface() -: communicator_(MPI_COMM_NULL), interfaces_() -{} - -/** -* @brief Print the interface to std::out for debugging. -*/ -void print() const; - -bool operator!=(const Interface& o) const -{ -return ! operator==(o); -} - -bool operator==(const Interface& o) const -{ -if(communicator_!=o.communicator_) -return false; -if(interfaces_.size()!=o.interfaces_.size()) -return false; -typedef InformationMap::const_iterator MIter; - -for(MIter m=interfaces_.begin(), om=o.interfaces_.begin(); -m!=interfaces_.end(); ++m, ++om) -{ -if(om->first!=m->first) -return false; -if(om->second.first!=om->second.first) -return false; -if(om->second.second!=om->second.second) -return false; -} -return true; -} - -/** -* @brief Destructor. -*/ -virtual ~Interface(); - -void strip(); -protected: - -/** -* @brief Get information about the interfaces. -* -* @return Map of the interfaces. -* The key of the map is the process number and the value -* is the information pair (first the send and then the receive -* information). -*/ -InformationMap& interfaces(); - -/** @brief The MPI communicator we use. */ -MPI_Comm communicator_; - -private: -/** -* @brief Information about the interfaces. -* -* The key of the map is the process number and the value -* is the information pair (first the send and then the receive -* information). -*/ -InformationMap interfaces_; - -template<bool send> -class InformationBuilder -{ -public: -InformationBuilder(InformationMap& interfaces) -: interfaces_(interfaces) -{} - -void reserve(int proc, int size) -{ -if(send) -interfaces_[proc].first.reserve(size); -else -interfaces_[proc].second.reserve(size); -} -void add(int proc, std::size_t local) -{ -if(send) { -interfaces_[proc].first.add(local); -}else{ -interfaces_[proc].second.add(local); -} -} - -private: -InformationMap& interfaces_; -}; -}; - -template<class R, class T1, class T2, class Op, bool send> -void InterfaceBuilder::buildInterface(const R& remoteIndices, const T1& sourceFlags, const T2& destFlags, Op& interfaceInformation) const -{ - -if(!remoteIndices.isSynced()) -DUNE_THROW(RemoteIndicesStateError,"RemoteIndices is not in sync with the index set. Call RemoteIndices::rebuild first!"); -// Allocate the memory for the data type construction. -typedef R RemoteIndices; -typedef typename RemoteIndices::RemoteIndexMap::const_iterator const_iterator; - -const const_iterator end=remoteIndices.end(); - -int rank; - -MPI_Comm_rank(remoteIndices.communicator(), &rank); - -// Allocate memory for the type construction. -for(const_iterator process=remoteIndices.begin(); process != end; ++process) { -// Messure the number of indices send to the remote process first -int size=0; -typedef typename RemoteIndices::RemoteIndexList::const_iterator RemoteIterator; -const RemoteIterator remoteEnd = send ? process->second.first->end() : -process->second.second->end(); -RemoteIterator remote = send ? process->second.first->begin() : process->second.second->begin(); - -while(remote!=remoteEnd) { -if( send ? destFlags.contains(remote->attribute()) : -sourceFlags.contains(remote->attribute())) { - -// do we send the index? -if( send ? sourceFlags.contains(remote->localIndexPair().local().attribute()) : -destFlags.contains(remote->localIndexPair().local().attribute())) -++size; -} -++remote; -} -interfaceInformation.reserve(process->first, size); -} - -// compare the local and remote indices and set up the types - -for(const_iterator process=remoteIndices.begin(); process != end; ++process) { -typedef typename RemoteIndices::RemoteIndexList::const_iterator RemoteIterator; -const RemoteIterator remoteEnd = send ? process->second.first->end() : -process->second.second->end(); -RemoteIterator remote = send ? process->second.first->begin() : process->second.second->begin(); - -while(remote!=remoteEnd) { -if( send ? destFlags.contains(remote->attribute()) : -sourceFlags.contains(remote->attribute())) { -// do we send the index? -if( send ? sourceFlags.contains(remote->localIndexPair().local().attribute()) : -destFlags.contains(remote->localIndexPair().local().attribute())) -interfaceInformation.add(process->first,remote->localIndexPair().local().local()); -} -++remote; -} -} -} - -inline MPI_Comm Interface::communicator() const -{ -return communicator_; - -} - - -inline const std::map<int,std::pair<InterfaceInformation,InterfaceInformation> >& Interface::interfaces() const -{ -return interfaces_; -} - -inline std::map<int,std::pair<InterfaceInformation,InterfaceInformation> >& Interface::interfaces() -{ -return interfaces_; -} - -inline void Interface::print() const -{ -typedef InformationMap::const_iterator const_iterator; -const const_iterator end=interfaces_.end(); -int rank; -MPI_Comm_rank(communicator(), &rank); - -for(const_iterator infoPair=interfaces_.begin(); infoPair!=end; ++infoPair) { -{ -std::cout<<rank<<": send for process "<<infoPair->first<<": "; -const InterfaceInformation& info(infoPair->second.first); -for(size_t i=0; i < info.size(); i++) -std::cout<<info[i]<<" "; -std::cout<<std::endl; -} { - -std::cout<<rank<<": receive for process "<<infoPair->first<<": "; -const InterfaceInformation& info(infoPair->second.second); -for(size_t i=0; i < info.size(); i++) -std::cout<<info[i]<<" "; -std::cout<<std::endl; -} - -} -} - -template<typename R, typename T1, typename T2> -inline void Interface::build(const R& remoteIndices, const T1& sourceFlags, -const T2& destFlags) -{ -communicator_=remoteIndices.communicator(); - -assert(interfaces_.empty()); - -// Build the send interface -InformationBuilder<true> sendInformation(interfaces_); -this->template buildInterface<R,T1,T2,InformationBuilder<true>,true>(remoteIndices, sourceFlags, -destFlags, sendInformation); - -// Build the receive interface -InformationBuilder<false> recvInformation(interfaces_); -this->template buildInterface<R,T1,T2,InformationBuilder<false>,false>(remoteIndices,sourceFlags, -destFlags, recvInformation); -strip(); -} -inline void Interface::strip() -{ -typedef InformationMap::iterator const_iterator; -for(const_iterator interfacePair = interfaces_.begin(); interfacePair != interfaces_.end();) -if(interfacePair->second.first.size()==0 && interfacePair->second.second.size()==0) { -interfacePair->second.first.free(); -interfacePair->second.second.free(); -const_iterator toerase=interfacePair++; -interfaces_.erase(toerase); -}else -++interfacePair; -} - -inline void Interface::free() -{ -typedef InformationMap::iterator iterator; -typedef InformationMap::const_iterator const_iterator; -const const_iterator end = interfaces_.end(); -for(iterator interfacePair = interfaces_.begin(); interfacePair != end; ++interfacePair) { -interfacePair->second.first.free(); -interfacePair->second.second.free(); -} -interfaces_.clear(); -} - -inline Interface::~Interface() -{ -free(); -} -/** @} */ - -inline std::ostream& operator<<(std::ostream& os, const Interface& interface) -{ -typedef Interface::InformationMap InfoMap; -typedef InfoMap::const_iterator Iter; -for(Iter i=interface.interfaces().begin(), end = interface.interfaces().end(); -i!=end; ++i) -{ -os<<i->first<<": [ source=["; -for(std::size_t j=0; j < i->second.first.size(); ++j) -os<<i->second.first[j]<<" "; -os<<"] size="<<i->second.first.size()<<", target=["; -for(std::size_t j=0; j < i->second.second.size(); ++j) -os<<i->second.second[j]<<" "; -os<<"] size="<<i->second.second.size()<<"\n"; -} -return os; -} + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for building the communication + * interface between remote indices. + * @author Markus Blatt + */ + + /** @} */ + /** + * @brief Base class of all classes representing a communication + * interface. + * + * It provides an generic utility method for building the interface + * for a set of remote indices. + */ + class InterfaceBuilder + { + public: + class RemoteIndicesStateError : public InvalidStateException + {}; + + virtual ~InterfaceBuilder() + {} + + protected: + /** + * @brief Not for public use. + */ + InterfaceBuilder() + {} + + /** + * @brief Builds the interface between remote processes. + * + * + * The types T1 and T2 are classes representing a set of + * enumeration values of type InterfaceBuilder::Attribute. They have to provide + * a (static) method + * \code + * bool contains(Attribute flag) const; + * \endcode + * for checking whether the set contains a specific flag. + * This functionality is for example provided the classes + * EnumItem, EnumRange and Combine. + * + * If the template parameter send is true the sending side of + * the interface will be built, otherwise the information for + * receiving will be built. + * + * + * If the template parameter send is true we create interface for sending + * in a forward communication. + * + * @param remoteIndices The indices known to remote processes. + * @param sourceFlags The set of flags marking source indices. + * @param destFlags The setof flags markig destination indices. + * @param functor A functor for callbacks. It should provide the + * following methods: + * \code + * // Reserve memory for the interface to processor proc. The interface + * // has to hold size entries + * void reserve(int proc, int size); + * + * // Add an entry to the interface + * // We will send/receive size entries at index local to process proc + * void add(int proc, int local); + * \endcode + */ + template<class R, class T1, class T2, class Op, bool send> + void buildInterface (const R& remoteIndices, + const T1& sourceFlags, const T2& destFlags, + Op& functor) const; + }; + + /** + * @brief Information describing an interface. + * + * This class is used for temporary gathering information + * about the interface needed for actually building it. It + * is used be class Interface as functor for InterfaceBuilder::build. + */ + class InterfaceInformation + { + + public: + + /** + * @brief Get the number of entries in the interface. + */ + size_t size() const + { + return size_; + } + /** + * @brief Get the local index for an entry. + * @param i The index of the entry. + */ + std::size_t& operator[](size_t i) + { + assert(i<size_); + return indices_[i]; + } + /** + * @brief Get the local index for an entry. + * @param i The index of the entry. + */ + std::size_t operator[](size_t i) const + { + assert(i<size_); + return indices_[i]; + } + /** + * @brief Reserve space for a number of entries. + * @param size The maximum number of entries to hold. + */ + void reserve(size_t size) + { + indices_ = new std::size_t[size]; + maxSize_ = size; + + } + /** + * brief Frees allocated memory. + */ + void free() + { + if(indices_) + delete[] indices_; + maxSize_ = 0; + size_=0; + indices_=0; + } + /** + * @brief Add a new index to the interface. + */ + void add(std::size_t index) + { + assert(size_<maxSize_); + indices_[size_++]=index; + } + + InterfaceInformation() + : size_(0), maxSize_(0), indices_(0) + {} + + virtual ~InterfaceInformation() + {} + + bool operator!=(const InterfaceInformation& o) const + { + return !operator==(o); + } + + bool operator==(const InterfaceInformation& o) const + { + if(size_!=o.size_) + return false; + for(std::size_t i=0; i< size_; ++i) + if(indices_[i]!=o.indices_[i]) + return false; + return true; + } + + private: + /** + * @brief The number of entries in the interface. + */ + size_t size_; + /** + * @brief The maximum number of indices we can hold. + */ + size_t maxSize_; + /** + * @brief The local indices of the interface. + */ + std::size_t* indices_; + }; + + /** @addtogroup Common_Parallel + * + * @{ + */ + + /** + * @brief Communication interface between remote and local indices. + * + * Describes the communication interface between + * indices on the local process and those on remote processes. + */ + class Interface : public InterfaceBuilder + { + + public: + /** + * @brief The type of the map form process number to InterfaceInformation for + * sending and receiving to and from it. + */ + typedef std::map<int,std::pair<InterfaceInformation,InterfaceInformation> > InformationMap; + + /** + * @brief Builds the interface. + * + * The types T1 and T2 are classes representing a set of + * enumeration values of type Interface::Attribute. They have to provide + * a (static) method + * \code + * bool contains(Attribute flag) const; + * \endcode + * for checking whether the set contains a specific flag. + * This functionality is for example provided the classes + * EnumItem, EnumRange and Combine. + * @param remoteIndices The indices known to remote processes. + * @param sourceFlags The set of flags marking indices we send from. + * @param destFlags The set of flags marking indices we receive for. + */ + template<typename R, typename T1, typename T2> + void build(const R& remoteIndices, const T1& sourceFlags, + const T2& destFlags); + + /** + * @brief Frees memory allocated during the build. + */ + void free(); + + /** + * @brief Get the MPI Communicator. + */ + MPI_Comm communicator() const; + + /** + * @brief Get information about the interfaces. + * + * @return Map of the interfaces. + * The key of the map is the process number and the value + * is the information pair (first the send and then the receive + * information). + */ + const InformationMap& interfaces() const; + + Interface(MPI_Comm comm) + : communicator_(comm), interfaces_() + {} + + Interface() + : communicator_(MPI_COMM_NULL), interfaces_() + {} + + /** + * @brief Print the interface to std::out for debugging. + */ + void print() const; + + bool operator!=(const Interface& o) const + { + return ! operator==(o); + } + + bool operator==(const Interface& o) const + { + if(communicator_!=o.communicator_) + return false; + if(interfaces_.size()!=o.interfaces_.size()) + return false; + typedef InformationMap::const_iterator MIter; + + for(MIter m=interfaces_.begin(), om=o.interfaces_.begin(); + m!=interfaces_.end(); ++m, ++om) + { + if(om->first!=m->first) + return false; + if(om->second.first!=om->second.first) + return false; + if(om->second.second!=om->second.second) + return false; + } + return true; + } + + /** + * @brief Destructor. + */ + virtual ~Interface(); + + void strip(); + protected: + + /** + * @brief Get information about the interfaces. + * + * @return Map of the interfaces. + * The key of the map is the process number and the value + * is the information pair (first the send and then the receive + * information). + */ + InformationMap& interfaces(); + + /** @brief The MPI communicator we use. */ + MPI_Comm communicator_; + + private: + /** + * @brief Information about the interfaces. + * + * The key of the map is the process number and the value + * is the information pair (first the send and then the receive + * information). + */ + InformationMap interfaces_; + + template<bool send> + class InformationBuilder + { + public: + InformationBuilder(InformationMap& interfaces) + : interfaces_(interfaces) + {} + + void reserve(int proc, int size) + { + if(send) + interfaces_[proc].first.reserve(size); + else + interfaces_[proc].second.reserve(size); + } + void add(int proc, std::size_t local) + { + if(send) { + interfaces_[proc].first.add(local); + }else{ + interfaces_[proc].second.add(local); + } + } + + private: + InformationMap& interfaces_; + }; + }; + + template<class R, class T1, class T2, class Op, bool send> + void InterfaceBuilder::buildInterface(const R& remoteIndices, const T1& sourceFlags, const T2& destFlags, Op& interfaceInformation) const + { + + if(!remoteIndices.isSynced()) + DUNE_THROW(RemoteIndicesStateError,"RemoteIndices is not in sync with the index set. Call RemoteIndices::rebuild first!"); + // Allocate the memory for the data type construction. + typedef R RemoteIndices; + typedef typename RemoteIndices::RemoteIndexMap::const_iterator const_iterator; + + const const_iterator end=remoteIndices.end(); + + int rank; + + MPI_Comm_rank(remoteIndices.communicator(), &rank); + + // Allocate memory for the type construction. + for(const_iterator process=remoteIndices.begin(); process != end; ++process) { + // Messure the number of indices send to the remote process first + int size=0; + typedef typename RemoteIndices::RemoteIndexList::const_iterator RemoteIterator; + const RemoteIterator remoteEnd = send ? process->second.first->end() : + process->second.second->end(); + RemoteIterator remote = send ? process->second.first->begin() : process->second.second->begin(); + + while(remote!=remoteEnd) { + if( send ? destFlags.contains(remote->attribute()) : + sourceFlags.contains(remote->attribute())) { + + // do we send the index? + if( send ? sourceFlags.contains(remote->localIndexPair().local().attribute()) : + destFlags.contains(remote->localIndexPair().local().attribute())) + ++size; + } + ++remote; + } + interfaceInformation.reserve(process->first, size); + } + + // compare the local and remote indices and set up the types + + for(const_iterator process=remoteIndices.begin(); process != end; ++process) { + typedef typename RemoteIndices::RemoteIndexList::const_iterator RemoteIterator; + const RemoteIterator remoteEnd = send ? process->second.first->end() : + process->second.second->end(); + RemoteIterator remote = send ? process->second.first->begin() : process->second.second->begin(); + + while(remote!=remoteEnd) { + if( send ? destFlags.contains(remote->attribute()) : + sourceFlags.contains(remote->attribute())) { + // do we send the index? + if( send ? sourceFlags.contains(remote->localIndexPair().local().attribute()) : + destFlags.contains(remote->localIndexPair().local().attribute())) + interfaceInformation.add(process->first,remote->localIndexPair().local().local()); + } + ++remote; + } + } + } + + inline MPI_Comm Interface::communicator() const + { + return communicator_; + + } + + + inline const std::map<int,std::pair<InterfaceInformation,InterfaceInformation> >& Interface::interfaces() const + { + return interfaces_; + } + + inline std::map<int,std::pair<InterfaceInformation,InterfaceInformation> >& Interface::interfaces() + { + return interfaces_; + } + + inline void Interface::print() const + { + typedef InformationMap::const_iterator const_iterator; + const const_iterator end=interfaces_.end(); + int rank; + MPI_Comm_rank(communicator(), &rank); + + for(const_iterator infoPair=interfaces_.begin(); infoPair!=end; ++infoPair) { + { + std::cout<<rank<<": send for process "<<infoPair->first<<": "; + const InterfaceInformation& info(infoPair->second.first); + for(size_t i=0; i < info.size(); i++) + std::cout<<info[i]<<" "; + std::cout<<std::endl; + } { + + std::cout<<rank<<": receive for process "<<infoPair->first<<": "; + const InterfaceInformation& info(infoPair->second.second); + for(size_t i=0; i < info.size(); i++) + std::cout<<info[i]<<" "; + std::cout<<std::endl; + } + + } + } + + template<typename R, typename T1, typename T2> + inline void Interface::build(const R& remoteIndices, const T1& sourceFlags, + const T2& destFlags) + { + communicator_=remoteIndices.communicator(); + + assert(interfaces_.empty()); + + // Build the send interface + InformationBuilder<true> sendInformation(interfaces_); + this->template buildInterface<R,T1,T2,InformationBuilder<true>,true>(remoteIndices, sourceFlags, + destFlags, sendInformation); + + // Build the receive interface + InformationBuilder<false> recvInformation(interfaces_); + this->template buildInterface<R,T1,T2,InformationBuilder<false>,false>(remoteIndices,sourceFlags, + destFlags, recvInformation); + strip(); + } + inline void Interface::strip() + { + typedef InformationMap::iterator const_iterator; + for(const_iterator interfacePair = interfaces_.begin(); interfacePair != interfaces_.end();) + if(interfacePair->second.first.size()==0 && interfacePair->second.second.size()==0) { + interfacePair->second.first.free(); + interfacePair->second.second.free(); + const_iterator toerase=interfacePair++; + interfaces_.erase(toerase); + }else + ++interfacePair; + } + + inline void Interface::free() + { + typedef InformationMap::iterator iterator; + typedef InformationMap::const_iterator const_iterator; + const const_iterator end = interfaces_.end(); + for(iterator interfacePair = interfaces_.begin(); interfacePair != end; ++interfacePair) { + interfacePair->second.first.free(); + interfacePair->second.second.free(); + } + interfaces_.clear(); + } + + inline Interface::~Interface() + { + free(); + } + /** @} */ + + inline std::ostream& operator<<(std::ostream& os, const Interface& interface) + { + typedef Interface::InformationMap InfoMap; + typedef InfoMap::const_iterator Iter; + for(Iter i=interface.interfaces().begin(), end = interface.interfaces().end(); + i!=end; ++i) + { + os<<i->first<<": [ source=["; + for(std::size_t j=0; j < i->second.first.size(); ++j) + os<<i->second.first[j]<<" "; + os<<"] size="<<i->second.first.size()<<", target=["; + for(std::size_t j=0; j < i->second.second.size(); ++j) + os<<i->second.second[j]<<" "; + os<<"] size="<<i->second.second.size()<<"\n"; + } + return os; + } } #endif // HAVE_MPI diff --git a/dune/common/parallel/localindex.hh b/dune/common/parallel/localindex.hh index 093a2f79c9076c5bb24b13ee7172df1d76c9620a..d7ebfdb54fda1c7fe3730a475cf4182c94d02fce 100644 --- a/dune/common/parallel/localindex.hh +++ b/dune/common/parallel/localindex.hh @@ -11,110 +11,110 @@ namespace Dune { -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Provides classes for use as the local index in ParallelIndexSet. -* @author Markus Blatt -*/ -/** -* @brief The states avaiable for the local indices. -* @see LocalIndex::state() -*/ -enum LocalIndexState {VALID, DELETED}; - - -/** -* @brief An index present on the local process. -*/ -class LocalIndex -{ -public: -/** -* @brief Constructor. -* known to other processes. -*/ -LocalIndex() : -localIndex_(0), state_(VALID){} - - -/** -* @brief Constructor. -* @param index The value of the index. -*/ -LocalIndex(std::size_t index) : -localIndex_(index), state_(VALID){} -/** -* @brief get the local index. -* @return The local index. -*/ -inline const std::size_t& local() const; - -/** -* @brief Convert to the local index represented by an int. -*/ -inline operator std::size_t() const; - -/** -* @brief Assign a new local index. -* -* @param index The new local index. -*/ -inline LocalIndex& operator=(std::size_t index); - -/** -* @brief Get the state. -* @return The state. -*/ -inline LocalIndexState state() const; - -/** -* @brief Set the state. -* @param state The state to set. -*/ -inline void setState(LocalIndexState state); - -private: -/** @brief The local index. */ -std::size_t localIndex_; - -/** -* @brief The state of the index. -* -* Has to be one of LocalIndexState! -* @see LocalIndexState. -*/ -char state_; - -}; - - - -inline const std::size_t& LocalIndex::local() const { -return localIndex_; -} - -inline LocalIndex::operator std::size_t() const { -return localIndex_; -} - -inline LocalIndex& LocalIndex::operator=(std::size_t index){ -localIndex_ = index; -return *this; -} - -inline LocalIndexState LocalIndex::state() const { -return static_cast<LocalIndexState>(state_); -} - -inline void LocalIndex::setState(LocalIndexState state){ -state_ = static_cast<char>(state); -} - -/** @} */ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for use as the local index in ParallelIndexSet. + * @author Markus Blatt + */ + /** + * @brief The states avaiable for the local indices. + * @see LocalIndex::state() + */ + enum LocalIndexState {VALID, DELETED}; + + + /** + * @brief An index present on the local process. + */ + class LocalIndex + { + public: + /** + * @brief Constructor. + * known to other processes. + */ + LocalIndex() : + localIndex_(0), state_(VALID){} + + + /** + * @brief Constructor. + * @param index The value of the index. + */ + LocalIndex(std::size_t index) : + localIndex_(index), state_(VALID){} + /** + * @brief get the local index. + * @return The local index. + */ + inline const std::size_t& local() const; + + /** + * @brief Convert to the local index represented by an int. + */ + inline operator std::size_t() const; + + /** + * @brief Assign a new local index. + * + * @param index The new local index. + */ + inline LocalIndex& operator=(std::size_t index); + + /** + * @brief Get the state. + * @return The state. + */ + inline LocalIndexState state() const; + + /** + * @brief Set the state. + * @param state The state to set. + */ + inline void setState(LocalIndexState state); + + private: + /** @brief The local index. */ + std::size_t localIndex_; + + /** + * @brief The state of the index. + * + * Has to be one of LocalIndexState! + * @see LocalIndexState. + */ + char state_; + + }; + + + + inline const std::size_t& LocalIndex::local() const { + return localIndex_; + } + + inline LocalIndex::operator std::size_t() const { + return localIndex_; + } + + inline LocalIndex& LocalIndex::operator=(std::size_t index){ + localIndex_ = index; + return *this; + } + + inline LocalIndexState LocalIndex::state() const { + return static_cast<LocalIndexState>(state_); + } + + inline void LocalIndex::setState(LocalIndexState state){ + state_ = static_cast<char>(state); + } + + /** @} */ } // namespace Dune diff --git a/dune/common/parallel/mpicommunication.hh b/dune/common/parallel/mpicommunication.hh index 769683288f2c968e232bf96198ac668dc3f43da4..fc92b8f8db03b6185d8324ea1dde157ef05947a9 100644 --- a/dune/common/parallel/mpicommunication.hh +++ b/dune/common/parallel/mpicommunication.hh @@ -5,12 +5,12 @@ #include <dune/internal/dune-common.hh> /*! -\file -\brief Implements an utility class that provides -MPI's collective communication methods. + \file + \brief Implements an utility class that provides + MPI's collective communication methods. -\ingroup ParallelCommunication -*/ + \ingroup ParallelCommunication + */ #if HAVE_MPI @@ -30,438 +30,438 @@ MPI's collective communication methods. namespace Dune { -//======================================================= -// use singleton pattern and template specialization to -// generate MPI operations -//======================================================= - -template<typename Type, typename BinaryFunction, typename Enable=void> -class Generic_MPI_Op -{ - -public: -static MPI_Op get () -{ -if (!op) -{ -op = std::make_unique<MPI_Op>(); -// The following line leaks an MPI operation object, because the corresponding -//`MPI_Op_free` is never called. It is never called because there is no easy -// way to call it at the right moment: right before the call to MPI_Finalize. -// See https://gitlab.dune-project.org/core/dune-istl/issues/80 -MPI_Op_create((void (*)(void*, void*, int*, MPI_Datatype*))&operation,true,op.get()); -} -return *op; -} -private: -static void operation (Type *in, Type *inout, int *len, MPI_Datatype*) -{ -BinaryFunction func; - -for (int i=0; i< *len; ++i, ++in, ++inout) { -Type temp; -temp = func(*in, *inout); -*inout = temp; -} -} -Generic_MPI_Op () {} -Generic_MPI_Op (const Generic_MPI_Op& ) {} -static std::unique_ptr<MPI_Op> op; -}; - - -template<typename Type, typename BinaryFunction, typename Enable> -std::unique_ptr<MPI_Op> Generic_MPI_Op<Type,BinaryFunction, Enable>::op; + //======================================================= + // use singleton pattern and template specialization to + // generate MPI operations + //======================================================= + + template<typename Type, typename BinaryFunction, typename Enable=void> + class Generic_MPI_Op + { + + public: + static MPI_Op get () + { + if (!op) + { + op = std::make_unique<MPI_Op>(); + // The following line leaks an MPI operation object, because the corresponding + //`MPI_Op_free` is never called. It is never called because there is no easy + // way to call it at the right moment: right before the call to MPI_Finalize. + // See https://gitlab.dune-project.org/core/dune-istl/issues/80 + MPI_Op_create((void (*)(void*, void*, int*, MPI_Datatype*))&operation,true,op.get()); + } + return *op; + } + private: + static void operation (Type *in, Type *inout, int *len, MPI_Datatype*) + { + BinaryFunction func; + + for (int i=0; i< *len; ++i, ++in, ++inout) { + Type temp; + temp = func(*in, *inout); + *inout = temp; + } + } + Generic_MPI_Op () {} + Generic_MPI_Op (const Generic_MPI_Op& ) {} + static std::unique_ptr<MPI_Op> op; + }; + + + template<typename Type, typename BinaryFunction, typename Enable> + std::unique_ptr<MPI_Op> Generic_MPI_Op<Type,BinaryFunction, Enable>::op; #define ComposeMPIOp(func,op) \ -template<class T, class S> \ -class Generic_MPI_Op<T, func<S>, std::enable_if_t<MPITraits<S>::is_intrinsic> >{ \ -public: \ -static MPI_Op get(){ \ -return op; \ -} \ -private: \ -Generic_MPI_Op () {} \ -Generic_MPI_Op (const Generic_MPI_Op & ) {} \ -} - - -ComposeMPIOp(std::plus, MPI_SUM); -ComposeMPIOp(std::multiplies, MPI_PROD); -ComposeMPIOp(Min, MPI_MIN); -ComposeMPIOp(Max, MPI_MAX); + template<class T, class S> \ + class Generic_MPI_Op<T, func<S>, std::enable_if_t<MPITraits<S>::is_intrinsic> >{ \ + public: \ + static MPI_Op get(){ \ + return op; \ + } \ + private: \ + Generic_MPI_Op () {} \ + Generic_MPI_Op (const Generic_MPI_Op & ) {} \ + } + + + ComposeMPIOp(std::plus, MPI_SUM); + ComposeMPIOp(std::multiplies, MPI_PROD); + ComposeMPIOp(Min, MPI_MIN); + ComposeMPIOp(Max, MPI_MAX); #undef ComposeMPIOp -//======================================================= -// use singleton pattern and template specialization to -// generate MPI operations -//======================================================= - -/*! \brief Specialization of Communication for MPI -\ingroup ParallelCommunication -*/ -template<> -class Communication<MPI_Comm> -{ -public: -//! Instantiation using a MPI communicator -Communication (const MPI_Comm& c = MPI_COMM_WORLD) -: communicator(c) -{ -if(communicator!=MPI_COMM_NULL) { -int initialized = 0; -MPI_Initialized(&initialized); -if (!initialized) -DUNE_THROW(ParallelError,"You must call MPIHelper::instance(argc,argv) in your main() function before using the MPI Communication!"); -MPI_Comm_rank(communicator,&me); -MPI_Comm_size(communicator,&procs); -}else{ -procs=0; -me=-1; -} -} - -//! @copydoc Communication::rank -int rank () const -{ -return me; -} - -//! @copydoc Communication::size -int size () const -{ -return procs; -} - -//! @copydoc Communication::send -template<class T> -int send(const T& data, int dest_rank, int tag) const -{ -auto mpi_data = getMPIData(data); -return MPI_Send(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), -dest_rank, tag, communicator); -} - -//! @copydoc Communication::isend -template<class T> -MPIFuture<const T> isend(const T&& data, int dest_rank, int tag) const -{ -MPIFuture<const T> future(std::forward<const T>(data)); -auto mpidata = future.get_mpidata(); -MPI_Isend(mpidata.ptr(), mpidata.size(), mpidata.type(), -dest_rank, tag, communicator, &future.req_); -return future; -} - -//! @copydoc Communication::recv -template<class T> -T recv(T&& data, int source_rank, int tag, MPI_Status* status = MPI_STATUS_IGNORE) const -{ -T lvalue_data(std::forward<T>(data)); -auto mpi_data = getMPIData(lvalue_data); -MPI_Recv(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), -source_rank, tag, communicator, status); -return lvalue_data; -} - -//! @copydoc Communication::irecv -template<class T> -MPIFuture<T> irecv(T&& data, int source_rank, int tag) const -{ -MPIFuture<T> future(std::forward<T>(data)); -auto mpidata = future.get_mpidata(); -MPI_Irecv(mpidata.ptr(), mpidata.size(), mpidata.type(), -source_rank, tag, communicator, &future.req_); -return future; -} - -template<class T> -T rrecv(T&& data, int source_rank, int tag, MPI_Status* status = MPI_STATUS_IGNORE) const -{ -MPI_Status _status; -MPI_Message _message; -T lvalue_data(std::forward<T>(data)); -auto mpi_data = getMPIData(lvalue_data); -static_assert(!mpi_data.static_size, "rrecv work only for non-static-sized types."); -if(status == MPI_STATUS_IGNORE) -status = &_status; -MPI_Mprobe(source_rank, tag, communicator, &_message, status); -int size; -MPI_Get_count(status, mpi_data.type(), &size); -mpi_data.resize(size); -MPI_Mrecv(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), &_message, status); -return lvalue_data; -} - -//! @copydoc Communication::sum -template<typename T> -T sum (const T& in) const -{ -T out; -allreduce<std::plus<T> >(&in,&out,1); -return out; -} - -//! @copydoc Communication::sum -template<typename T> -int sum (T* inout, int len) const -{ -return allreduce<std::plus<T> >(inout,len); -} - -//! @copydoc Communication::prod -template<typename T> -T prod (const T& in) const -{ -T out; -allreduce<std::multiplies<T> >(&in,&out,1); -return out; -} - -//! @copydoc Communication::prod -template<typename T> -int prod (T* inout, int len) const -{ -return allreduce<std::multiplies<T> >(inout,len); -} - -//! @copydoc Communication::min -template<typename T> -T min (const T& in) const -{ -T out; -allreduce<Min<T> >(&in,&out,1); -return out; -} - -//! @copydoc Communication::min -template<typename T> -int min (T* inout, int len) const -{ -return allreduce<Min<T> >(inout,len); -} - - -//! @copydoc Communication::max -template<typename T> -T max (const T& in) const -{ -T out; -allreduce<Max<T> >(&in,&out,1); -return out; -} - -//! @copydoc Communication::max -template<typename T> -int max (T* inout, int len) const -{ -return allreduce<Max<T> >(inout,len); -} - -//! @copydoc Communication::barrier -int barrier () const -{ -return MPI_Barrier(communicator); -} - -//! @copydoc Communication::ibarrier -MPIFuture<void> ibarrier () const -{ -MPIFuture<void> future(true); // make a valid MPIFuture<void> -MPI_Ibarrier(communicator, &future.req_); -return future; -} - - -//! @copydoc Communication::broadcast -template<typename T> -int broadcast (T* inout, int len, int root) const -{ -return MPI_Bcast(inout,len,MPITraits<T>::getType(),root,communicator); -} - -//! @copydoc Communication::ibroadcast -template<class T> -MPIFuture<T> ibroadcast(T&& data, int root) const{ -MPIFuture<T> future(std::forward<T>(data)); -auto mpidata = future.get_mpidata(); -MPI_Ibcast(mpidata.ptr(), -mpidata.size(), -mpidata.type(), -root, -communicator, -&future.req_); -return future; -} - -//! @copydoc Communication::gather() -//! @note out must have space for P*len elements -template<typename T> -int gather (const T* in, T* out, int len, int root) const -{ -return MPI_Gather(const_cast<T*>(in),len,MPITraits<T>::getType(), -out,len,MPITraits<T>::getType(), -root,communicator); -} - -//! @copydoc Communication::igather -template<class TIN, class TOUT = std::vector<TIN>> -MPIFuture<TOUT, TIN> igather(TIN&& data_in, TOUT&& data_out, int root) const{ -MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); -auto mpidata_in = future.get_send_mpidata(); -auto mpidata_out = future.get_mpidata(); -assert(root != me || mpidata_in.size()*procs <= mpidata_out.size()); -int outlen = (me==root) * mpidata_in.size(); -MPI_Igather(mpidata_in.ptr(), mpidata_in.size(), mpidata_in.type(), -mpidata_out.ptr(), outlen, mpidata_out.type(), -root, communicator, &future.req_); -return future; -} - -//! @copydoc Communication::gatherv() -template<typename T> -int gatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ, int root) const -{ -return MPI_Gatherv(const_cast<T*>(in),sendlen,MPITraits<T>::getType(), -out,recvlen,displ,MPITraits<T>::getType(), -root,communicator); -} - -//! @copydoc Communication::scatter() -//! @note out must have space for P*len elements -template<typename T> -int scatter (const T* send, T* recv, int len, int root) const -{ -return MPI_Scatter(const_cast<T*>(send),len,MPITraits<T>::getType(), -recv,len,MPITraits<T>::getType(), -root,communicator); -} - -//! @copydoc Communication::iscatter -template<class TIN, class TOUT = TIN> -MPIFuture<TOUT, TIN> iscatter(TIN&& data_in, TOUT&& data_out, int root) const -{ -MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); -auto mpidata_in = future.get_send_mpidata(); -auto mpidata_out = future.get_mpidata(); -int inlen = (me==root) * mpidata_in.size()/procs; -MPI_Iscatter(mpidata_in.ptr(), inlen, mpidata_in.type(), -mpidata_out.ptr(), mpidata_out.size(), mpidata_out.type(), -root, communicator, &future.req_); -return future; -} - -//! @copydoc Communication::scatterv() -template<typename T> -int scatterv (const T* send, int* sendlen, int* displ, T* recv, int recvlen, int root) const -{ -return MPI_Scatterv(const_cast<T*>(send),sendlen,displ,MPITraits<T>::getType(), -recv,recvlen,MPITraits<T>::getType(), -root,communicator); -} - - -operator MPI_Comm () const -{ -return communicator; -} - -//! @copydoc Communication::allgather() -template<typename T, typename T1> -int allgather(const T* sbuf, int count, T1* rbuf) const -{ -return MPI_Allgather(const_cast<T*>(sbuf), count, MPITraits<T>::getType(), -rbuf, count, MPITraits<T1>::getType(), -communicator); -} - -//! @copydoc Communication::iallgather -template<class TIN, class TOUT = TIN> -MPIFuture<TOUT, TIN> iallgather(TIN&& data_in, TOUT&& data_out) const -{ -MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); -auto mpidata_in = future.get_send_mpidata(); -auto mpidata_out = future.get_mpidata(); -assert(mpidata_in.size()*procs <= mpidata_out.size()); -int outlen = mpidata_in.size(); -MPI_Iallgather(mpidata_in.ptr(), mpidata_in.size(), mpidata_in.type(), -mpidata_out.ptr(), outlen, mpidata_out.type(), -communicator, &future.req_); -return future; -} - -//! @copydoc Communication::allgatherv() -template<typename T> -int allgatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ) const -{ -return MPI_Allgatherv(const_cast<T*>(in),sendlen,MPITraits<T>::getType(), -out,recvlen,displ,MPITraits<T>::getType(), -communicator); -} - -//! @copydoc Communication::allreduce(Type* inout,int len) const -template<typename BinaryFunction, typename Type> -int allreduce(Type* inout, int len) const -{ -Type* out = new Type[len]; -int ret = allreduce<BinaryFunction>(inout,out,len); -std::copy(out, out+len, inout); -delete[] out; -return ret; -} - -template<typename BinaryFunction, typename Type> -Type allreduce(Type&& in) const{ -Type lvalue_data = std::forward<Type>(in); -auto data = getMPIData(lvalue_data); -MPI_Allreduce(MPI_IN_PLACE, data.ptr(), data.size(), data.type(), -(Generic_MPI_Op<Type, BinaryFunction>::get()), -communicator); -return lvalue_data; -} - -//! @copydoc Communication::iallreduce -template<class BinaryFunction, class TIN, class TOUT = TIN> -MPIFuture<TOUT, TIN> iallreduce(TIN&& data_in, TOUT&& data_out) const { -MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); -auto mpidata_in = future.get_send_mpidata(); -auto mpidata_out = future.get_mpidata(); -assert(mpidata_out.size() == mpidata_in.size()); -assert(mpidata_out.type() == mpidata_in.type()); -MPI_Iallreduce(mpidata_in.ptr(), mpidata_out.ptr(), -mpidata_out.size(), mpidata_out.type(), -(Generic_MPI_Op<TIN, BinaryFunction>::get()), -communicator, &future.req_); -return future; -} - -//! @copydoc Communication::iallreduce -template<class BinaryFunction, class T> -MPIFuture<T> iallreduce(T&& data) const{ -MPIFuture<T> future(std::forward<T>(data)); -auto mpidata = future.get_mpidata(); -MPI_Iallreduce(MPI_IN_PLACE, mpidata.ptr(), -mpidata.size(), mpidata.type(), -(Generic_MPI_Op<T, BinaryFunction>::get()), -communicator, &future.req_); -return future; -} - -//! @copydoc Communication::allreduce(Type* in,Type* out,int len) const -template<typename BinaryFunction, typename Type> -int allreduce(const Type* in, Type* out, int len) const -{ -return MPI_Allreduce(const_cast<Type*>(in), out, len, MPITraits<Type>::getType(), -(Generic_MPI_Op<Type, BinaryFunction>::get()),communicator); -} - -private: -MPI_Comm communicator; -int me; -int procs; -}; + //======================================================= + // use singleton pattern and template specialization to + // generate MPI operations + //======================================================= + + /*! \brief Specialization of Communication for MPI + \ingroup ParallelCommunication + */ + template<> + class Communication<MPI_Comm> + { + public: + //! Instantiation using a MPI communicator + Communication (const MPI_Comm& c = MPI_COMM_WORLD) + : communicator(c) + { + if(communicator!=MPI_COMM_NULL) { + int initialized = 0; + MPI_Initialized(&initialized); + if (!initialized) + DUNE_THROW(ParallelError,"You must call MPIHelper::instance(argc,argv) in your main() function before using the MPI Communication!"); + MPI_Comm_rank(communicator,&me); + MPI_Comm_size(communicator,&procs); + }else{ + procs=0; + me=-1; + } + } + + //! @copydoc Communication::rank + int rank () const + { + return me; + } + + //! @copydoc Communication::size + int size () const + { + return procs; + } + + //! @copydoc Communication::send + template<class T> + int send(const T& data, int dest_rank, int tag) const + { + auto mpi_data = getMPIData(data); + return MPI_Send(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), + dest_rank, tag, communicator); + } + + //! @copydoc Communication::isend + template<class T> + MPIFuture<const T> isend(const T&& data, int dest_rank, int tag) const + { + MPIFuture<const T> future(std::forward<const T>(data)); + auto mpidata = future.get_mpidata(); + MPI_Isend(mpidata.ptr(), mpidata.size(), mpidata.type(), + dest_rank, tag, communicator, &future.req_); + return future; + } + + //! @copydoc Communication::recv + template<class T> + T recv(T&& data, int source_rank, int tag, MPI_Status* status = MPI_STATUS_IGNORE) const + { + T lvalue_data(std::forward<T>(data)); + auto mpi_data = getMPIData(lvalue_data); + MPI_Recv(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), + source_rank, tag, communicator, status); + return lvalue_data; + } + + //! @copydoc Communication::irecv + template<class T> + MPIFuture<T> irecv(T&& data, int source_rank, int tag) const + { + MPIFuture<T> future(std::forward<T>(data)); + auto mpidata = future.get_mpidata(); + MPI_Irecv(mpidata.ptr(), mpidata.size(), mpidata.type(), + source_rank, tag, communicator, &future.req_); + return future; + } + + template<class T> + T rrecv(T&& data, int source_rank, int tag, MPI_Status* status = MPI_STATUS_IGNORE) const + { + MPI_Status _status; + MPI_Message _message; + T lvalue_data(std::forward<T>(data)); + auto mpi_data = getMPIData(lvalue_data); + static_assert(!mpi_data.static_size, "rrecv work only for non-static-sized types."); + if(status == MPI_STATUS_IGNORE) + status = &_status; + MPI_Mprobe(source_rank, tag, communicator, &_message, status); + int size; + MPI_Get_count(status, mpi_data.type(), &size); + mpi_data.resize(size); + MPI_Mrecv(mpi_data.ptr(), mpi_data.size(), mpi_data.type(), &_message, status); + return lvalue_data; + } + + //! @copydoc Communication::sum + template<typename T> + T sum (const T& in) const + { + T out; + allreduce<std::plus<T> >(&in,&out,1); + return out; + } + + //! @copydoc Communication::sum + template<typename T> + int sum (T* inout, int len) const + { + return allreduce<std::plus<T> >(inout,len); + } + + //! @copydoc Communication::prod + template<typename T> + T prod (const T& in) const + { + T out; + allreduce<std::multiplies<T> >(&in,&out,1); + return out; + } + + //! @copydoc Communication::prod + template<typename T> + int prod (T* inout, int len) const + { + return allreduce<std::multiplies<T> >(inout,len); + } + + //! @copydoc Communication::min + template<typename T> + T min (const T& in) const + { + T out; + allreduce<Min<T> >(&in,&out,1); + return out; + } + + //! @copydoc Communication::min + template<typename T> + int min (T* inout, int len) const + { + return allreduce<Min<T> >(inout,len); + } + + + //! @copydoc Communication::max + template<typename T> + T max (const T& in) const + { + T out; + allreduce<Max<T> >(&in,&out,1); + return out; + } + + //! @copydoc Communication::max + template<typename T> + int max (T* inout, int len) const + { + return allreduce<Max<T> >(inout,len); + } + + //! @copydoc Communication::barrier + int barrier () const + { + return MPI_Barrier(communicator); + } + + //! @copydoc Communication::ibarrier + MPIFuture<void> ibarrier () const + { + MPIFuture<void> future(true); // make a valid MPIFuture<void> + MPI_Ibarrier(communicator, &future.req_); + return future; + } + + + //! @copydoc Communication::broadcast + template<typename T> + int broadcast (T* inout, int len, int root) const + { + return MPI_Bcast(inout,len,MPITraits<T>::getType(),root,communicator); + } + + //! @copydoc Communication::ibroadcast + template<class T> + MPIFuture<T> ibroadcast(T&& data, int root) const{ + MPIFuture<T> future(std::forward<T>(data)); + auto mpidata = future.get_mpidata(); + MPI_Ibcast(mpidata.ptr(), + mpidata.size(), + mpidata.type(), + root, + communicator, + &future.req_); + return future; + } + + //! @copydoc Communication::gather() + //! @note out must have space for P*len elements + template<typename T> + int gather (const T* in, T* out, int len, int root) const + { + return MPI_Gather(const_cast<T*>(in),len,MPITraits<T>::getType(), + out,len,MPITraits<T>::getType(), + root,communicator); + } + + //! @copydoc Communication::igather + template<class TIN, class TOUT = std::vector<TIN>> + MPIFuture<TOUT, TIN> igather(TIN&& data_in, TOUT&& data_out, int root) const{ + MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + assert(root != me || mpidata_in.size()*procs <= mpidata_out.size()); + int outlen = (me==root) * mpidata_in.size(); + MPI_Igather(mpidata_in.ptr(), mpidata_in.size(), mpidata_in.type(), + mpidata_out.ptr(), outlen, mpidata_out.type(), + root, communicator, &future.req_); + return future; + } + + //! @copydoc Communication::gatherv() + template<typename T> + int gatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ, int root) const + { + return MPI_Gatherv(const_cast<T*>(in),sendlen,MPITraits<T>::getType(), + out,recvlen,displ,MPITraits<T>::getType(), + root,communicator); + } + + //! @copydoc Communication::scatter() + //! @note out must have space for P*len elements + template<typename T> + int scatter (const T* send, T* recv, int len, int root) const + { + return MPI_Scatter(const_cast<T*>(send),len,MPITraits<T>::getType(), + recv,len,MPITraits<T>::getType(), + root,communicator); + } + + //! @copydoc Communication::iscatter + template<class TIN, class TOUT = TIN> + MPIFuture<TOUT, TIN> iscatter(TIN&& data_in, TOUT&& data_out, int root) const + { + MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + int inlen = (me==root) * mpidata_in.size()/procs; + MPI_Iscatter(mpidata_in.ptr(), inlen, mpidata_in.type(), + mpidata_out.ptr(), mpidata_out.size(), mpidata_out.type(), + root, communicator, &future.req_); + return future; + } + + //! @copydoc Communication::scatterv() + template<typename T> + int scatterv (const T* send, int* sendlen, int* displ, T* recv, int recvlen, int root) const + { + return MPI_Scatterv(const_cast<T*>(send),sendlen,displ,MPITraits<T>::getType(), + recv,recvlen,MPITraits<T>::getType(), + root,communicator); + } + + + operator MPI_Comm () const + { + return communicator; + } + + //! @copydoc Communication::allgather() + template<typename T, typename T1> + int allgather(const T* sbuf, int count, T1* rbuf) const + { + return MPI_Allgather(const_cast<T*>(sbuf), count, MPITraits<T>::getType(), + rbuf, count, MPITraits<T1>::getType(), + communicator); + } + + //! @copydoc Communication::iallgather + template<class TIN, class TOUT = TIN> + MPIFuture<TOUT, TIN> iallgather(TIN&& data_in, TOUT&& data_out) const + { + MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + assert(mpidata_in.size()*procs <= mpidata_out.size()); + int outlen = mpidata_in.size(); + MPI_Iallgather(mpidata_in.ptr(), mpidata_in.size(), mpidata_in.type(), + mpidata_out.ptr(), outlen, mpidata_out.type(), + communicator, &future.req_); + return future; + } + + //! @copydoc Communication::allgatherv() + template<typename T> + int allgatherv (const T* in, int sendlen, T* out, int* recvlen, int* displ) const + { + return MPI_Allgatherv(const_cast<T*>(in),sendlen,MPITraits<T>::getType(), + out,recvlen,displ,MPITraits<T>::getType(), + communicator); + } + + //! @copydoc Communication::allreduce(Type* inout,int len) const + template<typename BinaryFunction, typename Type> + int allreduce(Type* inout, int len) const + { + Type* out = new Type[len]; + int ret = allreduce<BinaryFunction>(inout,out,len); + std::copy(out, out+len, inout); + delete[] out; + return ret; + } + + template<typename BinaryFunction, typename Type> + Type allreduce(Type&& in) const{ + Type lvalue_data = std::forward<Type>(in); + auto data = getMPIData(lvalue_data); + MPI_Allreduce(MPI_IN_PLACE, data.ptr(), data.size(), data.type(), + (Generic_MPI_Op<Type, BinaryFunction>::get()), + communicator); + return lvalue_data; + } + + //! @copydoc Communication::iallreduce + template<class BinaryFunction, class TIN, class TOUT = TIN> + MPIFuture<TOUT, TIN> iallreduce(TIN&& data_in, TOUT&& data_out) const { + MPIFuture<TOUT, TIN> future(std::forward<TOUT>(data_out), std::forward<TIN>(data_in)); + auto mpidata_in = future.get_send_mpidata(); + auto mpidata_out = future.get_mpidata(); + assert(mpidata_out.size() == mpidata_in.size()); + assert(mpidata_out.type() == mpidata_in.type()); + MPI_Iallreduce(mpidata_in.ptr(), mpidata_out.ptr(), + mpidata_out.size(), mpidata_out.type(), + (Generic_MPI_Op<TIN, BinaryFunction>::get()), + communicator, &future.req_); + return future; + } + + //! @copydoc Communication::iallreduce + template<class BinaryFunction, class T> + MPIFuture<T> iallreduce(T&& data) const{ + MPIFuture<T> future(std::forward<T>(data)); + auto mpidata = future.get_mpidata(); + MPI_Iallreduce(MPI_IN_PLACE, mpidata.ptr(), + mpidata.size(), mpidata.type(), + (Generic_MPI_Op<T, BinaryFunction>::get()), + communicator, &future.req_); + return future; + } + + //! @copydoc Communication::allreduce(Type* in,Type* out,int len) const + template<typename BinaryFunction, typename Type> + int allreduce(const Type* in, Type* out, int len) const + { + return MPI_Allreduce(const_cast<Type*>(in), out, len, MPITraits<Type>::getType(), + (Generic_MPI_Op<Type, BinaryFunction>::get()),communicator); + } + + private: + MPI_Comm communicator; + int me; + int procs; + }; } // namespace dune #endif // HAVE_MPI diff --git a/dune/common/parallel/mpidata.hh b/dune/common/parallel/mpidata.hh index 8c2b92053a539289866323eb3549438cf68eebcd..caf80be829ebe9705f1c11de67cd3484bdf02461 100644 --- a/dune/common/parallel/mpidata.hh +++ b/dune/common/parallel/mpidata.hh @@ -15,125 +15,125 @@ #include <dune/common/parallel/mpitraits.hh> /** @addtogroup ParallelCommunication -* -* @{ -*/ + * + * @{ + */ /** -* @file -* -* @brief Interface class to translate objects to a MPI_Datatype, void* -* and size used for MPI calls. -* -* Furthermore it can be used to resize the object -* if possible. This makes it possible to receive a message with variable -* size. See `Communication::rrecv`. -* -* To 'register' a new dynamic type for MPI communication specialize `MPIData` or -* overload `getMPIData`. -* -*/ + * @file + * + * @brief Interface class to translate objects to a MPI_Datatype, void* + * and size used for MPI calls. + * + * Furthermore it can be used to resize the object + * if possible. This makes it possible to receive a message with variable + * size. See `Communication::rrecv`. + * + * To 'register' a new dynamic type for MPI communication specialize `MPIData` or + * overload `getMPIData`. + * + */ namespace Dune{ -template<class, class = void> -struct MPIData; - -template<class T> -auto getMPIData(T& t){ -return MPIData<T>(t); -} - -// Default implementation for static datatypes -template<class T, class Enable> -struct MPIData -{ -friend auto getMPIData<T>(T&); -protected: -T& data_; - -MPIData(T& t) -: data_(t) -{} - -public: -void* ptr() const { -return (void*)&data_; -} - -// indicates whether the datatype can be resized -static constexpr bool static_size = true; - -int size() const{ -return 1; -} - -MPI_Datatype type() const { -return MPITraits<std::decay_t<T>>::getType(); -} -}; - -// dummy implementation for void -template<> -struct MPIData<void>{ -protected: -MPIData() {} - -public: -void* ptr(){ -return nullptr; -} -int size(){ -return 0; -} -void get(){} -MPI_Datatype type() const{ -return MPI_INT; -} -}; - -// specializations: -// std::vector of static sized elements or std::string -template<class T> -struct MPIData<T, void_t<std::tuple<decltype(std::declval<T>().data()), -decltype(std::declval<T>().size()), -typename std::decay_t<T>::value_type>>>{ -private: -template<class U> -using hasResizeOp = decltype(std::declval<U>().resize(0)); - -protected: -friend auto getMPIData<T>(T&); -MPIData(T& t) -: data_(t) -{} -public: -static constexpr bool static_size = std::is_const<T>::value || !Std::is_detected_v<hasResizeOp, T>; -void* ptr() { -return (void*) data_.data(); -} -int size() { -return data_.size(); -} -MPI_Datatype type() const{ -return MPITraits<typename std::decay_t<T>::value_type>::getType(); -} - -template<class S = T> -auto /*void*/ resize(int size) --> std::enable_if_t<!std::is_const<S>::value || !Std::is_detected_v<hasResizeOp, S>> -{ -data_.resize(size); -} - -protected: -T& data_; -}; + template<class, class = void> + struct MPIData; + + template<class T> + auto getMPIData(T& t){ + return MPIData<T>(t); + } + + // Default implementation for static datatypes + template<class T, class Enable> + struct MPIData + { + friend auto getMPIData<T>(T&); + protected: + T& data_; + + MPIData(T& t) + : data_(t) + {} + + public: + void* ptr() const { + return (void*)&data_; + } + + // indicates whether the datatype can be resized + static constexpr bool static_size = true; + + int size() const{ + return 1; + } + + MPI_Datatype type() const { + return MPITraits<std::decay_t<T>>::getType(); + } + }; + + // dummy implementation for void + template<> + struct MPIData<void>{ + protected: + MPIData() {} + + public: + void* ptr(){ + return nullptr; + } + int size(){ + return 0; + } + void get(){} + MPI_Datatype type() const{ + return MPI_INT; + } + }; + + // specializations: + // std::vector of static sized elements or std::string + template<class T> + struct MPIData<T, void_t<std::tuple<decltype(std::declval<T>().data()), + decltype(std::declval<T>().size()), + typename std::decay_t<T>::value_type>>>{ + private: + template<class U> + using hasResizeOp = decltype(std::declval<U>().resize(0)); + + protected: + friend auto getMPIData<T>(T&); + MPIData(T& t) + : data_(t) + {} + public: + static constexpr bool static_size = std::is_const<T>::value || !Std::is_detected_v<hasResizeOp, T>; + void* ptr() { + return (void*) data_.data(); + } + int size() { + return data_.size(); + } + MPI_Datatype type() const{ + return MPITraits<typename std::decay_t<T>::value_type>::getType(); + } + + template<class S = T> + auto /*void*/ resize(int size) + -> std::enable_if_t<!std::is_const<S>::value || !Std::is_detected_v<hasResizeOp, S>> + { + data_.resize(size); + } + + protected: + T& data_; + }; } /** -* @} -*/ + * @} + */ #endif #endif diff --git a/dune/common/parallel/mpifuture.hh b/dune/common/parallel/mpifuture.hh index 34b47bb4e0eeed1565571be50d67d3519f0690b9..4703d8b923a7bad04979314cc9aab2e30b09dfdd 100644 --- a/dune/common/parallel/mpifuture.hh +++ b/dune/common/parallel/mpifuture.hh @@ -13,162 +13,162 @@ #if HAVE_MPI namespace Dune{ -namespace impl{ -template<class T> -struct Buffer{ -Buffer(bool valid){ -if(valid) -value = std::make_unique<T>(); -} -template<class V> -Buffer(V&& t) -: value(std::make_unique<T>(std::forward<V>(t))) -{} -std::unique_ptr<T> value; -T get(){ -T tmp = std::move(*value); -value.reset(); -return tmp; -} -operator bool () const { -return (bool)value; -} -T& operator *() const{ -return *value; -} -}; - -template<class T> -struct Buffer<T&>{ -Buffer(bool valid = false) -{ -if(valid) -value = T(); -} -template<class V> -Buffer(V&& t) -: value(std::forward<V>(t)) -{} -std::optional<std::reference_wrapper<T>> value; -T& get(){ -T& tmp = *value; -value.reset(); -return tmp; -} -operator bool () const{ -return (bool)value; -} -T& operator *() const{ -return *value; -} -}; - -template<> -struct Buffer<void>{ -bool valid_; -Buffer(bool valid = false) -: valid_(valid) -{} -operator bool () const{ -return valid_; -} -void get(){} -}; -} - -/*! \brief Provides a future-like object for MPI communication. It contains -the object that will be received and might contain also a sending object, -which must be hold (keep alive) until the communication has been completed. -*/ -template<class R, class S = void> -class MPIFuture{ -mutable MPI_Request req_; -mutable MPI_Status status_; -impl::Buffer<R> data_; -impl::Buffer<S> send_data_; -friend class Communication<MPI_Comm>; -public: -MPIFuture(bool valid = false) -: req_(MPI_REQUEST_NULL) -, data_(valid) -{} - -// Hide this constructor if R or S is void -template<class V = R, class U = S> -MPIFuture(V&& recv_data, U&& send_data, typename std::enable_if_t<!std::is_void<V>::value && !std::is_void<U>::value>* = 0) : -req_(MPI_REQUEST_NULL) -, data_(std::forward<R>(recv_data)) -, send_data_(std::forward<S>(send_data)) -{} - -// hide this constructor if R is void -template<class V = R> -MPIFuture(V&& recv_data, typename std::enable_if_t<!std::is_void<V>::value>* = 0) -: req_(MPI_REQUEST_NULL) -, data_(std::forward<V>(recv_data)) -{} - -~MPIFuture() { -if(req_ != MPI_REQUEST_NULL){ -try{ // might fail when it is a collective communication -MPI_Cancel(&req_); -MPI_Request_free(&req_); -}catch(...){ -} -} -} - -MPIFuture(MPIFuture&& f) -: req_(MPI_REQUEST_NULL) -, data_(std::move(f.data_)) -, send_data_(std::move(f.send_data_)) -{ -std::swap(req_, f.req_); -std::swap(status_, f.status_); -} - -MPIFuture& operator=(MPIFuture&& f){ -std::swap(req_, f.req_); -std::swap(status_, f.status_); -std::swap(data_, f.data_); -std::swap(send_data_, f.send_data_); -return *this; -} - -bool valid() const{ -return (bool)data_; -} - -void wait(){ -if(!valid()) -DUNE_THROW(InvalidFutureException, "The MPIFuture is not valid!"); -MPI_Wait(&req_, &status_); -} - -bool ready() const{ -int flag = -1; -MPI_Test(&req_, &flag, &status_); -return flag; -} - -R get() { -wait(); -return data_.get(); -} - -S get_send_data(){ -wait(); -return send_data_.get(); -} - -auto get_mpidata(){ -return getMPIData<R>(*data_); -} - -auto get_send_mpidata(){ -return getMPIData<S>(*send_data_); -} -}; + namespace impl{ + template<class T> + struct Buffer{ + Buffer(bool valid){ + if(valid) + value = std::make_unique<T>(); + } + template<class V> + Buffer(V&& t) + : value(std::make_unique<T>(std::forward<V>(t))) + {} + std::unique_ptr<T> value; + T get(){ + T tmp = std::move(*value); + value.reset(); + return tmp; + } + operator bool () const { + return (bool)value; + } + T& operator *() const{ + return *value; + } + }; + + template<class T> + struct Buffer<T&>{ + Buffer(bool valid = false) + { + if(valid) + value = T(); + } + template<class V> + Buffer(V&& t) + : value(std::forward<V>(t)) + {} + std::optional<std::reference_wrapper<T>> value; + T& get(){ + T& tmp = *value; + value.reset(); + return tmp; + } + operator bool () const{ + return (bool)value; + } + T& operator *() const{ + return *value; + } + }; + + template<> + struct Buffer<void>{ + bool valid_; + Buffer(bool valid = false) + : valid_(valid) + {} + operator bool () const{ + return valid_; + } + void get(){} + }; + } + + /*! \brief Provides a future-like object for MPI communication. It contains + the object that will be received and might contain also a sending object, + which must be hold (keep alive) until the communication has been completed. + */ + template<class R, class S = void> + class MPIFuture{ + mutable MPI_Request req_; + mutable MPI_Status status_; + impl::Buffer<R> data_; + impl::Buffer<S> send_data_; + friend class Communication<MPI_Comm>; + public: + MPIFuture(bool valid = false) + : req_(MPI_REQUEST_NULL) + , data_(valid) + {} + + // Hide this constructor if R or S is void + template<class V = R, class U = S> + MPIFuture(V&& recv_data, U&& send_data, typename std::enable_if_t<!std::is_void<V>::value && !std::is_void<U>::value>* = 0) : + req_(MPI_REQUEST_NULL) + , data_(std::forward<R>(recv_data)) + , send_data_(std::forward<S>(send_data)) + {} + + // hide this constructor if R is void + template<class V = R> + MPIFuture(V&& recv_data, typename std::enable_if_t<!std::is_void<V>::value>* = 0) + : req_(MPI_REQUEST_NULL) + , data_(std::forward<V>(recv_data)) + {} + + ~MPIFuture() { + if(req_ != MPI_REQUEST_NULL){ + try{ // might fail when it is a collective communication + MPI_Cancel(&req_); + MPI_Request_free(&req_); + }catch(...){ + } + } + } + + MPIFuture(MPIFuture&& f) + : req_(MPI_REQUEST_NULL) + , data_(std::move(f.data_)) + , send_data_(std::move(f.send_data_)) + { + std::swap(req_, f.req_); + std::swap(status_, f.status_); + } + + MPIFuture& operator=(MPIFuture&& f){ + std::swap(req_, f.req_); + std::swap(status_, f.status_); + std::swap(data_, f.data_); + std::swap(send_data_, f.send_data_); + return *this; + } + + bool valid() const{ + return (bool)data_; + } + + void wait(){ + if(!valid()) + DUNE_THROW(InvalidFutureException, "The MPIFuture is not valid!"); + MPI_Wait(&req_, &status_); + } + + bool ready() const{ + int flag = -1; + MPI_Test(&req_, &flag, &status_); + return flag; + } + + R get() { + wait(); + return data_.get(); + } + + S get_send_data(){ + wait(); + return send_data_.get(); + } + + auto get_mpidata(){ + return getMPIData<R>(*data_); + } + + auto get_send_mpidata(){ + return getMPIData<S>(*send_data_); + } + }; } #endif diff --git a/dune/common/parallel/mpiguard.hh b/dune/common/parallel/mpiguard.hh index ef462cfb35e30c7b808f2540f983ebc8f4db6100..217fe9dce72464ebcc04bf9264c2ccf7ec99cf55 100644 --- a/dune/common/parallel/mpiguard.hh +++ b/dune/common/parallel/mpiguard.hh @@ -2,11 +2,11 @@ // vi: set et ts=4 sw=2 sts=2: /** -* @file -* @brief Implements a MPIGuard which detects an error on a remote process -* @author Christian Engwer -* @ingroup ParallelCommunication -*/ + * @file + * @brief Implements a MPIGuard which detects an error on a remote process + * @author Christian Engwer + * @ingroup ParallelCommunication + */ #ifndef DUNE_COMMON_MPIGUARD_HH #define DUNE_COMMON_MPIGUARD_HH @@ -22,212 +22,212 @@ namespace Dune #ifndef DOXYGEN -/* -Interface class for the communication needed by MPIGuard -*/ -struct GuardCommunicator -{ -// cleanup -virtual ~GuardCommunicator() {}; -// all the communication methods we need -virtual int rank() = 0; -virtual int size() = 0; -virtual int sum(int i) = 0; -// create a new GuardCommunicator pointer -template <class C> -static GuardCommunicator * create(const CollectiveCommunication<C> & c); + /* + Interface class for the communication needed by MPIGuard + */ + struct GuardCommunicator + { + // cleanup + virtual ~GuardCommunicator() {}; + // all the communication methods we need + virtual int rank() = 0; + virtual int size() = 0; + virtual int sum(int i) = 0; + // create a new GuardCommunicator pointer + template <class C> + static GuardCommunicator * create(const CollectiveCommunication<C> & c); #if HAVE_MPI -inline -static GuardCommunicator * create(const MPI_Comm & c); + inline + static GuardCommunicator * create(const MPI_Comm & c); #endif -}; - -namespace { -/* -templated implementation of different communication classes -*/ -// the default class will always fail, due to the missing implementation of "sum" -template <class Imp> -struct GenericGuardCommunicator -: public GuardCommunicator -{}; -// specialization for Communication -template <class T> -struct GenericGuardCommunicator< Communication<T> > -: public GuardCommunicator -{ -const Communication<T> comm; -GenericGuardCommunicator(const Communication<T> & c) : -comm(c) {} -int rank() override { return comm.rank(); }; -int size() override { return comm.size(); }; -int sum(int i) override { return comm.sum(i); } -}; + }; + + namespace { + /* + templated implementation of different communication classes + */ + // the default class will always fail, due to the missing implementation of "sum" + template <class Imp> + struct GenericGuardCommunicator + : public GuardCommunicator + {}; + // specialization for Communication + template <class T> + struct GenericGuardCommunicator< Communication<T> > + : public GuardCommunicator + { + const Communication<T> comm; + GenericGuardCommunicator(const Communication<T> & c) : + comm(c) {} + int rank() override { return comm.rank(); }; + int size() override { return comm.size(); }; + int sum(int i) override { return comm.sum(i); } + }; #if HAVE_MPI -// specialization for MPI_Comm -template <> -struct GenericGuardCommunicator<MPI_Comm> -: public GenericGuardCommunicator< Communication<MPI_Comm> > -{ -GenericGuardCommunicator(const MPI_Comm & c) : -GenericGuardCommunicator< Communication<MPI_Comm> >( -Communication<MPI_Comm>(c)) {} -}; + // specialization for MPI_Comm + template <> + struct GenericGuardCommunicator<MPI_Comm> + : public GenericGuardCommunicator< Communication<MPI_Comm> > + { + GenericGuardCommunicator(const MPI_Comm & c) : + GenericGuardCommunicator< Communication<MPI_Comm> >( + Communication<MPI_Comm>(c)) {} + }; #endif -} // anonymous namespace + } // anonymous namespace -template<class C> -GuardCommunicator * GuardCommunicator::create(const CollectiveCommunication<C> & comm) -{ -return new GenericGuardCommunicator< CollectiveCommunication<C> >(comm); -} + template<class C> + GuardCommunicator * GuardCommunicator::create(const CollectiveCommunication<C> & comm) + { + return new GenericGuardCommunicator< CollectiveCommunication<C> >(comm); + } #if HAVE_MPI -GuardCommunicator * GuardCommunicator::create(const MPI_Comm & comm) -{ -return new GenericGuardCommunicator< CollectiveCommunication<MPI_Comm> >(comm); -} + GuardCommunicator * GuardCommunicator::create(const MPI_Comm & comm) + { + return new GenericGuardCommunicator< CollectiveCommunication<MPI_Comm> >(comm); + } #endif #endif -/*! @brief This exception is thrown if the MPIGuard detects an error on a remote process -@ingroup ParallelCommunication -*/ -class MPIGuardError : public ParallelError {}; - -/*! @brief detects a thrown exception and communicates to all other processes -@ingroup ParallelCommunication - -@code -{ -MPIGuard guard(...); - -do_something(); - -// tell the guard that you successfully passed a critical operation -guard.finalize(); -// reactivate the guard for the next critical operation -guard.reactivate(); - -int result = do_something_else(); - -// tell the guard the result of your operation -guard.finalize(result == success); -} -@endcode - -You create a MPIGuard object. If an exception is risen on a -process the MPIGuard detects the exception, because the finalize -method was not called. When reaching the finalize call all -other processes are informed that an error occurred and the -MPIGuard throws an exception of type MPIGuardError. - -@note You can initialize the MPIGuard from different types of communication objects: -- MPIHelper -- Communication -- MPI_Comm -*/ -class MPIGuard -{ -GuardCommunicator * comm_; -bool active_; - -// we don't want to copy this class -MPIGuard (const MPIGuard &); - -public: -/*! @brief create an MPIGuard operating on the Communicator of the global Dune::MPIHelper - -@param active should the MPIGuard be active upon creation? -*/ -MPIGuard (bool active=true) : -comm_(GuardCommunicator::create( -MPIHelper::getCommunication())), -active_(active) -{} - -/*! @brief create an MPIGuard operating on the Communicator of a special Dune::MPIHelper m - -@param m a reference to an MPIHelper -@param active should the MPIGuard be active upon creation? -*/ -MPIGuard (MPIHelper & m, bool active=true) : -comm_(GuardCommunicator::create( -m.getCommunication())), -active_(active) -{} - -/*! @brief create an MPIGuard operating on an arbitrary communicator. - -Supported types for the communication object are: -- MPIHelper -- Communication -- MPI_Comm - -@param comm reference to a communication object -@param active should the MPIGuard be active upon creation? -*/ -template <class C> -MPIGuard (const C & comm, bool active=true) : -comm_(GuardCommunicator::create(comm)), -active_(active) -{} + /*! @brief This exception is thrown if the MPIGuard detects an error on a remote process + @ingroup ParallelCommunication + */ + class MPIGuardError : public ParallelError {}; + + /*! @brief detects a thrown exception and communicates to all other processes + @ingroup ParallelCommunication + + @code + { + MPIGuard guard(...); + + do_something(); + + // tell the guard that you successfully passed a critical operation + guard.finalize(); + // reactivate the guard for the next critical operation + guard.reactivate(); + + int result = do_something_else(); + + // tell the guard the result of your operation + guard.finalize(result == success); + } + @endcode + + You create a MPIGuard object. If an exception is risen on a + process the MPIGuard detects the exception, because the finalize + method was not called. When reaching the finalize call all + other processes are informed that an error occurred and the + MPIGuard throws an exception of type MPIGuardError. + + @note You can initialize the MPIGuard from different types of communication objects: + - MPIHelper + - Communication + - MPI_Comm + */ + class MPIGuard + { + GuardCommunicator * comm_; + bool active_; + + // we don't want to copy this class + MPIGuard (const MPIGuard &); + + public: + /*! @brief create an MPIGuard operating on the Communicator of the global Dune::MPIHelper + + @param active should the MPIGuard be active upon creation? + */ + MPIGuard (bool active=true) : + comm_(GuardCommunicator::create( + MPIHelper::getCommunication())), + active_(active) + {} + + /*! @brief create an MPIGuard operating on the Communicator of a special Dune::MPIHelper m + + @param m a reference to an MPIHelper + @param active should the MPIGuard be active upon creation? + */ + MPIGuard (MPIHelper & m, bool active=true) : + comm_(GuardCommunicator::create( + m.getCommunication())), + active_(active) + {} + + /*! @brief create an MPIGuard operating on an arbitrary communicator. + + Supported types for the communication object are: + - MPIHelper + - Communication + - MPI_Comm + + @param comm reference to a communication object + @param active should the MPIGuard be active upon creation? + */ + template <class C> + MPIGuard (const C & comm, bool active=true) : + comm_(GuardCommunicator::create(comm)), + active_(active) + {} #if HAVE_MPI -MPIGuard (const MPI_Comm & comm, bool active=true) : -comm_(GuardCommunicator::create(comm)), -active_(active) -{} + MPIGuard (const MPI_Comm & comm, bool active=true) : + comm_(GuardCommunicator::create(comm)), + active_(active) + {} #endif -/*! @brief destroy the guard and check for undetected exceptions -*/ -~MPIGuard() -{ -if (active_) -{ -active_ = false; -finalize(false); -} -delete comm_; -} - -/*! @brief reactivate the guard. - -If the guard is still active finalize(true) is called first. -*/ -void reactivate() { -if (active_ == true) -finalize(); -active_ = true; -} - -/*! @brief stop the guard. - -If no success parameter is passed, the guard assumes that -everything worked as planned. All errors are communicated -and an exception of type MPIGuardError is thrown if an error -(or exception) occurred on any of the processors in the -communicator. - -@param success inform the guard about possible errors -*/ -void finalize(bool success = true) -{ -int result = success ? 0 : 1; -bool was_active = active_; -active_ = false; -result = comm_->sum(result); -if (result>0 && was_active) -{ -DUNE_THROW(MPIGuardError, "Terminating process " -<< comm_->rank() << " due to " -<< result << " remote error(s)"); -} -} -}; + /*! @brief destroy the guard and check for undetected exceptions + */ + ~MPIGuard() + { + if (active_) + { + active_ = false; + finalize(false); + } + delete comm_; + } + + /*! @brief reactivate the guard. + + If the guard is still active finalize(true) is called first. + */ + void reactivate() { + if (active_ == true) + finalize(); + active_ = true; + } + + /*! @brief stop the guard. + + If no success parameter is passed, the guard assumes that + everything worked as planned. All errors are communicated + and an exception of type MPIGuardError is thrown if an error + (or exception) occurred on any of the processors in the + communicator. + + @param success inform the guard about possible errors + */ + void finalize(bool success = true) + { + int result = success ? 0 : 1; + bool was_active = active_; + active_ = false; + result = comm_->sum(result); + if (result>0 && was_active) + { + DUNE_THROW(MPIGuardError, "Terminating process " + << comm_->rank() << " due to " + << result << " remote error(s)"); + } + } + }; } diff --git a/dune/common/parallel/mpihelper.hh b/dune/common/parallel/mpihelper.hh index 892cec748b0008939eaf930af389eeb034b23e31..6f2f7eccbd5cdbea77754cd490f643047129cb7a 100644 --- a/dune/common/parallel/mpihelper.hh +++ b/dune/common/parallel/mpihelper.hh @@ -18,288 +18,288 @@ namespace Dune { -/** -* @file -* @brief Helpers for dealing with MPI. -* -* @ingroup ParallelCommunication -* -* Basically there are two helpers available: -* <dl> -* <dt>FakeMPIHelper</dt> -* <dd>A class adhering to the interface of MPIHelper -* that does not need MPI at all. This can be used -* to create a sequential program even if MPI is -* used to compile it. -* </dd> -* <dt>MPIHelper</dt> -* <dd>A real MPI helper. When the singleton -* gets instantiated MPI_Init will be -* called and before the program exits -* MPI_Finalize will be called. -* </dd> -* </dl> -* -* Example of who to use these classes: -* -* A program that is parallel if compiled with MPI -* and sequential otherwise: -* \code -* int main(int argc, char** argv){ -* typedef Dune::MPIHelper MPIHelper; -* MPIHelper::instance(argc, argv); -* typename MPIHelper::MPICommunicator world = -* MPIHelper::getCommunicator(); -* ... -* \endcode -* -* If one wants to have sequential program even if the code is -* compiled with mpi then one simply has to exchange the typedef -* with \code typedef Dune::MPIHelper FakeMPIHelper; \endcode. -* -* For checking whether we really use MPI or just fake please use -* MPIHelper::isFake (this is also possible at compile time!) -*/ -/** -* @brief A fake mpi helper. -* -* This helper can be used if no MPI is available -* or one wants to run sequentially even if MPI is -* available and used. -*/ -class FakeMPIHelper -{ -public: -enum { -/** -* @brief Are we fake (i.e. pretend to have MPI support but are compiled -* without.) -*/ -isFake = true -}; + /** + * @file + * @brief Helpers for dealing with MPI. + * + * @ingroup ParallelCommunication + * + * Basically there are two helpers available: + * <dl> + * <dt>FakeMPIHelper</dt> + * <dd>A class adhering to the interface of MPIHelper + * that does not need MPI at all. This can be used + * to create a sequential program even if MPI is + * used to compile it. + * </dd> + * <dt>MPIHelper</dt> + * <dd>A real MPI helper. When the singleton + * gets instantiated MPI_Init will be + * called and before the program exits + * MPI_Finalize will be called. + * </dd> + * </dl> + * + * Example of who to use these classes: + * + * A program that is parallel if compiled with MPI + * and sequential otherwise: + * \code + * int main(int argc, char** argv){ + * typedef Dune::MPIHelper MPIHelper; + * MPIHelper::instance(argc, argv); + * typename MPIHelper::MPICommunicator world = + * MPIHelper::getCommunicator(); + * ... + * \endcode + * + * If one wants to have sequential program even if the code is + * compiled with mpi then one simply has to exchange the typedef + * with \code typedef Dune::MPIHelper FakeMPIHelper; \endcode. + * + * For checking whether we really use MPI or just fake please use + * MPIHelper::isFake (this is also possible at compile time!) + */ + /** + * @brief A fake mpi helper. + * + * This helper can be used if no MPI is available + * or one wants to run sequentially even if MPI is + * available and used. + */ + class FakeMPIHelper + { + public: + enum { + /** + * @brief Are we fake (i.e. pretend to have MPI support but are compiled + * without.) + */ + isFake = true + }; -/** -* @brief The type of the mpi communicator. -*/ -typedef No_Comm MPICommunicator; + /** + * @brief The type of the mpi communicator. + */ + typedef No_Comm MPICommunicator; -/** \brief get the default communicator -* -* Return a communicator to exchange data with all processes -* -* \returns a fake communicator -*/ -DUNE_EXPORT static MPICommunicator getCommunicator () -{ -static MPICommunicator comm; -return comm; -} + /** \brief get the default communicator + * + * Return a communicator to exchange data with all processes + * + * \returns a fake communicator + */ + DUNE_EXPORT static MPICommunicator getCommunicator () + { + static MPICommunicator comm; + return comm; + } -/** \brief get a local communicator -* -* Returns a communicator to communicate with the local process only -* -* \returns a fake communicator -*/ -static MPICommunicator getLocalCommunicator () -{ -return getCommunicator(); -} + /** \brief get a local communicator + * + * Returns a communicator to communicate with the local process only + * + * \returns a fake communicator + */ + static MPICommunicator getLocalCommunicator () + { + return getCommunicator(); + } -// Will be deprecated after the 2.7 release -//[[deprecated("getCollectionCommunication is deprecated. Use getCommunication instead.")]] -static Communication<MPICommunicator> getCollectiveCommunication() -{ -return Communication<MPICommunicator>(getCommunicator()); -} + // Will be deprecated after the 2.7 release + //[[deprecated("getCollectionCommunication is deprecated. Use getCommunication instead.")]] + static Communication<MPICommunicator> getCollectiveCommunication() + { + return Communication<MPICommunicator>(getCommunicator()); + } -static Communication<MPICommunicator> -getCommunication() -{ -return Communication<MPICommunicator>(getCommunicator()); -} + static Communication<MPICommunicator> + getCommunication() + { + return Communication<MPICommunicator>(getCommunicator()); + } -/** -* @brief Get the singleton instance of the helper. -* -* This method has to be called with the same arguments -* that the main method of the program was called: -* \code -* int main(int argc, char** argv){ -* MPIHelper::instance(argc, argv); -* // program code comes here -* ... -* } -* \endcode -* @param argc The number of arguments provided to main. -* @param argv The arguments provided to main. -*/ -DUNE_EXPORT static FakeMPIHelper& instance(int argc, char** argv) -{ -(void)argc; (void)argv; -// create singleton instance -static FakeMPIHelper singleton; -return singleton; -} + /** + * @brief Get the singleton instance of the helper. + * + * This method has to be called with the same arguments + * that the main method of the program was called: + * \code + * int main(int argc, char** argv){ + * MPIHelper::instance(argc, argv); + * // program code comes here + * ... + * } + * \endcode + * @param argc The number of arguments provided to main. + * @param argv The arguments provided to main. + */ + DUNE_EXPORT static FakeMPIHelper& instance(int argc, char** argv) + { + (void)argc; (void)argv; + // create singleton instance + static FakeMPIHelper singleton; + return singleton; + } -/** -* @brief return rank of process, i.e. zero -*/ -int rank () const { return 0; } -/** -* @brief return rank of process, i.e. one -*/ -int size () const { return 1; } + /** + * @brief return rank of process, i.e. zero + */ + int rank () const { return 0; } + /** + * @brief return rank of process, i.e. one + */ + int size () const { return 1; } -private: -FakeMPIHelper() {} -FakeMPIHelper(const FakeMPIHelper&); -FakeMPIHelper& operator=(const FakeMPIHelper); -}; + private: + FakeMPIHelper() {} + FakeMPIHelper(const FakeMPIHelper&); + FakeMPIHelper& operator=(const FakeMPIHelper); + }; #if HAVE_MPI -/** -* @brief A real mpi helper. -* @ingroup ParallelCommunication -* -* This helper should be used for parallel programs. -*/ -class MPIHelper -{ -public: -enum { -/** -* @brief Are we fake (i. e. pretend to have MPI support but are compiled -* without. -*/ -isFake = false -}; + /** + * @brief A real mpi helper. + * @ingroup ParallelCommunication + * + * This helper should be used for parallel programs. + */ + class MPIHelper + { + public: + enum { + /** + * @brief Are we fake (i. e. pretend to have MPI support but are compiled + * without. + */ + isFake = false + }; -/** -* @brief The type of the mpi communicator. -*/ -typedef MPI_Comm MPICommunicator; + /** + * @brief The type of the mpi communicator. + */ + typedef MPI_Comm MPICommunicator; -/** \brief get the default communicator -* -* Return a communicator to exchange data with all processes -* -* \returns MPI_COMM_WORLD -*/ -static MPICommunicator getCommunicator () -{ -return MPI_COMM_WORLD; -} + /** \brief get the default communicator + * + * Return a communicator to exchange data with all processes + * + * \returns MPI_COMM_WORLD + */ + static MPICommunicator getCommunicator () + { + return MPI_COMM_WORLD; + } -/** \brief get a local communicator -* -* Returns a communicator to exchange data with the local process only -* -* \returns MPI_COMM_SELF -*/ -static MPICommunicator getLocalCommunicator () -{ -return MPI_COMM_SELF; -} + /** \brief get a local communicator + * + * Returns a communicator to exchange data with the local process only + * + * \returns MPI_COMM_SELF + */ + static MPICommunicator getLocalCommunicator () + { + return MPI_COMM_SELF; + } -// Will be deprecated after the 2.7 release -//[[deprecated("getCollectionCommunication is deprecated. Use getCommunication instead.")]] -static Communication<MPICommunicator> -getCollectiveCommunication() -{ -return Communication<MPICommunicator>(getCommunicator()); -} + // Will be deprecated after the 2.7 release + //[[deprecated("getCollectionCommunication is deprecated. Use getCommunication instead.")]] + static Communication<MPICommunicator> + getCollectiveCommunication() + { + return Communication<MPICommunicator>(getCommunicator()); + } -static Communication<MPICommunicator> -getCommunication() -{ -return Communication<MPICommunicator>(getCommunicator()); -} -/** -* @brief Get the singleton instance of the helper. -* -* This method has to be called with the same arguments -* that the main method of the program was called: -* \code -* int main(int argc, char** argv){ -* MPIHelper::instance(argc, argv); -* // program code comes here -* ... -* } -* \endcode -* @param argc The number of arguments provided to main. -* @param argv The arguments provided to main. -*/ -DUNE_EXPORT static MPIHelper& instance(int& argc, char**& argv) -{ -// create singleton instance -static MPIHelper singleton (argc, argv); -return singleton; -} + static Communication<MPICommunicator> + getCommunication() + { + return Communication<MPICommunicator>(getCommunicator()); + } + /** + * @brief Get the singleton instance of the helper. + * + * This method has to be called with the same arguments + * that the main method of the program was called: + * \code + * int main(int argc, char** argv){ + * MPIHelper::instance(argc, argv); + * // program code comes here + * ... + * } + * \endcode + * @param argc The number of arguments provided to main. + * @param argv The arguments provided to main. + */ + DUNE_EXPORT static MPIHelper& instance(int& argc, char**& argv) + { + // create singleton instance + static MPIHelper singleton (argc, argv); + return singleton; + } -/** -* @brief return rank of process -*/ -int rank () const { return rank_; } -/** -* @brief return number of processes -*/ -int size () const { return size_; } + /** + * @brief return rank of process + */ + int rank () const { return rank_; } + /** + * @brief return number of processes + */ + int size () const { return size_; } -private: -int rank_; -int size_; -bool initializedHere_; -void prevent_warning(int){} + private: + int rank_; + int size_; + bool initializedHere_; + void prevent_warning(int){} -//! \brief calls MPI_Init with argc and argv as parameters -MPIHelper(int& argc, char**& argv) -: initializedHere_(false) -{ -int wasInitialized = -1; -MPI_Initialized( &wasInitialized ); -if(!wasInitialized) -{ -rank_ = -1; -size_ = -1; -static int is_initialized = MPI_Init(&argc, &argv); -prevent_warning(is_initialized); -initializedHere_ = true; -} + //! \brief calls MPI_Init with argc and argv as parameters + MPIHelper(int& argc, char**& argv) + : initializedHere_(false) + { + int wasInitialized = -1; + MPI_Initialized( &wasInitialized ); + if(!wasInitialized) + { + rank_ = -1; + size_ = -1; + static int is_initialized = MPI_Init(&argc, &argv); + prevent_warning(is_initialized); + initializedHere_ = true; + } -MPI_Comm_rank(MPI_COMM_WORLD,&rank_); -MPI_Comm_size(MPI_COMM_WORLD,&size_); + MPI_Comm_rank(MPI_COMM_WORLD,&rank_); + MPI_Comm_size(MPI_COMM_WORLD,&size_); -assert( rank_ >= 0 ); -assert( size_ >= 1 ); + assert( rank_ >= 0 ); + assert( size_ >= 1 ); -dverb << "Called MPI_Init on p=" << rank_ << "!" << std::endl; -} -//! \brief calls MPI_Finalize -~MPIHelper() -{ -int wasFinalized = -1; -MPI_Finalized( &wasFinalized ); -if(!wasFinalized && initializedHere_) -{ -MPI_Finalize(); -dverb << "Called MPI_Finalize on p=" << rank_ << "!" <<std::endl; -} + dverb << "Called MPI_Init on p=" << rank_ << "!" << std::endl; + } + //! \brief calls MPI_Finalize + ~MPIHelper() + { + int wasFinalized = -1; + MPI_Finalized( &wasFinalized ); + if(!wasFinalized && initializedHere_) + { + MPI_Finalize(); + dverb << "Called MPI_Finalize on p=" << rank_ << "!" <<std::endl; + } -} -MPIHelper(const MPIHelper&); -MPIHelper& operator=(const MPIHelper); -}; + } + MPIHelper(const MPIHelper&); + MPIHelper& operator=(const MPIHelper); + }; #else // !HAVE_MPI -// We do not have MPI therefore FakeMPIHelper -// is the MPIHelper -/** -* @brief If no MPI is available FakeMPIHelper becomes the MPIHelper -* @ingroup ParallelCommunication -*/ -typedef FakeMPIHelper MPIHelper; + // We do not have MPI therefore FakeMPIHelper + // is the MPIHelper + /** + * @brief If no MPI is available FakeMPIHelper becomes the MPIHelper + * @ingroup ParallelCommunication + */ + typedef FakeMPIHelper MPIHelper; #endif // !HAVE_MPI diff --git a/dune/common/parallel/mpipack.hh b/dune/common/parallel/mpipack.hh index efbfe1967aa5481b3a932c857b5611282e67e8bf..41f855d11a54fb50837218feadf49b6eb0ea79eb 100644 --- a/dune/common/parallel/mpipack.hh +++ b/dune/common/parallel/mpipack.hh @@ -1,18 +1,18 @@ // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- // vi: set et ts=4 sw=2 sts=2: /** -* @file -* -* @brief See MPI_Pack. -* -* This Wrapper class takes care of the -* memory management and provides methods to pack and unpack -* objects. All objects that can be used for MPI communication can -* also be packed and unpacked to/from MPIPack. -* -* @author Nils-Arne Dreier -* @ingroup ParallelCommunication -*/ + * @file + * + * @brief See MPI_Pack. + * + * This Wrapper class takes care of the + * memory management and provides methods to pack and unpack + * objects. All objects that can be used for MPI communication can + * also be packed and unpacked to/from MPIPack. + * + * @author Nils-Arne Dreier + * @ingroup ParallelCommunication + */ #ifndef DUNE_COMMON_PARALLEL_MPIPACK_HH @@ -28,197 +28,197 @@ namespace Dune { -class MPIPack { -std::vector<char> _buffer; -int _position; -MPI_Comm _comm; - -friend struct MPIData<MPIPack>; -friend struct MPIData<const MPIPack>; -public: -MPIPack(Communication<MPI_Comm> comm, std::size_t size = 0) -: _buffer(size) -, _position(0) -, _comm(comm) -{} - -// Its not valid to copy a MPIPack but you can move it -MPIPack(const MPIPack&) = delete; -MPIPack& operator = (const MPIPack& other) = delete; -MPIPack(MPIPack&&) = default; -MPIPack& operator = (MPIPack&& other) = default; - -/** @brief Packs the data into the object. Enlarges the internal buffer if -* necessary. -* -* @throw MPIError -*/ -template<class T> -void pack(const T& data){ -auto mpidata = getMPIData(data); -int size = getPackSize(mpidata.size(), _comm, mpidata.type()); -constexpr bool has_static_size = decltype(getMPIData(std::declval<T&>()))::static_size; -if(!has_static_size) -size += getPackSize(1, _comm, MPI_INT); -if (_position + size > 0 && size_t(_position + size) > _buffer.size()) // resize buffer if necessary -_buffer.resize(_position + size); -if(!has_static_size){ -int size = mpidata.size(); -MPI_Pack(&size, 1, MPI_INT, _buffer.data(), _buffer.size(), -&_position, _comm); -} -MPI_Pack(mpidata.ptr(), mpidata.size(), -mpidata.type(), _buffer.data(), _buffer.size(), -&_position, _comm); -} - -/** @brief Unpacks data from the object -* -* @throw MPIError -*/ -template<class T> -auto /*void*/ unpack(T& data) --> std::enable_if_t<decltype(getMPIData(data))::static_size, void> -{ -auto mpidata = getMPIData(data); -MPI_Unpack(_buffer.data(), _buffer.size(), &_position, -mpidata.ptr(), mpidata.size(), -mpidata.type(), _comm); -} - -/** @brief Unpacks data from the object -* -* @throw MPIError -*/ -template<class T> -auto /*void*/ unpack(T& data) --> std::enable_if_t<!decltype(getMPIData(data))::static_size, void> -{ -auto mpidata = getMPIData(data); -int size = 0; -MPI_Unpack(_buffer.data(), _buffer.size(), &_position, -&size, 1, -MPI_INT, _comm); -mpidata.resize(size); -MPI_Unpack(_buffer.data(), _buffer.size(), &_position, -mpidata.ptr(), mpidata.size(), -mpidata.type(), _comm); -} - - -//! @copydoc pack -template<typename T> -friend MPIPack& operator << (MPIPack& p, const T& t){ -p.pack(t); -return p; -} - -//! @copydoc unpack -template<typename T> -friend MPIPack& operator >> (MPIPack& p, T& t){ -p.unpack(t); -return p; -} - -//! @copydoc unpack -template<typename T> -MPIPack& read(T& t){ -unpack(t); -return *this; -} - -//! @copydoc pack -template<typename T> -MPIPack& write(const T& t){ -pack(t); -return *this; -} - -/** @brief Resizes the internal buffer. -\param size new size of internal buffer -*/ -void resize(size_t size){ -_buffer.resize(size); -} - -/** @brief Enlarges the internal buffer. -*/ -void enlarge(int s) { -_buffer.resize(_buffer.size() + s); -} - -/** @brief Returns the size of the internal buffer. -*/ -size_t size() const { -return _buffer.size(); -} - -/** @brief Sets the position in the buffer where the next -* pack/unpack operation should take place. -*/ -void seek(int p){ -_position = p; -} - -/** @brief Gets the position in the buffer where the next -* pack/unpack operation should take place. -*/ -int tell() const{ -return _position; -} - -/** @brief Checks whether the end of the buffer is reached. -*/ -bool eof() const{ -return std::size_t(_position)==_buffer.size(); -} - -/** @brief Returns the size of the data needed to store the data -* in an MPIPack. See `MPI_Pack_size`. -*/ -static int getPackSize(int len, const MPI_Comm& comm, const MPI_Datatype& dt){ -int size; -MPI_Pack_size(len, dt, comm, &size); -return size; -} - -friend bool operator==(const MPIPack& a, const MPIPack& b) { -return a._buffer == b._buffer && a._comm == b._comm; -} -friend bool operator!=(const MPIPack& a, const MPIPack& b) { -return !(a==b); -} - -}; - -template<class P> -struct MPIData<P, std::enable_if_t<std::is_same<std::remove_const_t<P>, MPIPack>::value>> { -protected: -friend auto getMPIData<P>(P& t); -MPIData(P& t) : -data_(t) -{} -public: -static constexpr bool static_size = std::is_const<P>::value; - -void* ptr() { -return (void*) data_._buffer.data(); -} - -int size() { -return data_.size(); -} - -MPI_Datatype type() const{ -return MPI_PACKED; -} - -void resize(int size){ -data_.resize(size); -} -protected: -P& data_; -}; + class MPIPack { + std::vector<char> _buffer; + int _position; + MPI_Comm _comm; + + friend struct MPIData<MPIPack>; + friend struct MPIData<const MPIPack>; + public: + MPIPack(Communication<MPI_Comm> comm, std::size_t size = 0) + : _buffer(size) + , _position(0) + , _comm(comm) + {} + + // Its not valid to copy a MPIPack but you can move it + MPIPack(const MPIPack&) = delete; + MPIPack& operator = (const MPIPack& other) = delete; + MPIPack(MPIPack&&) = default; + MPIPack& operator = (MPIPack&& other) = default; + + /** @brief Packs the data into the object. Enlarges the internal buffer if + * necessary. + * + * @throw MPIError + */ + template<class T> + void pack(const T& data){ + auto mpidata = getMPIData(data); + int size = getPackSize(mpidata.size(), _comm, mpidata.type()); + constexpr bool has_static_size = decltype(getMPIData(std::declval<T&>()))::static_size; + if(!has_static_size) + size += getPackSize(1, _comm, MPI_INT); + if (_position + size > 0 && size_t(_position + size) > _buffer.size()) // resize buffer if necessary + _buffer.resize(_position + size); + if(!has_static_size){ + int size = mpidata.size(); + MPI_Pack(&size, 1, MPI_INT, _buffer.data(), _buffer.size(), + &_position, _comm); + } + MPI_Pack(mpidata.ptr(), mpidata.size(), + mpidata.type(), _buffer.data(), _buffer.size(), + &_position, _comm); + } + + /** @brief Unpacks data from the object + * + * @throw MPIError + */ + template<class T> + auto /*void*/ unpack(T& data) + -> std::enable_if_t<decltype(getMPIData(data))::static_size, void> + { + auto mpidata = getMPIData(data); + MPI_Unpack(_buffer.data(), _buffer.size(), &_position, + mpidata.ptr(), mpidata.size(), + mpidata.type(), _comm); + } + + /** @brief Unpacks data from the object + * + * @throw MPIError + */ + template<class T> + auto /*void*/ unpack(T& data) + -> std::enable_if_t<!decltype(getMPIData(data))::static_size, void> + { + auto mpidata = getMPIData(data); + int size = 0; + MPI_Unpack(_buffer.data(), _buffer.size(), &_position, + &size, 1, + MPI_INT, _comm); + mpidata.resize(size); + MPI_Unpack(_buffer.data(), _buffer.size(), &_position, + mpidata.ptr(), mpidata.size(), + mpidata.type(), _comm); + } + + + //! @copydoc pack + template<typename T> + friend MPIPack& operator << (MPIPack& p, const T& t){ + p.pack(t); + return p; + } + + //! @copydoc unpack + template<typename T> + friend MPIPack& operator >> (MPIPack& p, T& t){ + p.unpack(t); + return p; + } + + //! @copydoc unpack + template<typename T> + MPIPack& read(T& t){ + unpack(t); + return *this; + } + + //! @copydoc pack + template<typename T> + MPIPack& write(const T& t){ + pack(t); + return *this; + } + + /** @brief Resizes the internal buffer. + \param size new size of internal buffer + */ + void resize(size_t size){ + _buffer.resize(size); + } + + /** @brief Enlarges the internal buffer. + */ + void enlarge(int s) { + _buffer.resize(_buffer.size() + s); + } + + /** @brief Returns the size of the internal buffer. + */ + size_t size() const { + return _buffer.size(); + } + + /** @brief Sets the position in the buffer where the next + * pack/unpack operation should take place. + */ + void seek(int p){ + _position = p; + } + + /** @brief Gets the position in the buffer where the next + * pack/unpack operation should take place. + */ + int tell() const{ + return _position; + } + + /** @brief Checks whether the end of the buffer is reached. + */ + bool eof() const{ + return std::size_t(_position)==_buffer.size(); + } + + /** @brief Returns the size of the data needed to store the data + * in an MPIPack. See `MPI_Pack_size`. + */ + static int getPackSize(int len, const MPI_Comm& comm, const MPI_Datatype& dt){ + int size; + MPI_Pack_size(len, dt, comm, &size); + return size; + } + + friend bool operator==(const MPIPack& a, const MPIPack& b) { + return a._buffer == b._buffer && a._comm == b._comm; + } + friend bool operator!=(const MPIPack& a, const MPIPack& b) { + return !(a==b); + } + + }; + + template<class P> + struct MPIData<P, std::enable_if_t<std::is_same<std::remove_const_t<P>, MPIPack>::value>> { + protected: + friend auto getMPIData<P>(P& t); + MPIData(P& t) : + data_(t) + {} + public: + static constexpr bool static_size = std::is_const<P>::value; + + void* ptr() { + return (void*) data_._buffer.data(); + } + + int size() { + return data_.size(); + } + + MPI_Datatype type() const{ + return MPI_PACKED; + } + + void resize(int size){ + data_.resize(size); + } + protected: + P& data_; + }; } // end namespace Dune diff --git a/dune/common/parallel/mpitraits.hh b/dune/common/parallel/mpitraits.hh index 77325da75c5d839d568aa6ebde62f49e5865c25d..38dc58b74681c2b444ab87ed32ff6a9e066fd78e 100644 --- a/dune/common/parallel/mpitraits.hh +++ b/dune/common/parallel/mpitraits.hh @@ -5,14 +5,14 @@ #include <dune/internal/dune-common.hh> /** @addtogroup ParallelCommunication -* -* @{ -*/ + * + * @{ + */ /** -* @file -* @brief Traits classes for mapping types onto MPI_Datatype. -* @author Markus Blatt -*/ + * @file + * @brief Traits classes for mapping types onto MPI_Datatype. + * @author Markus Blatt + */ #if HAVE_MPI @@ -25,171 +25,171 @@ namespace Dune { -/** -* @brief A traits class describing the mapping of types onto MPI_Datatypes. -* -* Specializations exist for the default types. -* Specializations should provide a static method -* \code -* static MPI_Datatype getType(); -* \endcode -*/ -template<typename T> -struct MPITraits -{ -private: -MPITraits(){} -MPITraits(const MPITraits&){} -static MPI_Datatype datatype; -static MPI_Datatype vectortype; -public: -static inline MPI_Datatype getType() -{ -if(datatype==MPI_DATATYPE_NULL) { -MPI_Type_contiguous(sizeof(T),MPI_BYTE,&datatype); -MPI_Type_commit(&datatype); -} -return datatype; -} -static constexpr bool is_intrinsic = false; -}; -template<class T> -MPI_Datatype MPITraits<T>::datatype = MPI_DATATYPE_NULL; + /** + * @brief A traits class describing the mapping of types onto MPI_Datatypes. + * + * Specializations exist for the default types. + * Specializations should provide a static method + * \code + * static MPI_Datatype getType(); + * \endcode + */ + template<typename T> + struct MPITraits + { + private: + MPITraits(){} + MPITraits(const MPITraits&){} + static MPI_Datatype datatype; + static MPI_Datatype vectortype; + public: + static inline MPI_Datatype getType() + { + if(datatype==MPI_DATATYPE_NULL) { + MPI_Type_contiguous(sizeof(T),MPI_BYTE,&datatype); + MPI_Type_commit(&datatype); + } + return datatype; + } + static constexpr bool is_intrinsic = false; + }; + template<class T> + MPI_Datatype MPITraits<T>::datatype = MPI_DATATYPE_NULL; #ifndef DOXYGEN -// A Macro for defining traits for the primitive data types + // A Macro for defining traits for the primitive data types #define ComposeMPITraits(p,m) \ -template<> \ -struct MPITraits<p>{ \ -static inline MPI_Datatype getType(){ \ -return m; \ -} \ -static constexpr bool is_intrinsic = true; \ -} - -ComposeMPITraits(char, MPI_CHAR); -ComposeMPITraits(unsigned char,MPI_UNSIGNED_CHAR); -ComposeMPITraits(short,MPI_SHORT); -ComposeMPITraits(unsigned short,MPI_UNSIGNED_SHORT); -ComposeMPITraits(int,MPI_INT); -ComposeMPITraits(unsigned int,MPI_UNSIGNED); -ComposeMPITraits(long,MPI_LONG); -ComposeMPITraits(unsigned long,MPI_UNSIGNED_LONG); -ComposeMPITraits(float,MPI_FLOAT); -ComposeMPITraits(double,MPI_DOUBLE); -ComposeMPITraits(long double,MPI_LONG_DOUBLE); + template<> \ + struct MPITraits<p>{ \ + static inline MPI_Datatype getType(){ \ + return m; \ + } \ + static constexpr bool is_intrinsic = true; \ + } + + ComposeMPITraits(char, MPI_CHAR); + ComposeMPITraits(unsigned char,MPI_UNSIGNED_CHAR); + ComposeMPITraits(short,MPI_SHORT); + ComposeMPITraits(unsigned short,MPI_UNSIGNED_SHORT); + ComposeMPITraits(int,MPI_INT); + ComposeMPITraits(unsigned int,MPI_UNSIGNED); + ComposeMPITraits(long,MPI_LONG); + ComposeMPITraits(unsigned long,MPI_UNSIGNED_LONG); + ComposeMPITraits(float,MPI_FLOAT); + ComposeMPITraits(double,MPI_DOUBLE); + ComposeMPITraits(long double,MPI_LONG_DOUBLE); #undef ComposeMPITraits -template<class K, int n> class FieldVector; - -template<class K, int n> -struct MPITraits<FieldVector<K,n> > -{ -static MPI_Datatype datatype; -static MPI_Datatype vectortype; - -static inline MPI_Datatype getType() -{ -if(datatype==MPI_DATATYPE_NULL) { -MPI_Type_contiguous(n, MPITraits<K>::getType(), &vectortype); -MPI_Type_commit(&vectortype); -FieldVector<K,n> fvector; -MPI_Aint base; -MPI_Aint displ; -MPI_Get_address(&fvector, &base); -MPI_Get_address(&(fvector[0]), &displ); -displ -= base; -int length[1]={1}; - -MPI_Type_create_struct(1, length, &displ, &vectortype, &datatype); -MPI_Type_commit(&datatype); -} -return datatype; -} - -}; - -template<class K, int n> -MPI_Datatype MPITraits<FieldVector<K,n> >::datatype = MPI_DATATYPE_NULL; -template<class K, int n> -MPI_Datatype MPITraits<FieldVector<K,n> >::vectortype = {MPI_DATATYPE_NULL}; - - -template<int k> -class bigunsignedint; - -template<int k> -struct MPITraits<bigunsignedint<k> > -{ -static MPI_Datatype datatype; -static MPI_Datatype vectortype; - -static inline MPI_Datatype getType() -{ -if(datatype==MPI_DATATYPE_NULL) { -MPI_Type_contiguous(bigunsignedint<k>::n, MPITraits<std::uint16_t>::getType(), -&vectortype); -//MPI_Type_commit(&vectortype); -bigunsignedint<k> data; -MPI_Aint base; -MPI_Aint displ; -MPI_Get_address(&data, &base); -MPI_Get_address(&(data.digit), &displ); -displ -= base; -int length[1]={1}; -MPI_Type_create_struct(1, length, &displ, &vectortype, &datatype); -MPI_Type_commit(&datatype); -} -return datatype; -} -}; + template<class K, int n> class FieldVector; + + template<class K, int n> + struct MPITraits<FieldVector<K,n> > + { + static MPI_Datatype datatype; + static MPI_Datatype vectortype; + + static inline MPI_Datatype getType() + { + if(datatype==MPI_DATATYPE_NULL) { + MPI_Type_contiguous(n, MPITraits<K>::getType(), &vectortype); + MPI_Type_commit(&vectortype); + FieldVector<K,n> fvector; + MPI_Aint base; + MPI_Aint displ; + MPI_Get_address(&fvector, &base); + MPI_Get_address(&(fvector[0]), &displ); + displ -= base; + int length[1]={1}; + + MPI_Type_create_struct(1, length, &displ, &vectortype, &datatype); + MPI_Type_commit(&datatype); + } + return datatype; + } + + }; + + template<class K, int n> + MPI_Datatype MPITraits<FieldVector<K,n> >::datatype = MPI_DATATYPE_NULL; + template<class K, int n> + MPI_Datatype MPITraits<FieldVector<K,n> >::vectortype = {MPI_DATATYPE_NULL}; + + + template<int k> + class bigunsignedint; + + template<int k> + struct MPITraits<bigunsignedint<k> > + { + static MPI_Datatype datatype; + static MPI_Datatype vectortype; + + static inline MPI_Datatype getType() + { + if(datatype==MPI_DATATYPE_NULL) { + MPI_Type_contiguous(bigunsignedint<k>::n, MPITraits<std::uint16_t>::getType(), + &vectortype); + //MPI_Type_commit(&vectortype); + bigunsignedint<k> data; + MPI_Aint base; + MPI_Aint displ; + MPI_Get_address(&data, &base); + MPI_Get_address(&(data.digit), &displ); + displ -= base; + int length[1]={1}; + MPI_Type_create_struct(1, length, &displ, &vectortype, &datatype); + MPI_Type_commit(&datatype); + } + return datatype; + } + }; } namespace Dune { -template<int k> -MPI_Datatype MPITraits<bigunsignedint<k> >::datatype = MPI_DATATYPE_NULL; -template<int k> -MPI_Datatype MPITraits<bigunsignedint<k> >::vectortype = MPI_DATATYPE_NULL; - -template<typename T1, typename T2> -struct MPITraits<std::pair<T1,T2 > > -{ -public: -inline static MPI_Datatype getType(); -private: -static MPI_Datatype type; -}; -template<typename T1, typename T2> -MPI_Datatype MPITraits<std::pair<T1,T2> >::getType() -{ -if(type==MPI_DATATYPE_NULL) { -int length[2] = {1, 1}; -MPI_Aint disp[2]; -MPI_Datatype types[2] = {MPITraits<T1>::getType(), -MPITraits<T2>::getType()}; - -using Pair = std::pair<T1, T2>; -static_assert(std::is_standard_layout<Pair>::value, "offsetof() is only defined for standard layout types"); -disp[0] = offsetof(Pair, first); -disp[1] = offsetof(Pair, second); - -MPI_Datatype tmp; -MPI_Type_create_struct(2, length, disp, types, &tmp); - -MPI_Type_create_resized(tmp, 0, sizeof(Pair), &type); -MPI_Type_commit(&type); - -MPI_Type_free(&tmp); -} -return type; -} - -template<typename T1, typename T2> -MPI_Datatype MPITraits<std::pair<T1,T2> >::type=MPI_DATATYPE_NULL; + template<int k> + MPI_Datatype MPITraits<bigunsignedint<k> >::datatype = MPI_DATATYPE_NULL; + template<int k> + MPI_Datatype MPITraits<bigunsignedint<k> >::vectortype = MPI_DATATYPE_NULL; + + template<typename T1, typename T2> + struct MPITraits<std::pair<T1,T2 > > + { + public: + inline static MPI_Datatype getType(); + private: + static MPI_Datatype type; + }; + template<typename T1, typename T2> + MPI_Datatype MPITraits<std::pair<T1,T2> >::getType() + { + if(type==MPI_DATATYPE_NULL) { + int length[2] = {1, 1}; + MPI_Aint disp[2]; + MPI_Datatype types[2] = {MPITraits<T1>::getType(), + MPITraits<T2>::getType()}; + + using Pair = std::pair<T1, T2>; + static_assert(std::is_standard_layout<Pair>::value, "offsetof() is only defined for standard layout types"); + disp[0] = offsetof(Pair, first); + disp[1] = offsetof(Pair, second); + + MPI_Datatype tmp; + MPI_Type_create_struct(2, length, disp, types, &tmp); + + MPI_Type_create_resized(tmp, 0, sizeof(Pair), &type); + MPI_Type_commit(&type); + + MPI_Type_free(&tmp); + } + return type; + } + + template<typename T1, typename T2> + MPI_Datatype MPITraits<std::pair<T1,T2> >::type=MPI_DATATYPE_NULL; #endif // !DOXYGEN diff --git a/dune/common/parallel/plocalindex.hh b/dune/common/parallel/plocalindex.hh index 5e848b9803402a2c2fee76450596e3011c828adb..a8426ede3a247a87e9deef9382199994838ac2db 100644 --- a/dune/common/parallel/plocalindex.hh +++ b/dune/common/parallel/plocalindex.hh @@ -15,307 +15,307 @@ namespace Dune { -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Provides classes for use as the local index in ParallelIndexSet for distributed computing. -* @author Markus Blatt -*/ - -template<class T> class ParallelLocalIndex; - -/** -* @brief Print the local index to a stream. -* @param os The output stream to print to. -* @param index The index to print. -*/ -template<class T> -std::ostream& operator<<(std::ostream& os, const ParallelLocalIndex<T>& index) -{ -os<<"{local="<<index.localIndex_<<", attr="<<T(index.attribute_)<<", public=" -<<(index.public_ ? true : false)<<"}"; -return os; -} - -/** -* @brief An index present on the local process with an additional attribute flag. -*/ -template<typename T> -class ParallelLocalIndex -{ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for use as the local index in ParallelIndexSet for distributed computing. + * @author Markus Blatt + */ + + template<class T> class ParallelLocalIndex; + + /** + * @brief Print the local index to a stream. + * @param os The output stream to print to. + * @param index The index to print. + */ + template<class T> + std::ostream& operator<<(std::ostream& os, const ParallelLocalIndex<T>& index) + { + os<<"{local="<<index.localIndex_<<", attr="<<T(index.attribute_)<<", public=" + <<(index.public_ ? true : false)<<"}"; + return os; + } + + /** + * @brief An index present on the local process with an additional attribute flag. + */ + template<typename T> + class ParallelLocalIndex + { #if HAVE_MPI -// friend declaration needed for MPITraits -friend struct MPITraits<ParallelLocalIndex<T> >; + // friend declaration needed for MPITraits + friend struct MPITraits<ParallelLocalIndex<T> >; #endif -friend std::ostream& operator<<<>(std::ostream& os, const ParallelLocalIndex<T>& index); - -public: -/** -* @brief The type of the attributes. -* Normally this will be an enumeration like -* <pre> -* enum Attributes{owner, border, overlap}; -* </pre> -*/ -typedef T Attribute; -/** -* @brief Constructor. -* -* The local index will be initialized to 0. -* @param attribute The attribute of the index. -* @param isPublic True if the index might also be -* known to other processes. -*/ -ParallelLocalIndex(const Attribute& attribute, bool isPublic); - -/** -* @brief Constructor. -* -* @param localIndex The local index. -* @param attribute The attribute of the index. -* @param isPublic True if the index might also be -* known to other processes. -*/ -ParallelLocalIndex(size_t localIndex, const Attribute& attribute, bool isPublic=true); -/** -* @brief Parameterless constructor. -* -* Needed for use in container classes. -*/ -ParallelLocalIndex(); + friend std::ostream& operator<<<>(std::ostream& os, const ParallelLocalIndex<T>& index); + + public: + /** + * @brief The type of the attributes. + * Normally this will be an enumeration like + * <pre> + * enum Attributes{owner, border, overlap}; + * </pre> + */ + typedef T Attribute; + /** + * @brief Constructor. + * + * The local index will be initialized to 0. + * @param attribute The attribute of the index. + * @param isPublic True if the index might also be + * known to other processes. + */ + ParallelLocalIndex(const Attribute& attribute, bool isPublic); + + /** + * @brief Constructor. + * + * @param localIndex The local index. + * @param attribute The attribute of the index. + * @param isPublic True if the index might also be + * known to other processes. + */ + ParallelLocalIndex(size_t localIndex, const Attribute& attribute, bool isPublic=true); + /** + * @brief Parameterless constructor. + * + * Needed for use in container classes. + */ + ParallelLocalIndex(); #if 0 -/** -* @brief Constructor. -* @param globalIndex The global index. -* @param attribute The attribute of the index. -* @param local The local index. -* @param isPublic True if the index might also be -* known to other processes. -* -*/ -ParallelLocalIndex(const Attribute& attribute, size_t local, bool isPublic); + /** + * @brief Constructor. + * @param globalIndex The global index. + * @param attribute The attribute of the index. + * @param local The local index. + * @param isPublic True if the index might also be + * known to other processes. + * + */ + ParallelLocalIndex(const Attribute& attribute, size_t local, bool isPublic); #endif -/** -* @brief Get the attribute of the index. -* @return The associated attribute. -*/ -inline const Attribute attribute() const; - -/** -* @brief Set the attribute of the index. -* @param attribute The associated attribute. -*/ -inline void setAttribute(const Attribute& attribute); - -/** -* @brief get the local index. -* @return The local index. -*/ -inline size_t local() const; - -/** -* @brief Convert to the local index represented by an int. -*/ -inline operator size_t() const; - -/** -* @brief Assign a new local index. -* -* @param index The new local index. -*/ -inline ParallelLocalIndex<Attribute>& operator=(size_t index); - -/** -* @brief Check whether the index might also be known other processes. -* @return True if the index might be known to other processors. -*/ -inline bool isPublic() const; - -/** -* @brief Get the state. -* @return The state. -*/ -inline LocalIndexState state() const; - -/** -* @brief Set the state. -* @param state The state to set. -*/ -inline void setState(const LocalIndexState& state); - -private: -/** @brief The local index. */ -size_t localIndex_; - -/** @brief An attribute for the index. */ -char attribute_; - -/** @brief True if the index is also known to other processors. */ -char public_; - -/** -* @brief The state of the index. -* -* Has to be one of LocalIndexState! -* @see LocalIndexState. -*/ -char state_; - -}; - -template<typename T> -bool operator==(const ParallelLocalIndex<T>& p1, -const ParallelLocalIndex<T>& p2) -{ -if(p1.local()!=p2.local()) -return false; -if(p1.attribute()!=p2.attribute()) -return false; -if(p1.isPublic()!=p2.isPublic()) -return false; -return true; -} -template<typename T> -bool operator!=(const ParallelLocalIndex<T>& p1, -const ParallelLocalIndex<T>& p2) -{ -return !(p1==p2); -} - - -template<typename T> -struct LocalIndexComparator<ParallelLocalIndex<T> > -{ -static bool compare(const ParallelLocalIndex<T>& t1, -const ParallelLocalIndex<T>& t2){ -return t1.attribute()<t2.attribute(); -} -}; + /** + * @brief Get the attribute of the index. + * @return The associated attribute. + */ + inline const Attribute attribute() const; + + /** + * @brief Set the attribute of the index. + * @param attribute The associated attribute. + */ + inline void setAttribute(const Attribute& attribute); + + /** + * @brief get the local index. + * @return The local index. + */ + inline size_t local() const; + + /** + * @brief Convert to the local index represented by an int. + */ + inline operator size_t() const; + + /** + * @brief Assign a new local index. + * + * @param index The new local index. + */ + inline ParallelLocalIndex<Attribute>& operator=(size_t index); + + /** + * @brief Check whether the index might also be known other processes. + * @return True if the index might be known to other processors. + */ + inline bool isPublic() const; + + /** + * @brief Get the state. + * @return The state. + */ + inline LocalIndexState state() const; + + /** + * @brief Set the state. + * @param state The state to set. + */ + inline void setState(const LocalIndexState& state); + + private: + /** @brief The local index. */ + size_t localIndex_; + + /** @brief An attribute for the index. */ + char attribute_; + + /** @brief True if the index is also known to other processors. */ + char public_; + + /** + * @brief The state of the index. + * + * Has to be one of LocalIndexState! + * @see LocalIndexState. + */ + char state_; + + }; + + template<typename T> + bool operator==(const ParallelLocalIndex<T>& p1, + const ParallelLocalIndex<T>& p2) + { + if(p1.local()!=p2.local()) + return false; + if(p1.attribute()!=p2.attribute()) + return false; + if(p1.isPublic()!=p2.isPublic()) + return false; + return true; + } + template<typename T> + bool operator!=(const ParallelLocalIndex<T>& p1, + const ParallelLocalIndex<T>& p2) + { + return !(p1==p2); + } + + + template<typename T> + struct LocalIndexComparator<ParallelLocalIndex<T> > + { + static bool compare(const ParallelLocalIndex<T>& t1, + const ParallelLocalIndex<T>& t2){ + return t1.attribute()<t2.attribute(); + } + }; #if HAVE_MPI -//! \todo Please doc me! -template<typename T> -class MPITraits<ParallelLocalIndex<T> > -{ -public: -static MPI_Datatype getType(); -private: -static MPI_Datatype type; + //! \todo Please doc me! + template<typename T> + class MPITraits<ParallelLocalIndex<T> > + { + public: + static MPI_Datatype getType(); + private: + static MPI_Datatype type; -}; + }; #endif -template<class T> -ParallelLocalIndex<T>::ParallelLocalIndex(const T& attribute, bool isPublic) -: localIndex_(0), attribute_(static_cast<char>(attribute)), -public_(static_cast<char>(isPublic)), state_(static_cast<char>(VALID)) -{} - - -template<class T> -ParallelLocalIndex<T>::ParallelLocalIndex(size_t local, const T& attribute, bool isPublic) -: localIndex_(local), attribute_(static_cast<char>(attribute)), -public_(static_cast<char>(isPublic)), state_(static_cast<char>(VALID)) -{} - -template<class T> -ParallelLocalIndex<T>::ParallelLocalIndex() -: localIndex_(0), attribute_(), public_(static_cast<char>(false)), -state_(static_cast<char>(VALID)) -{} - -template<class T> -inline const T ParallelLocalIndex<T>::attribute() const -{ -return T(attribute_); -} - -template<class T> -inline void -ParallelLocalIndex<T>::setAttribute(const Attribute& attribute) -{ -attribute_ = attribute; -} - -template<class T> -inline size_t ParallelLocalIndex<T>::local() const -{ -return localIndex_; -} - -template<class T> -inline ParallelLocalIndex<T>::operator size_t() const -{ -return localIndex_; -} - -template<class T> -inline ParallelLocalIndex<T>& -ParallelLocalIndex<T>::operator=(size_t index) -{ -localIndex_=index; -return *this; -} - -template<class T> -inline bool ParallelLocalIndex<T>::isPublic() const -{ -return static_cast<bool>(public_); -} - -template<class T> -inline LocalIndexState ParallelLocalIndex<T>::state() const -{ -return LocalIndexState(state_); -} - -template<class T> -inline void ParallelLocalIndex<T>::setState(const LocalIndexState& state) -{ -state_=static_cast<char>(state); -} + template<class T> + ParallelLocalIndex<T>::ParallelLocalIndex(const T& attribute, bool isPublic) + : localIndex_(0), attribute_(static_cast<char>(attribute)), + public_(static_cast<char>(isPublic)), state_(static_cast<char>(VALID)) + {} + + + template<class T> + ParallelLocalIndex<T>::ParallelLocalIndex(size_t local, const T& attribute, bool isPublic) + : localIndex_(local), attribute_(static_cast<char>(attribute)), + public_(static_cast<char>(isPublic)), state_(static_cast<char>(VALID)) + {} + + template<class T> + ParallelLocalIndex<T>::ParallelLocalIndex() + : localIndex_(0), attribute_(), public_(static_cast<char>(false)), + state_(static_cast<char>(VALID)) + {} + + template<class T> + inline const T ParallelLocalIndex<T>::attribute() const + { + return T(attribute_); + } + + template<class T> + inline void + ParallelLocalIndex<T>::setAttribute(const Attribute& attribute) + { + attribute_ = attribute; + } + + template<class T> + inline size_t ParallelLocalIndex<T>::local() const + { + return localIndex_; + } + + template<class T> + inline ParallelLocalIndex<T>::operator size_t() const + { + return localIndex_; + } + + template<class T> + inline ParallelLocalIndex<T>& + ParallelLocalIndex<T>::operator=(size_t index) + { + localIndex_=index; + return *this; + } + + template<class T> + inline bool ParallelLocalIndex<T>::isPublic() const + { + return static_cast<bool>(public_); + } + + template<class T> + inline LocalIndexState ParallelLocalIndex<T>::state() const + { + return LocalIndexState(state_); + } + + template<class T> + inline void ParallelLocalIndex<T>::setState(const LocalIndexState& state) + { + state_=static_cast<char>(state); + } #if HAVE_MPI -template<typename T> -MPI_Datatype MPITraits<ParallelLocalIndex<T> >::getType() -{ + template<typename T> + MPI_Datatype MPITraits<ParallelLocalIndex<T> >::getType() + { -if(type==MPI_DATATYPE_NULL) { -int length = 1; -MPI_Aint base, disp; -MPI_Datatype types[1] = {MPITraits<char>::getType()}; -ParallelLocalIndex<T> rep; -MPI_Get_address(&rep, &base); -MPI_Get_address(&(rep.attribute_), &disp); -disp -= base; + if(type==MPI_DATATYPE_NULL) { + int length = 1; + MPI_Aint base, disp; + MPI_Datatype types[1] = {MPITraits<char>::getType()}; + ParallelLocalIndex<T> rep; + MPI_Get_address(&rep, &base); + MPI_Get_address(&(rep.attribute_), &disp); + disp -= base; -MPI_Datatype tmp; -MPI_Type_create_struct(1, &length, &disp, types, &tmp); + MPI_Datatype tmp; + MPI_Type_create_struct(1, &length, &disp, types, &tmp); -MPI_Type_create_resized(tmp, 0, sizeof(ParallelLocalIndex<T>), &type); -MPI_Type_commit(&type); + MPI_Type_create_resized(tmp, 0, sizeof(ParallelLocalIndex<T>), &type); + MPI_Type_commit(&type); -MPI_Type_free(&tmp); -} -return type; -} + MPI_Type_free(&tmp); + } + return type; + } -template<typename T> -MPI_Datatype MPITraits<ParallelLocalIndex<T> >::type = MPI_DATATYPE_NULL; + template<typename T> + MPI_Datatype MPITraits<ParallelLocalIndex<T> >::type = MPI_DATATYPE_NULL; #endif -/** @} */ + /** @} */ } // namespace Dune #endif diff --git a/dune/common/parallel/remoteindices.hh b/dune/common/parallel/remoteindices.hh index 6d0b70cf8c3b951957d239daa7d51eab4e2d3e23..3d6ce476b8cbe92e870076159750c9615c897f07 100644 --- a/dune/common/parallel/remoteindices.hh +++ b/dune/common/parallel/remoteindices.hh @@ -25,1867 +25,1867 @@ #include <dune/common/stdstreams.hh> namespace Dune { -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Classes describing a distributed indexset -* @author Markus Blatt -*/ - -//! \todo Please doc me! -template<typename TG, typename TA> -class MPITraits<IndexPair<TG,ParallelLocalIndex<TA> > > -{ -public: -inline static MPI_Datatype getType(); -private: -static MPI_Datatype type; -}; - - -template<typename T, typename A> -class RemoteIndices; - -template<typename T1, typename T2> -class RemoteIndex; - -// forward declaration needed for friend declaration. -template<typename T> -class IndicesSyncer; - -template<typename T1, typename T2> -std::ostream& operator<<(std::ostream& os, const RemoteIndex<T1,T2>& index); - - -template<typename T, typename A, bool mode> -class RemoteIndexListModifier; - - -/** -* @brief Information about an index residing on another processor. -*/ -template<typename T1, typename T2> -class RemoteIndex -{ -template<typename T> -friend class IndicesSyncer; - -template<typename T, typename A, typename A1> -friend void repairLocalIndexPointers(std::map<int,SLList<std::pair<typename T::GlobalIndex, typename T::LocalIndex::Attribute>,A> >&, -RemoteIndices<T,A1>&, -const T&); - -template<typename T, typename A, bool mode> -friend class RemoteIndexListModifier; - -public: -/** -* @brief the type of the global index. -* This type has to provide at least a operator< for sorting. -*/ -typedef T1 GlobalIndex; -/** -* @brief The type of the attributes. -* Normally this will be an enumeration like -* \code -* enum Attributes{owner, border, overlap} -* \endcode -* e.g. OwnerOverlapCopyAttributes. -*/ -typedef T2 Attribute; - -/** -* @brief The type of the index pair. -*/ -typedef IndexPair<GlobalIndex,ParallelLocalIndex<Attribute> > -PairType; - -/** -* @brief Get the attribute of the index on the remote process. -* @return The remote attribute. -*/ -const Attribute attribute() const; - -/** -* @brief Get the corresponding local index pair. -* @return The corresponding local index pair. -*/ - -const PairType& localIndexPair() const; - -/** -* @brief Parameterless Constructor. -*/ -RemoteIndex(); - - -/** -* @brief Constructor. -* @param attribute The attribute of the index on the remote processor. -* @param local The corresponding local index. -*/ -RemoteIndex(const T2& attribute, -const PairType* local); - - -/** -* @brief Constructor. -* Private as it should only be called from within Indexset. -* @param attribute The attribute of the index on the remote processor. -*/ -RemoteIndex(const T2& attribute); - -bool operator==(const RemoteIndex& ri) const; - -bool operator!=(const RemoteIndex& ri) const; -private: -/** @brief The corresponding local index for this process. */ -const PairType* localIndex_; - -/** @brief The attribute of the index on the other process. */ -char attribute_; -}; - -template<class T, class A> -std::ostream& operator<<(std::ostream& os, const RemoteIndices<T,A>& indices); - -class InterfaceBuilder; - -template<class T, class A> -class CollectiveIterator; - -// forward declaration needed for friend declaration. -template<class T> -class IndicesSyncer; - -// forward declaration needed for friend declaration. -template<typename T1, typename T2> -class OwnerOverlapCopyCommunication; - - -/** -* @brief The indices present on remote processes. -* -* To set up communication between the set of processes active in -* the communication every process needs to know which -* indices are also known to other processes and which attributes -* are attached to them on the remote side. -* -* This information is managed by this class. The information can either -* be computed automatically calling rebuild (which requires information -* to be sent in a ring) or set up by hand using the -* RemoteIndexListModifiers returned by function getModifier(int). -* -* @tparam T The type of the underlying index set. -* @tparam A The type of the allocator to use. -*/ -template<class T, class A=std::allocator<RemoteIndex<typename T::GlobalIndex, -typename T::LocalIndex::Attribute> > > -class RemoteIndices -{ -friend class InterfaceBuilder; -friend class IndicesSyncer<T>; -template<typename T1, typename A2, typename A1> -friend void repairLocalIndexPointers(std::map<int,SLList<std::pair<typename T1::GlobalIndex, typename T1::LocalIndex::Attribute>,A2> >&, -RemoteIndices<T1,A1>&, -const T1&); - -template<class G, class T1, class T2> -friend void fillIndexSetHoles(const G& graph, Dune::OwnerOverlapCopyCommunication<T1,T2>& oocomm); -friend std::ostream& operator<<<>(std::ostream&, const RemoteIndices<T>&); - -public: - -/** -* @brief Type of the index set we use, e.g. ParallelLocalIndexSet. -*/ -typedef T ParallelIndexSet; - -/** -* @brief The type of the collective iterator over all remote indices. */ -typedef CollectiveIterator<T,A> CollectiveIteratorT; - -/** -* @brief The type of the global index. -*/ -typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; - - -/** -* @brief The type of the local index. -*/ -typedef typename ParallelIndexSet::LocalIndex LocalIndex; - -/** -* @brief The type of the attribute. -*/ -typedef typename LocalIndex::Attribute Attribute; - -/** -* @brief Type of the remote indices we manage. -*/ -typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; - - -/** -* @brief The type of the allocator for the remote index list. -*/ -using Allocator = typename std::allocator_traits<A>::template rebind_alloc<RemoteIndex>; - -/** @brief The type of the remote index list. */ -typedef Dune::SLList<RemoteIndex,Allocator> -RemoteIndexList; - -/** @brief The type of the map from rank to remote index list. */ -typedef std::map<int, std::pair<RemoteIndexList*,RemoteIndexList*> > -RemoteIndexMap; - -typedef typename RemoteIndexMap::const_iterator const_iterator; - -/** -* @brief Constructor. -* @param comm The communicator to use. -* @param source The indexset which represents the global to -* local mapping at the source of the communication -* @param destination The indexset to which the communication -* which represents the global to -* local mapping at the destination of the communication. -* May be the same as the source indexset. -* @param neighbours Optional: The neighbours the process shares indices with. -* If this parameter is omitted a ring communication with all indices will take -* place to calculate this information which is O(P). -* @param includeSelf If true, sending from indices of the processor to other -* indices on the same processor is enabled even if the same indexset is used -* on both the -* sending and receiving side. -*/ -inline RemoteIndices(const ParallelIndexSet& source, const ParallelIndexSet& destination, -const MPI_Comm& comm, const std::vector<int>& neighbours=std::vector<int>(), bool includeSelf=false); - -RemoteIndices(); - -/** -* @brief Tell whether sending from indices of the processor to other -* indices on the same processor is enabled even if the same indexset is -* used on both the sending and receiving side. -* -* @param includeSelf If true it is enabled. -*/ -void setIncludeSelf(bool includeSelf); - -/** -* @brief Set the index sets and communicator we work with. -* -* @warning All remote indices already setup will be deleted! -* -* @param comm The communicator to use. -* @param source The indexset which represents the global to -* local mapping at the source of the communication -* @param destination The indexset to which the communication -* which represents the global to -* local mapping at the destination of the communication. -* May be the same as the source indexset. -* @param neighbours Optional: The neighbours the process shares indices with. -* If this parameter is omitted a ring communication with all indices will take -* place to calculate this information which is O(P). -*/ -void setIndexSets(const ParallelIndexSet& source, const ParallelIndexSet& destination, -const MPI_Comm& comm, const std::vector<int>& neighbours=std::vector<int>()); - -template<typename C> -void setNeighbours(const C& neighbours) -{ -neighbourIds.clear(); -neighbourIds.insert(neighbours.begin(), neighbours.end()); - -} - -const std::set<int>& getNeighbours() const -{ -return neighbourIds; -} - -/** -* @brief Destructor. -*/ -~RemoteIndices(); - -/** -* @brief Rebuilds the set of remote indices. -* -* This has to be called whenever the underlying index sets -* change. -* -* If the template parameter ignorePublic is true all indices will be treated -* as public. -*/ -template<bool ignorePublic> -void rebuild(); - -bool operator==(const RemoteIndices& ri); - -/** -* @brief Checks whether the remote indices are synced with -* the indexsets. -* -* If they are not synced the remote indices need to be rebuild. -* @return True if they are synced. -*/ -inline bool isSynced() const; - -/** -* @brief Get the mpi communicator used. -*/ -inline MPI_Comm communicator() const; - -/** -* @brief Get a modifier for a remote index list. -* -* Sometimes the user knows in advance which indices will be present -* on other processors, too. Then he can set them up using this modifier. -* -* @warning Use with care. If the remote index list is inconsistent -* after the modification the communication might result in a dead lock! -* -* @tparam mode If true the index set corresponding to the remote indices might get modified. -* Therefore the internal pointers to the indices need to be repaired. -* @tparam send If true the remote index information at the sending side will -* be modified, if false the receiving side. -*/ -template<bool mode, bool send> -inline RemoteIndexListModifier<T,A,mode> getModifier(int process); - -/** -* @brief Find an iterator over the remote index lists of a specific process. -* @param proc The identifier of the process. -* @return The iterator the remote index lists postioned at the process. -* If theres is no list for this process, the end iterator is returned. -*/ -inline const_iterator find(int proc) const; - -/** -* @brief Get an iterator over all remote index lists. -* @return The iterator over all remote index lists postioned at the first process. -*/ -inline const_iterator begin() const; - -/** -* @brief Get an iterator over all remote index lists. -* @return The iterator over all remote index lists postioned at the end. -*/ -inline const_iterator end() const; - -/** -* @brief Get an iterator for colletively iterating over the remote indices of all remote processes. -*/ -template<bool send> -inline CollectiveIteratorT iterator() const; - -/** -* @brief Free the index lists. -*/ -inline void free(); - -/** -* @brief Get the number of processors we share indices with. -* @return The number of neighbours. -*/ -inline int neighbours() const; - -/** @brief Get the index set at the source. */ -inline const ParallelIndexSet& sourceIndexSet() const; - -/** @brief Get the index set at destination. */ -inline const ParallelIndexSet& destinationIndexSet() const; - -private: -/** copying is forbidden. */ -RemoteIndices(const RemoteIndices&) = delete; - -/** @brief Index set used at the source of the communication. */ -const ParallelIndexSet* source_; - -/** @brief Index set used at the destination of the communication. */ -const ParallelIndexSet* target_; - -/** @brief The communicator to use.*/ -MPI_Comm comm_; - -/** @brief The neighbours we share indices with. -* If not empty this will speedup rebuild. */ -std::set<int> neighbourIds; - -/** @brief The communicator tag to use. */ -const static int commTag_=333; - -/** -* @brief The sequence number of the source index set when the remote indices -* where build. -*/ -int sourceSeqNo_; - -/** -* @brief The sequence number of the destination index set when the remote indices -* where build. -*/ -int destSeqNo_; - -/** -* @brief Whether the public flag was ignored during the build. -*/ -bool publicIgnored; - -/** -* @brief Whether the next build will be the first build ever. -*/ -bool firstBuild; - -/* -* @brief If true, sending from indices of the processor to other -* indices on the same processor is enabled even if the same indexset is used -* on both the -* sending and receiving side. -*/ -bool includeSelf; - -/** @brief The index pair type. */ -typedef IndexPair<GlobalIndex, LocalIndex> -PairType; - -/** -* @brief The remote indices. -* -* The key is the process id and the values are the pair of remote -* index lists, the first for receiving, the second for sending. -*/ -RemoteIndexMap remoteIndices_; - -/** -* @brief Build the remote mapping. -* -* If the template parameter ignorePublic is true all indices will be treated -* as public. -* @param includeSelf If true, sending from indices of the processor to other -* indices on the same processor is enabled even if the same indexset is used -* on both the -* sending and receiving side. -*/ -template<bool ignorePublic> -inline void buildRemote(bool includeSelf); - -/** -* @brief Count the number of public indices in an index set. -* @param indexSet The index set whose indices we count. -* @return the number of indices marked as public. -*/ -inline int noPublic(const ParallelIndexSet& indexSet); - -/** -* @brief Pack the indices to send if source_ and target_ are the same. -* -* If the template parameter ignorePublic is true all indices will be treated -* as public. -* @param myPairs Array to store references to the public indices in. -* @param p_out The output buffer to pack the entries to. -* @param type The mpi datatype for the pairs. -* @param bufferSize The size of the output buffer p_out. -* @param position The position to start packing. -*/ -template<bool ignorePublic> -inline void packEntries(PairType** myPairs, const ParallelIndexSet& indexSet, -char* p_out, MPI_Datatype type, int bufferSize, -int* position, int n); - -/** -* @brief unpacks the received indices and builds the remote index list. -* -* @param remote The list to add the indices to. -* @param remoteEntries The number of remote entries to unpack. -* @param local The local indices to check whether we know the remote -* indices. -* @param localEntries The number of local indices. -* @param type The mpi data type for unpacking. -* @param p_in The input buffer to unpack from. -* @param position The position in the buffer to start unpacking from. -* @param bufferSize The size of the input buffer. -*/ -inline void unpackIndices(RemoteIndexList& remote, int remoteEntries, -PairType** local, int localEntries, char* p_in, -MPI_Datatype type, int* position, int bufferSize, -bool fromOurself); - -inline void unpackIndices(RemoteIndexList& send, RemoteIndexList& receive, -int remoteEntries, PairType** localSource, -int localSourceEntries, PairType** localDest, -int localDestEntries, char* p_in, -MPI_Datatype type, int* position, int bufferSize); - -void unpackCreateRemote(char* p_in, PairType** sourcePairs, PairType** DestPairs, -int remoteProc, int sourcePublish, int destPublish, -int bufferSize, bool sendTwo, bool fromOurSelf=false); -}; - -/** @} */ - -/** -* @brief Modifier for adding and/or deleting remote indices from -* the remote index list. -* -* In some cases all the information about the indices also present -* on remote process might already be known. In this case this -* information can be provided to the RemoteIndices via this modifier. -* This prevents the global communication needed by a call to -* RemoteIndices::rebuild. -* -* In some cases it might advisable to run IndicesSyncer::sync afterwards. -* -* @warning Use with care. If the indices are not consistent afterwards -* communication attempts might deadlock! -*/ -template<class T, class A, bool mode> -class RemoteIndexListModifier -{ - -template<typename T1, typename A1> -friend class RemoteIndices; - -public: -class InvalidPosition : public RangeError -{}; - -enum { -/** -* @brief If true the index set corresponding to the -* remote indices might get modified. -* -* If for example new indices are added to an index set -* all pointers of the remote indices to the local indices -* become invalid after ParallelIndexSet::endResize() was called. -*/ -MODIFYINDEXSET=mode -}; - -/** -* @brief Type of the index set we use. -*/ -typedef T ParallelIndexSet; - -/** -* @brief The type of the global index. -*/ -typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; - -/** -* @brief The type of the local index. -*/ -typedef typename ParallelIndexSet::LocalIndex LocalIndex; - -/** -* @brief The type of the attribute. -*/ -typedef typename LocalIndex::Attribute Attribute; - -/** -* @brief Type of the remote indices we manage. -*/ -typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; - -/** -* @brief The type of the allocator for the remote index list. -*/ -typedef A Allocator; - -/** @brief The type of the remote index list. */ -typedef Dune::SLList<RemoteIndex,Allocator> -RemoteIndexList; - -/** -* @brief The type of the modifying iterator of the remote index list. -*/ -typedef SLListModifyIterator<RemoteIndex,Allocator> ModifyIterator; - -/** -* @brief The type of the remote index list iterator. -*/ -typedef typename RemoteIndexList::const_iterator ConstIterator; - -/** -* @brief Insert an index to the list. -* -* Moves to the position where the index fits and inserts it. -* After the insertion only indices with an bigger global index -* than the inserted can be inserted. -* -* This method is only available if MODIFYINDEXSET is false. -* -* @param index The index to insert. -* @exception InvalidPosition Thrown if the index at the current position or -* the one before has bigger global index than the one to be inserted. -*/ -void insert(const RemoteIndex& index); - - -/** -* @brief Insert an index to the list. -* -* Moves to the position where the index fits and inserts it. -* After the insertion only indices with an bigger global index -* than the inserted can be inserted. -* -* This method is only available if MODIFYINDEXSET is true. -* -* @param index The index to insert. -* @param global The global index of the remote index. -* @exception InvalidPosition Thrown if the index at the current position or -* the one before has bigger global index than the one to be inserted. -*/ -void insert(const RemoteIndex& index, const GlobalIndex& global); - -/** -* @brief Remove a remote index. -* @param global The global index corresponding to the remote index. -* @return True If there was a corresponding remote index. -* @exception InvalidPostion If there was an insertion or deletion of -* a remote index corresponding to a bigger global index before. -*/ -bool remove(const GlobalIndex& global); - -/** -* @brief Repair the pointers to the local index pairs. -* -* Due to adding new indices or/and deleting indices in the -* index set all pointers to the local index pair might become -* invalid during ParallelIndexSet::endResize(). -* This method repairs them. -* -* @exception InvalidIndexSetState Thrown if the underlying -* index set is not in ParallelIndexSetState::GROUND mode (only when -* compiled with DUNE_ISTL_WITH_CHECKING!). -*/ -void repairLocalIndexPointers(); - - -RemoteIndexListModifier(const RemoteIndexListModifier&); - -/** -* @brief Default constructor. -* @warning Object is not usable! -*/ -RemoteIndexListModifier() -: glist_() -{} - -private: - -/** -* @brief Create a modifier for a remote index list. -* @param indexSet The set of indices the process knows. -* @param rList The list of remote indices to modify. -*/ -RemoteIndexListModifier(const ParallelIndexSet& indexSet, -RemoteIndexList& rList); - -typedef SLList<GlobalIndex,Allocator> GlobalList; -typedef typename GlobalList::ModifyIterator GlobalModifyIterator; -RemoteIndexList* rList_; -const ParallelIndexSet* indexSet_; -GlobalList glist_; -ModifyIterator iter_; -GlobalModifyIterator giter_; -ConstIterator end_; -bool first_; -GlobalIndex last_; -}; - -/** -* @brief A collective iterator for moving over the remote indices for -* all processes collectively. -*/ -template<class T, class A> -class CollectiveIterator -{ - -/** -* @brief Type of the index set we use. -*/ -typedef T ParallelIndexSet; - -/** -* @brief The type of the global index. -*/ -typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; - -/** -* @brief The type of the local index. -*/ -typedef typename ParallelIndexSet::LocalIndex LocalIndex; - -/** -* @brief The type of the attribute. -*/ -typedef typename LocalIndex::Attribute Attribute; - -/** @brief The remote index type */ -typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; - -/** @brief The allocator of the remote indices. */ -using Allocator = typename std::allocator_traits<A>::template rebind_alloc<RemoteIndex>; - -/** @brief The type of the remote index list. */ -typedef Dune::SLList<RemoteIndex,Allocator> RemoteIndexList; - -/** @brief The of map for storing the iterators. */ -typedef std::map<int,std::pair<typename RemoteIndexList::const_iterator, -const typename RemoteIndexList::const_iterator> > -Map; - -public: - -/** @brief The type of the map from rank to remote index list. */ -typedef std::map<int, std::pair<RemoteIndexList*,RemoteIndexList*> > -RemoteIndexMap; - -/** -* @brief Constructor. -* @param map_ The map of the remote indices. -* @param send True if we want iterate over the remote indices used for sending. -*/ -inline CollectiveIterator(const RemoteIndexMap& map_, bool send); - -/** -* @brief Advances all underlying iterators. -* -* All iterators are advanced until they point to a remote index whose -* global id is bigger or equal to global. -* Iterators pointing to their end are removed. -* @param global The index we search for. -*/ -inline void advance(const GlobalIndex& global); - -/** -* @brief Advances all underlying iterators. -* -* All iterators are advanced until they point to a remote index whose -* global id is bigger or equal to global. -* Iterators pointing to their end are removed. -* @param global The index we search for. -* @param attribute The attribute we search for. -*/ -inline void advance(const GlobalIndex& global, const Attribute& attribute); - -CollectiveIterator& operator++(); - -/** -* @brief Checks whether there are still iterators in the map. -*/ -inline bool empty(); - -/** -* @brief Iterator over the valid underlying iterators. -* -* An iterator is valid if it points to a remote index whose -* global id is equal to the one currently examined. -*/ -class iterator -{ -public: -typedef typename Map::iterator RealIterator; -typedef typename Map::iterator ConstRealIterator; - - -//! \todo Please doc me! -iterator(const RealIterator& iter, const ConstRealIterator& end, GlobalIndex& index) -: iter_(iter), end_(end), index_(index), hasAttribute(false) -{ -// Move to the first valid entry -while(iter_!=end_ && iter_->second.first->localIndexPair().global()!=index_) -++iter_; -} - -iterator(const RealIterator& iter, const ConstRealIterator& end, GlobalIndex index, -Attribute attribute) -: iter_(iter), end_(end), index_(index), attribute_(attribute), hasAttribute(true) -{ -// Move to the first valid entry or the end -while(iter_!=end_ && (iter_->second.first->localIndexPair().global()!=index_ -|| iter_->second.first->localIndexPair().local().attribute()!=attribute)) -++iter_; -} -//! \todo Please doc me! -iterator(const iterator& other) -: iter_(other.iter_), end_(other.end_), index_(other.index_) -{ } - -//! \todo Please doc me! -iterator& operator++() -{ -++iter_; -// If entry is not valid move on -while(iter_!=end_ && (iter_->second.first->localIndexPair().global()!=index_ || -(hasAttribute && -iter_->second.first->localIndexPair().local().attribute()!=attribute_))) -++iter_; -assert(iter_==end_ || -(iter_->second.first->localIndexPair().global()==index_)); -assert(iter_==end_ || !hasAttribute || -(iter_->second.first->localIndexPair().local().attribute()==attribute_)); -return *this; -} - -//! \todo Please doc me! -const RemoteIndex& operator*() const -{ -return *(iter_->second.first); -} - -//! \todo Please doc me! -int process() const -{ -return iter_->first; -} - -//! \todo Please doc me! -const RemoteIndex* operator->() const -{ -return iter_->second.first.operator->(); -} - -//! \todo Please doc me! -bool operator==(const iterator& other) const -{ -return other.iter_==iter_; -} - -//! \todo Please doc me! -bool operator!=(const iterator& other) const -{ -return other.iter_!=iter_; -} - -private: -iterator(); - -RealIterator iter_; -RealIterator end_; -GlobalIndex index_; -Attribute attribute_; -bool hasAttribute; -}; - -iterator begin(); - -iterator end(); - -private: - -Map map_; -GlobalIndex index_; -Attribute attribute_; -bool noattribute; -}; - -template<typename TG, typename TA> -MPI_Datatype MPITraits<IndexPair<TG,ParallelLocalIndex<TA> > >::getType() -{ -if(type==MPI_DATATYPE_NULL) { -int length[2] = {1, 1}; -MPI_Aint base; -MPI_Aint disp[2]; -MPI_Datatype types[2] = {MPITraits<TG>::getType(), -MPITraits<ParallelLocalIndex<TA> >::getType()}; -IndexPair<TG,ParallelLocalIndex<TA> > rep; -MPI_Get_address(&rep, &base); // lower bound of the datatype -MPI_Get_address(&(rep.global_), &disp[0]); -MPI_Get_address(&(rep.local_), &disp[1]); -for (MPI_Aint& d : disp) -d -= base; - -MPI_Datatype tmp; -MPI_Type_create_struct(2, length, disp, types, &tmp); - -MPI_Type_create_resized(tmp, 0, sizeof(IndexPair<TG,ParallelLocalIndex<TA> >), &type); -MPI_Type_commit(&type); - -MPI_Type_free(&tmp); -} -return type; -} - -template<typename TG, typename TA> -MPI_Datatype MPITraits<IndexPair<TG,ParallelLocalIndex<TA> > >::type=MPI_DATATYPE_NULL; - -template<typename T1, typename T2> -RemoteIndex<T1,T2>::RemoteIndex(const T2& attribute, const PairType* local) -: localIndex_(local), attribute_(static_cast<std::underlying_type_t<T2>>(attribute)) -{} - -template<typename T1, typename T2> -RemoteIndex<T1,T2>::RemoteIndex(const T2& attribute) -: localIndex_(0), attribute_(static_cast<std::underlying_type_t<T2>>(attribute)) -{} - -template<typename T1, typename T2> -RemoteIndex<T1,T2>::RemoteIndex() -: localIndex_(0), attribute_() -{} -template<typename T1, typename T2> -inline bool RemoteIndex<T1,T2>::operator==(const RemoteIndex& ri) const -{ -return localIndex_==ri.localIndex_ && attribute_==ri.attribute; -} - -template<typename T1, typename T2> -inline bool RemoteIndex<T1,T2>::operator!=(const RemoteIndex& ri) const -{ -return localIndex_!=ri.localIndex_ || attribute_!=ri.attribute_; -} - -template<typename T1, typename T2> -inline const T2 RemoteIndex<T1,T2>::attribute() const -{ -return T2(attribute_); -} - -template<typename T1, typename T2> -inline const IndexPair<T1,ParallelLocalIndex<T2> >& RemoteIndex<T1,T2>::localIndexPair() const -{ -return *localIndex_; -} - -template<typename T, typename A> -inline RemoteIndices<T,A>::RemoteIndices(const ParallelIndexSet& source, -const ParallelIndexSet& destination, -const MPI_Comm& comm, -const std::vector<int>& neighbours, -bool includeSelf_) -: source_(&source), target_(&destination), comm_(comm), -sourceSeqNo_(-1), destSeqNo_(-1), publicIgnored(false), firstBuild(true), -includeSelf(includeSelf_) -{ -setNeighbours(neighbours); -} - -template<typename T, typename A> -void RemoteIndices<T,A>::setIncludeSelf(bool b) -{ -includeSelf=b; -} - -template<typename T, typename A> -RemoteIndices<T,A>::RemoteIndices() -: source_(0), target_(0), sourceSeqNo_(-1), -destSeqNo_(-1), publicIgnored(false), firstBuild(true), -includeSelf(false) -{} - -template<class T, typename A> -void RemoteIndices<T,A>::setIndexSets(const ParallelIndexSet& source, -const ParallelIndexSet& destination, -const MPI_Comm& comm, -const std::vector<int>& neighbours) -{ -free(); -source_ = &source; -target_ = &destination; -comm_ = comm; -firstBuild = true; -setNeighbours(neighbours); -} - -template<typename T, typename A> -const typename RemoteIndices<T,A>::ParallelIndexSet& -RemoteIndices<T,A>::sourceIndexSet() const -{ -return *source_; -} - - -template<typename T, typename A> -const typename RemoteIndices<T,A>::ParallelIndexSet& -RemoteIndices<T,A>::destinationIndexSet() const -{ -return *target_; -} - - -template<typename T, typename A> -RemoteIndices<T,A>::~RemoteIndices() -{ -free(); -} - -template<typename T, typename A> -template<bool ignorePublic> -inline void RemoteIndices<T,A>::packEntries(IndexPair<GlobalIndex,LocalIndex>** pairs, -const ParallelIndexSet& indexSet, -char* p_out, MPI_Datatype type, -int bufferSize, -int *position, int n) -{ -DUNE_UNUSED_PARAMETER(n); -// fill with own indices -typedef typename ParallelIndexSet::const_iterator const_iterator; -typedef IndexPair<GlobalIndex,LocalIndex> PairType; -const const_iterator end = indexSet.end(); - -//Now pack the source indices -int i=0; -for(const_iterator index = indexSet.begin(); index != end; ++index) -if(ignorePublic || index->local().isPublic()) { - -MPI_Pack(const_cast<PairType*>(&(*index)), 1, -type, -p_out, bufferSize, position, comm_); -pairs[i++] = const_cast<PairType*>(&(*index)); - -} -assert(i==n); -} - -template<typename T, typename A> -inline int RemoteIndices<T,A>::noPublic(const ParallelIndexSet& indexSet) -{ -typedef typename ParallelIndexSet::const_iterator const_iterator; - -int noPublic=0; - -const const_iterator end=indexSet.end(); -for(const_iterator index=indexSet.begin(); index!=end; ++index) -if(index->local().isPublic()) -noPublic++; - -return noPublic; - -} - - -template<typename T, typename A> -inline void RemoteIndices<T,A>::unpackCreateRemote(char* p_in, PairType** sourcePairs, -PairType** destPairs, int remoteProc, -int sourcePublish, int destPublish, -int bufferSize, bool sendTwo, -bool fromOurSelf) -{ - -// unpack the number of indices we received -int noRemoteSource=-1, noRemoteDest=-1; -char twoIndexSets=0; -int position=0; -// Did we receive two index sets? -MPI_Unpack(p_in, bufferSize, &position, &twoIndexSets, 1, MPI_CHAR, comm_); -// The number of source indices received -MPI_Unpack(p_in, bufferSize, &position, &noRemoteSource, 1, MPI_INT, comm_); -// The number of destination indices received -MPI_Unpack(p_in, bufferSize, &position, &noRemoteDest, 1, MPI_INT, comm_); - - -// Indices for which we receive -RemoteIndexList* receive= new RemoteIndexList(); -// Indices for which we send -RemoteIndexList* send=0; - -MPI_Datatype type= MPITraits<PairType>::getType(); - -if(!twoIndexSets) { -if(sendTwo) { -send = new RemoteIndexList(); -// Create both remote index sets simultaneously -unpackIndices(*send, *receive, noRemoteSource, sourcePairs, sourcePublish, -destPairs, destPublish, p_in, type, &position, bufferSize); -}else{ -// we only need one list -unpackIndices(*receive, noRemoteSource, sourcePairs, sourcePublish, -p_in, type, &position, bufferSize, fromOurSelf); -send=receive; -} -}else{ - -int oldPos=position; -// Two index sets received -unpackIndices(*receive, noRemoteSource, destPairs, destPublish, -p_in, type, &position, bufferSize, fromOurSelf); -if(!sendTwo) -//unpack source entries again as destination entries -position=oldPos; - -send = new RemoteIndexList(); -unpackIndices(*send, noRemoteDest, sourcePairs, sourcePublish, -p_in, type, &position, bufferSize, fromOurSelf); -} - -if(receive->empty() && send->empty()) { -if(send==receive) { -delete send; -}else{ -delete send; -delete receive; -} -}else{ -remoteIndices_.insert(std::make_pair(remoteProc, -std::make_pair(send,receive))); -} -} - - -template<typename T, typename A> -template<bool ignorePublic> -inline void RemoteIndices<T,A>::buildRemote(bool includeSelf_) -{ -// Processor configuration -int rank, procs; -MPI_Comm_rank(comm_, &rank); -MPI_Comm_size(comm_, &procs); - -// number of local indices to publish -// The indices of the destination will be send. -int sourcePublish, destPublish; - -// Do we need to send two index sets? -char sendTwo = (source_ != target_); - -if(procs==1 && !(sendTwo || includeSelf_)) -// Nothing to communicate -return; - -sourcePublish = (ignorePublic) ? source_->size() : noPublic(*source_); - -if(sendTwo) -destPublish = (ignorePublic) ? target_->size() : noPublic(*target_); -else -// we only need to send one set of indices -destPublish = 0; - -int maxPublish, publish=sourcePublish+destPublish; - -// Calucate maximum number of indices send -MPI_Allreduce(&publish, &maxPublish, 1, MPI_INT, MPI_MAX, comm_); - -// allocate buffers -typedef IndexPair<GlobalIndex,LocalIndex> PairType; - -PairType** destPairs; -PairType** sourcePairs = new PairType*[sourcePublish>0 ? sourcePublish : 1]; - -if(sendTwo) -destPairs = new PairType*[destPublish>0 ? destPublish : 1]; -else -destPairs=sourcePairs; - -char** buffer = new char*[2]; -int bufferSize; -int position=0; -int intSize; -int charSize; - -// calculate buffer size -MPI_Datatype type = MPITraits<PairType>::getType(); - -MPI_Pack_size(maxPublish, type, comm_, -&bufferSize); -MPI_Pack_size(1, MPI_INT, comm_, -&intSize); -MPI_Pack_size(1, MPI_CHAR, comm_, -&charSize); -// Our message will contain the following: -// a bool whether two index sets where sent -// the size of the source and the dest indexset, -// then the source and destination indices -bufferSize += 2 * intSize + charSize; - -if(bufferSize<=0) bufferSize=1; - -buffer[0] = new char[bufferSize]; -buffer[1] = new char[bufferSize]; - - -// pack entries into buffer[0], p_out below! -MPI_Pack(&sendTwo, 1, MPI_CHAR, buffer[0], bufferSize, &position, -comm_); - -// The number of indices we send for each index set -MPI_Pack(&sourcePublish, 1, MPI_INT, buffer[0], bufferSize, &position, -comm_); -MPI_Pack(&destPublish, 1, MPI_INT, buffer[0], bufferSize, &position, -comm_); - -// Now pack the source indices and setup the destination pairs -packEntries<ignorePublic>(sourcePairs, *source_, buffer[0], type, -bufferSize, &position, sourcePublish); -// If necessary send the dest indices and setup the source pairs -if(sendTwo) -packEntries<ignorePublic>(destPairs, *target_, buffer[0], type, -bufferSize, &position, destPublish); - - -// Update remote indices for ourself -if(sendTwo|| includeSelf_) -unpackCreateRemote(buffer[0], sourcePairs, destPairs, rank, sourcePublish, -destPublish, bufferSize, sendTwo, includeSelf_); - -neighbourIds.erase(rank); - -if(neighbourIds.size()==0) -{ -Dune::dvverb<<rank<<": Sending messages in a ring"<<std::endl; -// send messages in ring -for(int proc=1; proc<procs; proc++) { -// pointers to the current input and output buffers -char* p_out = buffer[1-(proc%2)]; -char* p_in = buffer[proc%2]; - -MPI_Status status; -if(rank%2==0) { -MPI_Ssend(p_out, bufferSize, MPI_PACKED, (rank+1)%procs, -commTag_, comm_); -MPI_Recv(p_in, bufferSize, MPI_PACKED, (rank+procs-1)%procs, -commTag_, comm_, &status); -}else{ -MPI_Recv(p_in, bufferSize, MPI_PACKED, (rank+procs-1)%procs, -commTag_, comm_, &status); -MPI_Ssend(p_out, bufferSize, MPI_PACKED, (rank+1)%procs, -commTag_, comm_); -} - - -// The process these indices are from -int remoteProc = (rank+procs-proc)%procs; - -unpackCreateRemote(p_in, sourcePairs, destPairs, remoteProc, sourcePublish, -destPublish, bufferSize, sendTwo); - -} - -} -else -{ -MPI_Request* requests=new MPI_Request[neighbourIds.size()]; -MPI_Request* req=requests; - -typedef typename std::set<int>::size_type size_type; -size_type noNeighbours=neighbourIds.size(); - -// setup sends -for(std::set<int>::iterator neighbour=neighbourIds.begin(); -neighbour!= neighbourIds.end(); ++neighbour) { -// Only send the information to the neighbouring processors -MPI_Issend(buffer[0], position , MPI_PACKED, *neighbour, commTag_, comm_, req++); -} - -//Test for received messages - -for(size_type received=0; received <noNeighbours; ++received) -{ -MPI_Status status; -// probe for next message -MPI_Probe(MPI_ANY_SOURCE, commTag_, comm_, &status); -int remoteProc=status.MPI_SOURCE; -int size; -MPI_Get_count(&status, MPI_PACKED, &size); -// receive message -MPI_Recv(buffer[1], size, MPI_PACKED, remoteProc, -commTag_, comm_, &status); - -unpackCreateRemote(buffer[1], sourcePairs, destPairs, remoteProc, sourcePublish, -destPublish, bufferSize, sendTwo); -} -// wait for completion of pending requests -MPI_Status* statuses = new MPI_Status[neighbourIds.size()]; - -if(MPI_ERR_IN_STATUS==MPI_Waitall(neighbourIds.size(), requests, statuses)) { -for(size_type i=0; i < neighbourIds.size(); ++i) -if(statuses[i].MPI_ERROR!=MPI_SUCCESS) { -std::cerr<<rank<<": MPI_Error occurred while receiving message."<<std::endl; -MPI_Abort(comm_, 999); -} -} -delete[] requests; -delete[] statuses; -} - - -// delete allocated memory -if(destPairs!=sourcePairs) -delete[] destPairs; - -delete[] sourcePairs; -delete[] buffer[0]; -delete[] buffer[1]; -delete[] buffer; -} - -template<typename T, typename A> -inline void RemoteIndices<T,A>::unpackIndices(RemoteIndexList& remote, -int remoteEntries, -PairType** local, -int localEntries, -char* p_in, -MPI_Datatype type, -int* position, -int bufferSize, -bool fromOurSelf) -{ -if(remoteEntries==0) -return; - -PairType index; -MPI_Unpack(p_in, bufferSize, position, &index, 1, -type, comm_); -GlobalIndex oldGlobal=index.global(); -int n_in=0, localIndex=0; - -//Check if we know the global index -while(localIndex<localEntries) { -if(local[localIndex]->global()==index.global()) { -int oldLocalIndex=localIndex; - -while(localIndex<localEntries && -local[localIndex]->global()==index.global()) { -if(!fromOurSelf || index.local().attribute() != -local[localIndex]->local().attribute()) -// if index is from us it has to have a different attribute -remote.push_back(RemoteIndex(index.local().attribute(), -local[localIndex])); -localIndex++; -} - -// unpack next remote index -if((++n_in) < remoteEntries) { -MPI_Unpack(p_in, bufferSize, position, &index, 1, -type, comm_); -if(index.global()==oldGlobal) -// Restart comparison for the same global indices -localIndex=oldLocalIndex; -else -oldGlobal=index.global(); -}else{ -// No more received indices -break; -} -continue; -} - -if (local[localIndex]->global()<index.global()) { -// compare with next entry in our list -++localIndex; -}else{ -// We do not know the index, unpack next -if((++n_in) < remoteEntries) { -MPI_Unpack(p_in, bufferSize, position, &index, 1, -type, comm_); -oldGlobal=index.global(); -}else -// No more received indices -break; -} -} - -// Unpack the other received indices without doing anything -while(++n_in < remoteEntries) -MPI_Unpack(p_in, bufferSize, position, &index, 1, -type, comm_); -} - - -template<typename T, typename A> -inline void RemoteIndices<T,A>::unpackIndices(RemoteIndexList& send, -RemoteIndexList& receive, -int remoteEntries, -PairType** localSource, -int localSourceEntries, -PairType** localDest, -int localDestEntries, -char* p_in, -MPI_Datatype type, -int* position, -int bufferSize) -{ -int n_in=0, sourceIndex=0, destIndex=0; - -//Check if we know the global index -while(n_in<remoteEntries && (sourceIndex<localSourceEntries || destIndex<localDestEntries)) { -// Unpack next index -PairType index; -MPI_Unpack(p_in, bufferSize, position, &index, 1, -type, comm_); -n_in++; - -// Advance until global index in localSource and localDest are >= than the one in the unpacked index -while(sourceIndex<localSourceEntries && localSource[sourceIndex]->global()<index.global()) -sourceIndex++; - -while(destIndex<localDestEntries && localDest[destIndex]->global()<index.global()) -destIndex++; - -// Add a remote index if we found the global index. -if(sourceIndex<localSourceEntries && localSource[sourceIndex]->global()==index.global()) -send.push_back(RemoteIndex(index.local().attribute(), -localSource[sourceIndex])); - -if(destIndex < localDestEntries && localDest[destIndex]->global() == index.global()) -receive.push_back(RemoteIndex(index.local().attribute(), -localDest[sourceIndex])); -} - -} - -template<typename T, typename A> -inline void RemoteIndices<T,A>::free() -{ -typedef typename RemoteIndexMap::iterator Iterator; -Iterator lend = remoteIndices_.end(); -for(Iterator lists=remoteIndices_.begin(); lists != lend; ++lists) { -if(lists->second.first==lists->second.second) { -// there is only one remote index list. -delete lists->second.first; -}else{ -delete lists->second.first; -delete lists->second.second; -} -} -remoteIndices_.clear(); -firstBuild=true; -} - -template<typename T, typename A> -inline int RemoteIndices<T,A>::neighbours() const -{ -return remoteIndices_.size(); -} - -template<typename T, typename A> -template<bool ignorePublic> -inline void RemoteIndices<T,A>::rebuild() -{ -// Test whether a rebuild is Needed. -if(firstBuild || -ignorePublic!=publicIgnored || ! -isSynced()) { -free(); - -buildRemote<ignorePublic>(includeSelf); - -sourceSeqNo_ = source_->seqNo(); -destSeqNo_ = target_->seqNo(); -firstBuild=false; -publicIgnored=ignorePublic; -} - - -} - -template<typename T, typename A> -inline bool RemoteIndices<T,A>::isSynced() const -{ -return sourceSeqNo_==source_->seqNo() && destSeqNo_ ==target_->seqNo(); -} - -template<typename T, typename A> -template<bool mode, bool send> -RemoteIndexListModifier<T,A,mode> RemoteIndices<T,A>::getModifier(int process) -{ - -// The user are on their own now! -// We assume they know what they are doing and just set the -// remote indices to synced status. -sourceSeqNo_ = source_->seqNo(); -destSeqNo_ = target_->seqNo(); - -typename RemoteIndexMap::iterator found = remoteIndices_.find(process); - -if(found == remoteIndices_.end()) -{ -if(source_ != target_) -found = remoteIndices_.insert(found, std::make_pair(process, -std::make_pair(new RemoteIndexList(), -new RemoteIndexList()))); -else{ -RemoteIndexList* rlist = new RemoteIndexList(); -found = remoteIndices_.insert(found, -std::make_pair(process, -std::make_pair(rlist, rlist))); -} -} - -firstBuild = false; - -if(send) -return RemoteIndexListModifier<T,A,mode>(*source_, *(found->second.first)); -else -return RemoteIndexListModifier<T,A,mode>(*target_, *(found->second.second)); -} - -template<typename T, typename A> -inline typename RemoteIndices<T,A>::const_iterator -RemoteIndices<T,A>::find(int proc) const -{ -return remoteIndices_.find(proc); -} - -template<typename T, typename A> -inline typename RemoteIndices<T,A>::const_iterator -RemoteIndices<T,A>::begin() const -{ -return remoteIndices_.begin(); -} - -template<typename T, typename A> -inline typename RemoteIndices<T,A>::const_iterator -RemoteIndices<T,A>::end() const -{ -return remoteIndices_.end(); -} - - -template<typename T, typename A> -bool RemoteIndices<T,A>::operator==(const RemoteIndices& ri) -{ -if(neighbours()!=ri.neighbours()) -return false; - -typedef RemoteIndexList RList; -typedef typename std::map<int,std::pair<RList*,RList*> >::const_iterator const_iterator; - -const const_iterator rend = remoteIndices_.end(); - -for(const_iterator rindex = remoteIndices_.begin(), rindex1=ri.remoteIndices_.begin(); rindex!=rend; ++rindex, ++rindex1) { -if(rindex->first != rindex1->first) -return false; -if(*(rindex->second.first) != *(rindex1->second.first)) -return false; -if(*(rindex->second.second) != *(rindex1->second.second)) -return false; -} -return true; -} - -template<class T, class A, bool mode> -RemoteIndexListModifier<T,A,mode>::RemoteIndexListModifier(const ParallelIndexSet& indexSet, -RemoteIndexList& rList) -: rList_(&rList), indexSet_(&indexSet), iter_(rList.beginModify()), end_(rList.end()), first_(true) -{ -if(MODIFYINDEXSET) { -assert(indexSet_); -for(ConstIterator iter=iter_; iter != end_; ++iter) -glist_.push_back(iter->localIndexPair().global()); -giter_ = glist_.beginModify(); -} -} - -template<typename T, typename A, bool mode> -RemoteIndexListModifier<T,A,mode>::RemoteIndexListModifier(const RemoteIndexListModifier<T,A,mode>& other) -: rList_(other.rList_), indexSet_(other.indexSet_), -glist_(other.glist_), iter_(other.iter_), giter_(other.giter_), end_(other.end_), -first_(other.first_), last_(other.last_) -{} - -template<typename T, typename A, bool mode> -inline void RemoteIndexListModifier<T,A,mode>::repairLocalIndexPointers() -{ -if(MODIFYINDEXSET) { -// repair pointers to local index set. + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Classes describing a distributed indexset + * @author Markus Blatt + */ + + //! \todo Please doc me! + template<typename TG, typename TA> + class MPITraits<IndexPair<TG,ParallelLocalIndex<TA> > > + { + public: + inline static MPI_Datatype getType(); + private: + static MPI_Datatype type; + }; + + + template<typename T, typename A> + class RemoteIndices; + + template<typename T1, typename T2> + class RemoteIndex; + + // forward declaration needed for friend declaration. + template<typename T> + class IndicesSyncer; + + template<typename T1, typename T2> + std::ostream& operator<<(std::ostream& os, const RemoteIndex<T1,T2>& index); + + + template<typename T, typename A, bool mode> + class RemoteIndexListModifier; + + + /** + * @brief Information about an index residing on another processor. + */ + template<typename T1, typename T2> + class RemoteIndex + { + template<typename T> + friend class IndicesSyncer; + + template<typename T, typename A, typename A1> + friend void repairLocalIndexPointers(std::map<int,SLList<std::pair<typename T::GlobalIndex, typename T::LocalIndex::Attribute>,A> >&, + RemoteIndices<T,A1>&, + const T&); + + template<typename T, typename A, bool mode> + friend class RemoteIndexListModifier; + + public: + /** + * @brief the type of the global index. + * This type has to provide at least a operator< for sorting. + */ + typedef T1 GlobalIndex; + /** + * @brief The type of the attributes. + * Normally this will be an enumeration like + * \code + * enum Attributes{owner, border, overlap} + * \endcode + * e.g. OwnerOverlapCopyAttributes. + */ + typedef T2 Attribute; + + /** + * @brief The type of the index pair. + */ + typedef IndexPair<GlobalIndex,ParallelLocalIndex<Attribute> > + PairType; + + /** + * @brief Get the attribute of the index on the remote process. + * @return The remote attribute. + */ + const Attribute attribute() const; + + /** + * @brief Get the corresponding local index pair. + * @return The corresponding local index pair. + */ + + const PairType& localIndexPair() const; + + /** + * @brief Parameterless Constructor. + */ + RemoteIndex(); + + + /** + * @brief Constructor. + * @param attribute The attribute of the index on the remote processor. + * @param local The corresponding local index. + */ + RemoteIndex(const T2& attribute, + const PairType* local); + + + /** + * @brief Constructor. + * Private as it should only be called from within Indexset. + * @param attribute The attribute of the index on the remote processor. + */ + RemoteIndex(const T2& attribute); + + bool operator==(const RemoteIndex& ri) const; + + bool operator!=(const RemoteIndex& ri) const; + private: + /** @brief The corresponding local index for this process. */ + const PairType* localIndex_; + + /** @brief The attribute of the index on the other process. */ + char attribute_; + }; + + template<class T, class A> + std::ostream& operator<<(std::ostream& os, const RemoteIndices<T,A>& indices); + + class InterfaceBuilder; + + template<class T, class A> + class CollectiveIterator; + + // forward declaration needed for friend declaration. + template<class T> + class IndicesSyncer; + + // forward declaration needed for friend declaration. + template<typename T1, typename T2> + class OwnerOverlapCopyCommunication; + + + /** + * @brief The indices present on remote processes. + * + * To set up communication between the set of processes active in + * the communication every process needs to know which + * indices are also known to other processes and which attributes + * are attached to them on the remote side. + * + * This information is managed by this class. The information can either + * be computed automatically calling rebuild (which requires information + * to be sent in a ring) or set up by hand using the + * RemoteIndexListModifiers returned by function getModifier(int). + * + * @tparam T The type of the underlying index set. + * @tparam A The type of the allocator to use. + */ + template<class T, class A=std::allocator<RemoteIndex<typename T::GlobalIndex, + typename T::LocalIndex::Attribute> > > + class RemoteIndices + { + friend class InterfaceBuilder; + friend class IndicesSyncer<T>; + template<typename T1, typename A2, typename A1> + friend void repairLocalIndexPointers(std::map<int,SLList<std::pair<typename T1::GlobalIndex, typename T1::LocalIndex::Attribute>,A2> >&, + RemoteIndices<T1,A1>&, + const T1&); + + template<class G, class T1, class T2> + friend void fillIndexSetHoles(const G& graph, Dune::OwnerOverlapCopyCommunication<T1,T2>& oocomm); + friend std::ostream& operator<<<>(std::ostream&, const RemoteIndices<T>&); + + public: + + /** + * @brief Type of the index set we use, e.g. ParallelLocalIndexSet. + */ + typedef T ParallelIndexSet; + + /** + * @brief The type of the collective iterator over all remote indices. */ + typedef CollectiveIterator<T,A> CollectiveIteratorT; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename LocalIndex::Attribute Attribute; + + /** + * @brief Type of the remote indices we manage. + */ + typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; + + + /** + * @brief The type of the allocator for the remote index list. + */ + using Allocator = typename std::allocator_traits<A>::template rebind_alloc<RemoteIndex>; + + /** @brief The type of the remote index list. */ + typedef Dune::SLList<RemoteIndex,Allocator> + RemoteIndexList; + + /** @brief The type of the map from rank to remote index list. */ + typedef std::map<int, std::pair<RemoteIndexList*,RemoteIndexList*> > + RemoteIndexMap; + + typedef typename RemoteIndexMap::const_iterator const_iterator; + + /** + * @brief Constructor. + * @param comm The communicator to use. + * @param source The indexset which represents the global to + * local mapping at the source of the communication + * @param destination The indexset to which the communication + * which represents the global to + * local mapping at the destination of the communication. + * May be the same as the source indexset. + * @param neighbours Optional: The neighbours the process shares indices with. + * If this parameter is omitted a ring communication with all indices will take + * place to calculate this information which is O(P). + * @param includeSelf If true, sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is used + * on both the + * sending and receiving side. + */ + inline RemoteIndices(const ParallelIndexSet& source, const ParallelIndexSet& destination, + const MPI_Comm& comm, const std::vector<int>& neighbours=std::vector<int>(), bool includeSelf=false); + + RemoteIndices(); + + /** + * @brief Tell whether sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is + * used on both the sending and receiving side. + * + * @param includeSelf If true it is enabled. + */ + void setIncludeSelf(bool includeSelf); + + /** + * @brief Set the index sets and communicator we work with. + * + * @warning All remote indices already setup will be deleted! + * + * @param comm The communicator to use. + * @param source The indexset which represents the global to + * local mapping at the source of the communication + * @param destination The indexset to which the communication + * which represents the global to + * local mapping at the destination of the communication. + * May be the same as the source indexset. + * @param neighbours Optional: The neighbours the process shares indices with. + * If this parameter is omitted a ring communication with all indices will take + * place to calculate this information which is O(P). + */ + void setIndexSets(const ParallelIndexSet& source, const ParallelIndexSet& destination, + const MPI_Comm& comm, const std::vector<int>& neighbours=std::vector<int>()); + + template<typename C> + void setNeighbours(const C& neighbours) + { + neighbourIds.clear(); + neighbourIds.insert(neighbours.begin(), neighbours.end()); + + } + + const std::set<int>& getNeighbours() const + { + return neighbourIds; + } + + /** + * @brief Destructor. + */ + ~RemoteIndices(); + + /** + * @brief Rebuilds the set of remote indices. + * + * This has to be called whenever the underlying index sets + * change. + * + * If the template parameter ignorePublic is true all indices will be treated + * as public. + */ + template<bool ignorePublic> + void rebuild(); + + bool operator==(const RemoteIndices& ri); + + /** + * @brief Checks whether the remote indices are synced with + * the indexsets. + * + * If they are not synced the remote indices need to be rebuild. + * @return True if they are synced. + */ + inline bool isSynced() const; + + /** + * @brief Get the mpi communicator used. + */ + inline MPI_Comm communicator() const; + + /** + * @brief Get a modifier for a remote index list. + * + * Sometimes the user knows in advance which indices will be present + * on other processors, too. Then he can set them up using this modifier. + * + * @warning Use with care. If the remote index list is inconsistent + * after the modification the communication might result in a dead lock! + * + * @tparam mode If true the index set corresponding to the remote indices might get modified. + * Therefore the internal pointers to the indices need to be repaired. + * @tparam send If true the remote index information at the sending side will + * be modified, if false the receiving side. + */ + template<bool mode, bool send> + inline RemoteIndexListModifier<T,A,mode> getModifier(int process); + + /** + * @brief Find an iterator over the remote index lists of a specific process. + * @param proc The identifier of the process. + * @return The iterator the remote index lists postioned at the process. + * If theres is no list for this process, the end iterator is returned. + */ + inline const_iterator find(int proc) const; + + /** + * @brief Get an iterator over all remote index lists. + * @return The iterator over all remote index lists postioned at the first process. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator over all remote index lists. + * @return The iterator over all remote index lists postioned at the end. + */ + inline const_iterator end() const; + + /** + * @brief Get an iterator for colletively iterating over the remote indices of all remote processes. + */ + template<bool send> + inline CollectiveIteratorT iterator() const; + + /** + * @brief Free the index lists. + */ + inline void free(); + + /** + * @brief Get the number of processors we share indices with. + * @return The number of neighbours. + */ + inline int neighbours() const; + + /** @brief Get the index set at the source. */ + inline const ParallelIndexSet& sourceIndexSet() const; + + /** @brief Get the index set at destination. */ + inline const ParallelIndexSet& destinationIndexSet() const; + + private: + /** copying is forbidden. */ + RemoteIndices(const RemoteIndices&) = delete; + + /** @brief Index set used at the source of the communication. */ + const ParallelIndexSet* source_; + + /** @brief Index set used at the destination of the communication. */ + const ParallelIndexSet* target_; + + /** @brief The communicator to use.*/ + MPI_Comm comm_; + + /** @brief The neighbours we share indices with. + * If not empty this will speedup rebuild. */ + std::set<int> neighbourIds; + + /** @brief The communicator tag to use. */ + const static int commTag_=333; + + /** + * @brief The sequence number of the source index set when the remote indices + * where build. + */ + int sourceSeqNo_; + + /** + * @brief The sequence number of the destination index set when the remote indices + * where build. + */ + int destSeqNo_; + + /** + * @brief Whether the public flag was ignored during the build. + */ + bool publicIgnored; + + /** + * @brief Whether the next build will be the first build ever. + */ + bool firstBuild; + + /* + * @brief If true, sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is used + * on both the + * sending and receiving side. + */ + bool includeSelf; + + /** @brief The index pair type. */ + typedef IndexPair<GlobalIndex, LocalIndex> + PairType; + + /** + * @brief The remote indices. + * + * The key is the process id and the values are the pair of remote + * index lists, the first for receiving, the second for sending. + */ + RemoteIndexMap remoteIndices_; + + /** + * @brief Build the remote mapping. + * + * If the template parameter ignorePublic is true all indices will be treated + * as public. + * @param includeSelf If true, sending from indices of the processor to other + * indices on the same processor is enabled even if the same indexset is used + * on both the + * sending and receiving side. + */ + template<bool ignorePublic> + inline void buildRemote(bool includeSelf); + + /** + * @brief Count the number of public indices in an index set. + * @param indexSet The index set whose indices we count. + * @return the number of indices marked as public. + */ + inline int noPublic(const ParallelIndexSet& indexSet); + + /** + * @brief Pack the indices to send if source_ and target_ are the same. + * + * If the template parameter ignorePublic is true all indices will be treated + * as public. + * @param myPairs Array to store references to the public indices in. + * @param p_out The output buffer to pack the entries to. + * @param type The mpi datatype for the pairs. + * @param bufferSize The size of the output buffer p_out. + * @param position The position to start packing. + */ + template<bool ignorePublic> + inline void packEntries(PairType** myPairs, const ParallelIndexSet& indexSet, + char* p_out, MPI_Datatype type, int bufferSize, + int* position, int n); + + /** + * @brief unpacks the received indices and builds the remote index list. + * + * @param remote The list to add the indices to. + * @param remoteEntries The number of remote entries to unpack. + * @param local The local indices to check whether we know the remote + * indices. + * @param localEntries The number of local indices. + * @param type The mpi data type for unpacking. + * @param p_in The input buffer to unpack from. + * @param position The position in the buffer to start unpacking from. + * @param bufferSize The size of the input buffer. + */ + inline void unpackIndices(RemoteIndexList& remote, int remoteEntries, + PairType** local, int localEntries, char* p_in, + MPI_Datatype type, int* position, int bufferSize, + bool fromOurself); + + inline void unpackIndices(RemoteIndexList& send, RemoteIndexList& receive, + int remoteEntries, PairType** localSource, + int localSourceEntries, PairType** localDest, + int localDestEntries, char* p_in, + MPI_Datatype type, int* position, int bufferSize); + + void unpackCreateRemote(char* p_in, PairType** sourcePairs, PairType** DestPairs, + int remoteProc, int sourcePublish, int destPublish, + int bufferSize, bool sendTwo, bool fromOurSelf=false); + }; + + /** @} */ + + /** + * @brief Modifier for adding and/or deleting remote indices from + * the remote index list. + * + * In some cases all the information about the indices also present + * on remote process might already be known. In this case this + * information can be provided to the RemoteIndices via this modifier. + * This prevents the global communication needed by a call to + * RemoteIndices::rebuild. + * + * In some cases it might advisable to run IndicesSyncer::sync afterwards. + * + * @warning Use with care. If the indices are not consistent afterwards + * communication attempts might deadlock! + */ + template<class T, class A, bool mode> + class RemoteIndexListModifier + { + + template<typename T1, typename A1> + friend class RemoteIndices; + + public: + class InvalidPosition : public RangeError + {}; + + enum { + /** + * @brief If true the index set corresponding to the + * remote indices might get modified. + * + * If for example new indices are added to an index set + * all pointers of the remote indices to the local indices + * become invalid after ParallelIndexSet::endResize() was called. + */ + MODIFYINDEXSET=mode + }; + + /** + * @brief Type of the index set we use. + */ + typedef T ParallelIndexSet; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename LocalIndex::Attribute Attribute; + + /** + * @brief Type of the remote indices we manage. + */ + typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; + + /** + * @brief The type of the allocator for the remote index list. + */ + typedef A Allocator; + + /** @brief The type of the remote index list. */ + typedef Dune::SLList<RemoteIndex,Allocator> + RemoteIndexList; + + /** + * @brief The type of the modifying iterator of the remote index list. + */ + typedef SLListModifyIterator<RemoteIndex,Allocator> ModifyIterator; + + /** + * @brief The type of the remote index list iterator. + */ + typedef typename RemoteIndexList::const_iterator ConstIterator; + + /** + * @brief Insert an index to the list. + * + * Moves to the position where the index fits and inserts it. + * After the insertion only indices with an bigger global index + * than the inserted can be inserted. + * + * This method is only available if MODIFYINDEXSET is false. + * + * @param index The index to insert. + * @exception InvalidPosition Thrown if the index at the current position or + * the one before has bigger global index than the one to be inserted. + */ + void insert(const RemoteIndex& index); + + + /** + * @brief Insert an index to the list. + * + * Moves to the position where the index fits and inserts it. + * After the insertion only indices with an bigger global index + * than the inserted can be inserted. + * + * This method is only available if MODIFYINDEXSET is true. + * + * @param index The index to insert. + * @param global The global index of the remote index. + * @exception InvalidPosition Thrown if the index at the current position or + * the one before has bigger global index than the one to be inserted. + */ + void insert(const RemoteIndex& index, const GlobalIndex& global); + + /** + * @brief Remove a remote index. + * @param global The global index corresponding to the remote index. + * @return True If there was a corresponding remote index. + * @exception InvalidPostion If there was an insertion or deletion of + * a remote index corresponding to a bigger global index before. + */ + bool remove(const GlobalIndex& global); + + /** + * @brief Repair the pointers to the local index pairs. + * + * Due to adding new indices or/and deleting indices in the + * index set all pointers to the local index pair might become + * invalid during ParallelIndexSet::endResize(). + * This method repairs them. + * + * @exception InvalidIndexSetState Thrown if the underlying + * index set is not in ParallelIndexSetState::GROUND mode (only when + * compiled with DUNE_ISTL_WITH_CHECKING!). + */ + void repairLocalIndexPointers(); + + + RemoteIndexListModifier(const RemoteIndexListModifier&); + + /** + * @brief Default constructor. + * @warning Object is not usable! + */ + RemoteIndexListModifier() + : glist_() + {} + + private: + + /** + * @brief Create a modifier for a remote index list. + * @param indexSet The set of indices the process knows. + * @param rList The list of remote indices to modify. + */ + RemoteIndexListModifier(const ParallelIndexSet& indexSet, + RemoteIndexList& rList); + + typedef SLList<GlobalIndex,Allocator> GlobalList; + typedef typename GlobalList::ModifyIterator GlobalModifyIterator; + RemoteIndexList* rList_; + const ParallelIndexSet* indexSet_; + GlobalList glist_; + ModifyIterator iter_; + GlobalModifyIterator giter_; + ConstIterator end_; + bool first_; + GlobalIndex last_; + }; + + /** + * @brief A collective iterator for moving over the remote indices for + * all processes collectively. + */ + template<class T, class A> + class CollectiveIterator + { + + /** + * @brief Type of the index set we use. + */ + typedef T ParallelIndexSet; + + /** + * @brief The type of the global index. + */ + typedef typename ParallelIndexSet::GlobalIndex GlobalIndex; + + /** + * @brief The type of the local index. + */ + typedef typename ParallelIndexSet::LocalIndex LocalIndex; + + /** + * @brief The type of the attribute. + */ + typedef typename LocalIndex::Attribute Attribute; + + /** @brief The remote index type */ + typedef Dune::RemoteIndex<GlobalIndex,Attribute> RemoteIndex; + + /** @brief The allocator of the remote indices. */ + using Allocator = typename std::allocator_traits<A>::template rebind_alloc<RemoteIndex>; + + /** @brief The type of the remote index list. */ + typedef Dune::SLList<RemoteIndex,Allocator> RemoteIndexList; + + /** @brief The of map for storing the iterators. */ + typedef std::map<int,std::pair<typename RemoteIndexList::const_iterator, + const typename RemoteIndexList::const_iterator> > + Map; + + public: + + /** @brief The type of the map from rank to remote index list. */ + typedef std::map<int, std::pair<RemoteIndexList*,RemoteIndexList*> > + RemoteIndexMap; + + /** + * @brief Constructor. + * @param map_ The map of the remote indices. + * @param send True if we want iterate over the remote indices used for sending. + */ + inline CollectiveIterator(const RemoteIndexMap& map_, bool send); + + /** + * @brief Advances all underlying iterators. + * + * All iterators are advanced until they point to a remote index whose + * global id is bigger or equal to global. + * Iterators pointing to their end are removed. + * @param global The index we search for. + */ + inline void advance(const GlobalIndex& global); + + /** + * @brief Advances all underlying iterators. + * + * All iterators are advanced until they point to a remote index whose + * global id is bigger or equal to global. + * Iterators pointing to their end are removed. + * @param global The index we search for. + * @param attribute The attribute we search for. + */ + inline void advance(const GlobalIndex& global, const Attribute& attribute); + + CollectiveIterator& operator++(); + + /** + * @brief Checks whether there are still iterators in the map. + */ + inline bool empty(); + + /** + * @brief Iterator over the valid underlying iterators. + * + * An iterator is valid if it points to a remote index whose + * global id is equal to the one currently examined. + */ + class iterator + { + public: + typedef typename Map::iterator RealIterator; + typedef typename Map::iterator ConstRealIterator; + + + //! \todo Please doc me! + iterator(const RealIterator& iter, const ConstRealIterator& end, GlobalIndex& index) + : iter_(iter), end_(end), index_(index), hasAttribute(false) + { + // Move to the first valid entry + while(iter_!=end_ && iter_->second.first->localIndexPair().global()!=index_) + ++iter_; + } + + iterator(const RealIterator& iter, const ConstRealIterator& end, GlobalIndex index, + Attribute attribute) + : iter_(iter), end_(end), index_(index), attribute_(attribute), hasAttribute(true) + { + // Move to the first valid entry or the end + while(iter_!=end_ && (iter_->second.first->localIndexPair().global()!=index_ + || iter_->second.first->localIndexPair().local().attribute()!=attribute)) + ++iter_; + } + //! \todo Please doc me! + iterator(const iterator& other) + : iter_(other.iter_), end_(other.end_), index_(other.index_) + { } + + //! \todo Please doc me! + iterator& operator++() + { + ++iter_; + // If entry is not valid move on + while(iter_!=end_ && (iter_->second.first->localIndexPair().global()!=index_ || + (hasAttribute && + iter_->second.first->localIndexPair().local().attribute()!=attribute_))) + ++iter_; + assert(iter_==end_ || + (iter_->second.first->localIndexPair().global()==index_)); + assert(iter_==end_ || !hasAttribute || + (iter_->second.first->localIndexPair().local().attribute()==attribute_)); + return *this; + } + + //! \todo Please doc me! + const RemoteIndex& operator*() const + { + return *(iter_->second.first); + } + + //! \todo Please doc me! + int process() const + { + return iter_->first; + } + + //! \todo Please doc me! + const RemoteIndex* operator->() const + { + return iter_->second.first.operator->(); + } + + //! \todo Please doc me! + bool operator==(const iterator& other) const + { + return other.iter_==iter_; + } + + //! \todo Please doc me! + bool operator!=(const iterator& other) const + { + return other.iter_!=iter_; + } + + private: + iterator(); + + RealIterator iter_; + RealIterator end_; + GlobalIndex index_; + Attribute attribute_; + bool hasAttribute; + }; + + iterator begin(); + + iterator end(); + + private: + + Map map_; + GlobalIndex index_; + Attribute attribute_; + bool noattribute; + }; + + template<typename TG, typename TA> + MPI_Datatype MPITraits<IndexPair<TG,ParallelLocalIndex<TA> > >::getType() + { + if(type==MPI_DATATYPE_NULL) { + int length[2] = {1, 1}; + MPI_Aint base; + MPI_Aint disp[2]; + MPI_Datatype types[2] = {MPITraits<TG>::getType(), + MPITraits<ParallelLocalIndex<TA> >::getType()}; + IndexPair<TG,ParallelLocalIndex<TA> > rep; + MPI_Get_address(&rep, &base); // lower bound of the datatype + MPI_Get_address(&(rep.global_), &disp[0]); + MPI_Get_address(&(rep.local_), &disp[1]); + for (MPI_Aint& d : disp) + d -= base; + + MPI_Datatype tmp; + MPI_Type_create_struct(2, length, disp, types, &tmp); + + MPI_Type_create_resized(tmp, 0, sizeof(IndexPair<TG,ParallelLocalIndex<TA> >), &type); + MPI_Type_commit(&type); + + MPI_Type_free(&tmp); + } + return type; + } + + template<typename TG, typename TA> + MPI_Datatype MPITraits<IndexPair<TG,ParallelLocalIndex<TA> > >::type=MPI_DATATYPE_NULL; + + template<typename T1, typename T2> + RemoteIndex<T1,T2>::RemoteIndex(const T2& attribute, const PairType* local) + : localIndex_(local), attribute_(static_cast<std::underlying_type_t<T2>>(attribute)) + {} + + template<typename T1, typename T2> + RemoteIndex<T1,T2>::RemoteIndex(const T2& attribute) + : localIndex_(0), attribute_(static_cast<std::underlying_type_t<T2>>(attribute)) + {} + + template<typename T1, typename T2> + RemoteIndex<T1,T2>::RemoteIndex() + : localIndex_(0), attribute_() + {} + template<typename T1, typename T2> + inline bool RemoteIndex<T1,T2>::operator==(const RemoteIndex& ri) const + { + return localIndex_==ri.localIndex_ && attribute_==ri.attribute; + } + + template<typename T1, typename T2> + inline bool RemoteIndex<T1,T2>::operator!=(const RemoteIndex& ri) const + { + return localIndex_!=ri.localIndex_ || attribute_!=ri.attribute_; + } + + template<typename T1, typename T2> + inline const T2 RemoteIndex<T1,T2>::attribute() const + { + return T2(attribute_); + } + + template<typename T1, typename T2> + inline const IndexPair<T1,ParallelLocalIndex<T2> >& RemoteIndex<T1,T2>::localIndexPair() const + { + return *localIndex_; + } + + template<typename T, typename A> + inline RemoteIndices<T,A>::RemoteIndices(const ParallelIndexSet& source, + const ParallelIndexSet& destination, + const MPI_Comm& comm, + const std::vector<int>& neighbours, + bool includeSelf_) + : source_(&source), target_(&destination), comm_(comm), + sourceSeqNo_(-1), destSeqNo_(-1), publicIgnored(false), firstBuild(true), + includeSelf(includeSelf_) + { + setNeighbours(neighbours); + } + + template<typename T, typename A> + void RemoteIndices<T,A>::setIncludeSelf(bool b) + { + includeSelf=b; + } + + template<typename T, typename A> + RemoteIndices<T,A>::RemoteIndices() + : source_(0), target_(0), sourceSeqNo_(-1), + destSeqNo_(-1), publicIgnored(false), firstBuild(true), + includeSelf(false) + {} + + template<class T, typename A> + void RemoteIndices<T,A>::setIndexSets(const ParallelIndexSet& source, + const ParallelIndexSet& destination, + const MPI_Comm& comm, + const std::vector<int>& neighbours) + { + free(); + source_ = &source; + target_ = &destination; + comm_ = comm; + firstBuild = true; + setNeighbours(neighbours); + } + + template<typename T, typename A> + const typename RemoteIndices<T,A>::ParallelIndexSet& + RemoteIndices<T,A>::sourceIndexSet() const + { + return *source_; + } + + + template<typename T, typename A> + const typename RemoteIndices<T,A>::ParallelIndexSet& + RemoteIndices<T,A>::destinationIndexSet() const + { + return *target_; + } + + + template<typename T, typename A> + RemoteIndices<T,A>::~RemoteIndices() + { + free(); + } + + template<typename T, typename A> + template<bool ignorePublic> + inline void RemoteIndices<T,A>::packEntries(IndexPair<GlobalIndex,LocalIndex>** pairs, + const ParallelIndexSet& indexSet, + char* p_out, MPI_Datatype type, + int bufferSize, + int *position, int n) + { + DUNE_UNUSED_PARAMETER(n); + // fill with own indices + typedef typename ParallelIndexSet::const_iterator const_iterator; + typedef IndexPair<GlobalIndex,LocalIndex> PairType; + const const_iterator end = indexSet.end(); + + //Now pack the source indices + int i=0; + for(const_iterator index = indexSet.begin(); index != end; ++index) + if(ignorePublic || index->local().isPublic()) { + + MPI_Pack(const_cast<PairType*>(&(*index)), 1, + type, + p_out, bufferSize, position, comm_); + pairs[i++] = const_cast<PairType*>(&(*index)); + + } + assert(i==n); + } + + template<typename T, typename A> + inline int RemoteIndices<T,A>::noPublic(const ParallelIndexSet& indexSet) + { + typedef typename ParallelIndexSet::const_iterator const_iterator; + + int noPublic=0; + + const const_iterator end=indexSet.end(); + for(const_iterator index=indexSet.begin(); index!=end; ++index) + if(index->local().isPublic()) + noPublic++; + + return noPublic; + + } + + + template<typename T, typename A> + inline void RemoteIndices<T,A>::unpackCreateRemote(char* p_in, PairType** sourcePairs, + PairType** destPairs, int remoteProc, + int sourcePublish, int destPublish, + int bufferSize, bool sendTwo, + bool fromOurSelf) + { + + // unpack the number of indices we received + int noRemoteSource=-1, noRemoteDest=-1; + char twoIndexSets=0; + int position=0; + // Did we receive two index sets? + MPI_Unpack(p_in, bufferSize, &position, &twoIndexSets, 1, MPI_CHAR, comm_); + // The number of source indices received + MPI_Unpack(p_in, bufferSize, &position, &noRemoteSource, 1, MPI_INT, comm_); + // The number of destination indices received + MPI_Unpack(p_in, bufferSize, &position, &noRemoteDest, 1, MPI_INT, comm_); + + + // Indices for which we receive + RemoteIndexList* receive= new RemoteIndexList(); + // Indices for which we send + RemoteIndexList* send=0; + + MPI_Datatype type= MPITraits<PairType>::getType(); + + if(!twoIndexSets) { + if(sendTwo) { + send = new RemoteIndexList(); + // Create both remote index sets simultaneously + unpackIndices(*send, *receive, noRemoteSource, sourcePairs, sourcePublish, + destPairs, destPublish, p_in, type, &position, bufferSize); + }else{ + // we only need one list + unpackIndices(*receive, noRemoteSource, sourcePairs, sourcePublish, + p_in, type, &position, bufferSize, fromOurSelf); + send=receive; + } + }else{ + + int oldPos=position; + // Two index sets received + unpackIndices(*receive, noRemoteSource, destPairs, destPublish, + p_in, type, &position, bufferSize, fromOurSelf); + if(!sendTwo) + //unpack source entries again as destination entries + position=oldPos; + + send = new RemoteIndexList(); + unpackIndices(*send, noRemoteDest, sourcePairs, sourcePublish, + p_in, type, &position, bufferSize, fromOurSelf); + } + + if(receive->empty() && send->empty()) { + if(send==receive) { + delete send; + }else{ + delete send; + delete receive; + } + }else{ + remoteIndices_.insert(std::make_pair(remoteProc, + std::make_pair(send,receive))); + } + } + + + template<typename T, typename A> + template<bool ignorePublic> + inline void RemoteIndices<T,A>::buildRemote(bool includeSelf_) + { + // Processor configuration + int rank, procs; + MPI_Comm_rank(comm_, &rank); + MPI_Comm_size(comm_, &procs); + + // number of local indices to publish + // The indices of the destination will be send. + int sourcePublish, destPublish; + + // Do we need to send two index sets? + char sendTwo = (source_ != target_); + + if(procs==1 && !(sendTwo || includeSelf_)) + // Nothing to communicate + return; + + sourcePublish = (ignorePublic) ? source_->size() : noPublic(*source_); + + if(sendTwo) + destPublish = (ignorePublic) ? target_->size() : noPublic(*target_); + else + // we only need to send one set of indices + destPublish = 0; + + int maxPublish, publish=sourcePublish+destPublish; + + // Calucate maximum number of indices send + MPI_Allreduce(&publish, &maxPublish, 1, MPI_INT, MPI_MAX, comm_); + + // allocate buffers + typedef IndexPair<GlobalIndex,LocalIndex> PairType; + + PairType** destPairs; + PairType** sourcePairs = new PairType*[sourcePublish>0 ? sourcePublish : 1]; + + if(sendTwo) + destPairs = new PairType*[destPublish>0 ? destPublish : 1]; + else + destPairs=sourcePairs; + + char** buffer = new char*[2]; + int bufferSize; + int position=0; + int intSize; + int charSize; + + // calculate buffer size + MPI_Datatype type = MPITraits<PairType>::getType(); + + MPI_Pack_size(maxPublish, type, comm_, + &bufferSize); + MPI_Pack_size(1, MPI_INT, comm_, + &intSize); + MPI_Pack_size(1, MPI_CHAR, comm_, + &charSize); + // Our message will contain the following: + // a bool whether two index sets where sent + // the size of the source and the dest indexset, + // then the source and destination indices + bufferSize += 2 * intSize + charSize; + + if(bufferSize<=0) bufferSize=1; + + buffer[0] = new char[bufferSize]; + buffer[1] = new char[bufferSize]; + + + // pack entries into buffer[0], p_out below! + MPI_Pack(&sendTwo, 1, MPI_CHAR, buffer[0], bufferSize, &position, + comm_); + + // The number of indices we send for each index set + MPI_Pack(&sourcePublish, 1, MPI_INT, buffer[0], bufferSize, &position, + comm_); + MPI_Pack(&destPublish, 1, MPI_INT, buffer[0], bufferSize, &position, + comm_); + + // Now pack the source indices and setup the destination pairs + packEntries<ignorePublic>(sourcePairs, *source_, buffer[0], type, + bufferSize, &position, sourcePublish); + // If necessary send the dest indices and setup the source pairs + if(sendTwo) + packEntries<ignorePublic>(destPairs, *target_, buffer[0], type, + bufferSize, &position, destPublish); + + + // Update remote indices for ourself + if(sendTwo|| includeSelf_) + unpackCreateRemote(buffer[0], sourcePairs, destPairs, rank, sourcePublish, + destPublish, bufferSize, sendTwo, includeSelf_); + + neighbourIds.erase(rank); + + if(neighbourIds.size()==0) + { + Dune::dvverb<<rank<<": Sending messages in a ring"<<std::endl; + // send messages in ring + for(int proc=1; proc<procs; proc++) { + // pointers to the current input and output buffers + char* p_out = buffer[1-(proc%2)]; + char* p_in = buffer[proc%2]; + + MPI_Status status; + if(rank%2==0) { + MPI_Ssend(p_out, bufferSize, MPI_PACKED, (rank+1)%procs, + commTag_, comm_); + MPI_Recv(p_in, bufferSize, MPI_PACKED, (rank+procs-1)%procs, + commTag_, comm_, &status); + }else{ + MPI_Recv(p_in, bufferSize, MPI_PACKED, (rank+procs-1)%procs, + commTag_, comm_, &status); + MPI_Ssend(p_out, bufferSize, MPI_PACKED, (rank+1)%procs, + commTag_, comm_); + } + + + // The process these indices are from + int remoteProc = (rank+procs-proc)%procs; + + unpackCreateRemote(p_in, sourcePairs, destPairs, remoteProc, sourcePublish, + destPublish, bufferSize, sendTwo); + + } + + } + else + { + MPI_Request* requests=new MPI_Request[neighbourIds.size()]; + MPI_Request* req=requests; + + typedef typename std::set<int>::size_type size_type; + size_type noNeighbours=neighbourIds.size(); + + // setup sends + for(std::set<int>::iterator neighbour=neighbourIds.begin(); + neighbour!= neighbourIds.end(); ++neighbour) { + // Only send the information to the neighbouring processors + MPI_Issend(buffer[0], position , MPI_PACKED, *neighbour, commTag_, comm_, req++); + } + + //Test for received messages + + for(size_type received=0; received <noNeighbours; ++received) + { + MPI_Status status; + // probe for next message + MPI_Probe(MPI_ANY_SOURCE, commTag_, comm_, &status); + int remoteProc=status.MPI_SOURCE; + int size; + MPI_Get_count(&status, MPI_PACKED, &size); + // receive message + MPI_Recv(buffer[1], size, MPI_PACKED, remoteProc, + commTag_, comm_, &status); + + unpackCreateRemote(buffer[1], sourcePairs, destPairs, remoteProc, sourcePublish, + destPublish, bufferSize, sendTwo); + } + // wait for completion of pending requests + MPI_Status* statuses = new MPI_Status[neighbourIds.size()]; + + if(MPI_ERR_IN_STATUS==MPI_Waitall(neighbourIds.size(), requests, statuses)) { + for(size_type i=0; i < neighbourIds.size(); ++i) + if(statuses[i].MPI_ERROR!=MPI_SUCCESS) { + std::cerr<<rank<<": MPI_Error occurred while receiving message."<<std::endl; + MPI_Abort(comm_, 999); + } + } + delete[] requests; + delete[] statuses; + } + + + // delete allocated memory + if(destPairs!=sourcePairs) + delete[] destPairs; + + delete[] sourcePairs; + delete[] buffer[0]; + delete[] buffer[1]; + delete[] buffer; + } + + template<typename T, typename A> + inline void RemoteIndices<T,A>::unpackIndices(RemoteIndexList& remote, + int remoteEntries, + PairType** local, + int localEntries, + char* p_in, + MPI_Datatype type, + int* position, + int bufferSize, + bool fromOurSelf) + { + if(remoteEntries==0) + return; + + PairType index; + MPI_Unpack(p_in, bufferSize, position, &index, 1, + type, comm_); + GlobalIndex oldGlobal=index.global(); + int n_in=0, localIndex=0; + + //Check if we know the global index + while(localIndex<localEntries) { + if(local[localIndex]->global()==index.global()) { + int oldLocalIndex=localIndex; + + while(localIndex<localEntries && + local[localIndex]->global()==index.global()) { + if(!fromOurSelf || index.local().attribute() != + local[localIndex]->local().attribute()) + // if index is from us it has to have a different attribute + remote.push_back(RemoteIndex(index.local().attribute(), + local[localIndex])); + localIndex++; + } + + // unpack next remote index + if((++n_in) < remoteEntries) { + MPI_Unpack(p_in, bufferSize, position, &index, 1, + type, comm_); + if(index.global()==oldGlobal) + // Restart comparison for the same global indices + localIndex=oldLocalIndex; + else + oldGlobal=index.global(); + }else{ + // No more received indices + break; + } + continue; + } + + if (local[localIndex]->global()<index.global()) { + // compare with next entry in our list + ++localIndex; + }else{ + // We do not know the index, unpack next + if((++n_in) < remoteEntries) { + MPI_Unpack(p_in, bufferSize, position, &index, 1, + type, comm_); + oldGlobal=index.global(); + }else + // No more received indices + break; + } + } + + // Unpack the other received indices without doing anything + while(++n_in < remoteEntries) + MPI_Unpack(p_in, bufferSize, position, &index, 1, + type, comm_); + } + + + template<typename T, typename A> + inline void RemoteIndices<T,A>::unpackIndices(RemoteIndexList& send, + RemoteIndexList& receive, + int remoteEntries, + PairType** localSource, + int localSourceEntries, + PairType** localDest, + int localDestEntries, + char* p_in, + MPI_Datatype type, + int* position, + int bufferSize) + { + int n_in=0, sourceIndex=0, destIndex=0; + + //Check if we know the global index + while(n_in<remoteEntries && (sourceIndex<localSourceEntries || destIndex<localDestEntries)) { + // Unpack next index + PairType index; + MPI_Unpack(p_in, bufferSize, position, &index, 1, + type, comm_); + n_in++; + + // Advance until global index in localSource and localDest are >= than the one in the unpacked index + while(sourceIndex<localSourceEntries && localSource[sourceIndex]->global()<index.global()) + sourceIndex++; + + while(destIndex<localDestEntries && localDest[destIndex]->global()<index.global()) + destIndex++; + + // Add a remote index if we found the global index. + if(sourceIndex<localSourceEntries && localSource[sourceIndex]->global()==index.global()) + send.push_back(RemoteIndex(index.local().attribute(), + localSource[sourceIndex])); + + if(destIndex < localDestEntries && localDest[destIndex]->global() == index.global()) + receive.push_back(RemoteIndex(index.local().attribute(), + localDest[sourceIndex])); + } + + } + + template<typename T, typename A> + inline void RemoteIndices<T,A>::free() + { + typedef typename RemoteIndexMap::iterator Iterator; + Iterator lend = remoteIndices_.end(); + for(Iterator lists=remoteIndices_.begin(); lists != lend; ++lists) { + if(lists->second.first==lists->second.second) { + // there is only one remote index list. + delete lists->second.first; + }else{ + delete lists->second.first; + delete lists->second.second; + } + } + remoteIndices_.clear(); + firstBuild=true; + } + + template<typename T, typename A> + inline int RemoteIndices<T,A>::neighbours() const + { + return remoteIndices_.size(); + } + + template<typename T, typename A> + template<bool ignorePublic> + inline void RemoteIndices<T,A>::rebuild() + { + // Test whether a rebuild is Needed. + if(firstBuild || + ignorePublic!=publicIgnored || ! + isSynced()) { + free(); + + buildRemote<ignorePublic>(includeSelf); + + sourceSeqNo_ = source_->seqNo(); + destSeqNo_ = target_->seqNo(); + firstBuild=false; + publicIgnored=ignorePublic; + } + + + } + + template<typename T, typename A> + inline bool RemoteIndices<T,A>::isSynced() const + { + return sourceSeqNo_==source_->seqNo() && destSeqNo_ ==target_->seqNo(); + } + + template<typename T, typename A> + template<bool mode, bool send> + RemoteIndexListModifier<T,A,mode> RemoteIndices<T,A>::getModifier(int process) + { + + // The user are on their own now! + // We assume they know what they are doing and just set the + // remote indices to synced status. + sourceSeqNo_ = source_->seqNo(); + destSeqNo_ = target_->seqNo(); + + typename RemoteIndexMap::iterator found = remoteIndices_.find(process); + + if(found == remoteIndices_.end()) + { + if(source_ != target_) + found = remoteIndices_.insert(found, std::make_pair(process, + std::make_pair(new RemoteIndexList(), + new RemoteIndexList()))); + else{ + RemoteIndexList* rlist = new RemoteIndexList(); + found = remoteIndices_.insert(found, + std::make_pair(process, + std::make_pair(rlist, rlist))); + } + } + + firstBuild = false; + + if(send) + return RemoteIndexListModifier<T,A,mode>(*source_, *(found->second.first)); + else + return RemoteIndexListModifier<T,A,mode>(*target_, *(found->second.second)); + } + + template<typename T, typename A> + inline typename RemoteIndices<T,A>::const_iterator + RemoteIndices<T,A>::find(int proc) const + { + return remoteIndices_.find(proc); + } + + template<typename T, typename A> + inline typename RemoteIndices<T,A>::const_iterator + RemoteIndices<T,A>::begin() const + { + return remoteIndices_.begin(); + } + + template<typename T, typename A> + inline typename RemoteIndices<T,A>::const_iterator + RemoteIndices<T,A>::end() const + { + return remoteIndices_.end(); + } + + + template<typename T, typename A> + bool RemoteIndices<T,A>::operator==(const RemoteIndices& ri) + { + if(neighbours()!=ri.neighbours()) + return false; + + typedef RemoteIndexList RList; + typedef typename std::map<int,std::pair<RList*,RList*> >::const_iterator const_iterator; + + const const_iterator rend = remoteIndices_.end(); + + for(const_iterator rindex = remoteIndices_.begin(), rindex1=ri.remoteIndices_.begin(); rindex!=rend; ++rindex, ++rindex1) { + if(rindex->first != rindex1->first) + return false; + if(*(rindex->second.first) != *(rindex1->second.first)) + return false; + if(*(rindex->second.second) != *(rindex1->second.second)) + return false; + } + return true; + } + + template<class T, class A, bool mode> + RemoteIndexListModifier<T,A,mode>::RemoteIndexListModifier(const ParallelIndexSet& indexSet, + RemoteIndexList& rList) + : rList_(&rList), indexSet_(&indexSet), iter_(rList.beginModify()), end_(rList.end()), first_(true) + { + if(MODIFYINDEXSET) { + assert(indexSet_); + for(ConstIterator iter=iter_; iter != end_; ++iter) + glist_.push_back(iter->localIndexPair().global()); + giter_ = glist_.beginModify(); + } + } + + template<typename T, typename A, bool mode> + RemoteIndexListModifier<T,A,mode>::RemoteIndexListModifier(const RemoteIndexListModifier<T,A,mode>& other) + : rList_(other.rList_), indexSet_(other.indexSet_), + glist_(other.glist_), iter_(other.iter_), giter_(other.giter_), end_(other.end_), + first_(other.first_), last_(other.last_) + {} + + template<typename T, typename A, bool mode> + inline void RemoteIndexListModifier<T,A,mode>::repairLocalIndexPointers() + { + if(MODIFYINDEXSET) { + // repair pointers to local index set. #ifdef DUNE_ISTL_WITH_CHECKING -if(indexSet_->state()!=GROUND) -DUNE_THROW(InvalidIndexSetState, "Index has to be in ground mode for repairing pointers to indices"); + if(indexSet_->state()!=GROUND) + DUNE_THROW(InvalidIndexSetState, "Index has to be in ground mode for repairing pointers to indices"); #endif -typedef typename ParallelIndexSet::const_iterator IndexIterator; -typedef typename GlobalList::const_iterator GlobalIterator; -typedef typename RemoteIndexList::iterator Iterator; -GlobalIterator giter = glist_.begin(); -IndexIterator index = indexSet_->begin(); - -for(Iterator iter=rList_->begin(); iter != end_; ++iter) { -while(index->global()<*giter) { -++index; + typedef typename ParallelIndexSet::const_iterator IndexIterator; + typedef typename GlobalList::const_iterator GlobalIterator; + typedef typename RemoteIndexList::iterator Iterator; + GlobalIterator giter = glist_.begin(); + IndexIterator index = indexSet_->begin(); + + for(Iterator iter=rList_->begin(); iter != end_; ++iter) { + while(index->global()<*giter) { + ++index; #ifdef DUNE_ISTL_WITH_CHECKING -if(index == indexSet_->end()) -DUNE_THROW(InvalidPosition, "No such global index in set!"); + if(index == indexSet_->end()) + DUNE_THROW(InvalidPosition, "No such global index in set!"); #endif -} + } #ifdef DUNE_ISTL_WITH_CHECKING -if(index->global() != *giter) -DUNE_THROW(InvalidPosition, "No such global index in set!"); + if(index->global() != *giter) + DUNE_THROW(InvalidPosition, "No such global index in set!"); #endif -iter->localIndex_ = &(*index); -} -} -} - -template<typename T, typename A, bool mode> -inline void RemoteIndexListModifier<T,A,mode>::insert(const RemoteIndex& index) -{ -static_assert(!mode,"Not allowed if the mode indicates that new indices" -"might be added to the underlying index set. Use " -"insert(const RemoteIndex&, const GlobalIndex&) instead"); + iter->localIndex_ = &(*index); + } + } + } + + template<typename T, typename A, bool mode> + inline void RemoteIndexListModifier<T,A,mode>::insert(const RemoteIndex& index) + { + static_assert(!mode,"Not allowed if the mode indicates that new indices" + "might be added to the underlying index set. Use " + "insert(const RemoteIndex&, const GlobalIndex&) instead"); #ifdef DUNE_ISTL_WITH_CHECKING -if(!first_ && index.localIndexPair().global()<last_) -DUNE_THROW(InvalidPosition, "Modifcation of remote indices have to occur with ascending global index."); + if(!first_ && index.localIndexPair().global()<last_) + DUNE_THROW(InvalidPosition, "Modifcation of remote indices have to occur with ascending global index."); #endif -// Move to the correct position -while(iter_ != end_ && iter_->localIndexPair().global() < index.localIndexPair().global()) { -++iter_; -} - -// No duplicate entries allowed -assert(iter_==end_ || iter_->localIndexPair().global() != index.localIndexPair().global()); -iter_.insert(index); -last_ = index.localIndexPair().global(); -first_ = false; -} - -template<typename T, typename A, bool mode> -inline void RemoteIndexListModifier<T,A,mode>::insert(const RemoteIndex& index, const GlobalIndex& global) -{ -static_assert(mode,"Not allowed if the mode indicates that no new indices" -"might be added to the underlying index set. Use " -"insert(const RemoteIndex&) instead"); + // Move to the correct position + while(iter_ != end_ && iter_->localIndexPair().global() < index.localIndexPair().global()) { + ++iter_; + } + + // No duplicate entries allowed + assert(iter_==end_ || iter_->localIndexPair().global() != index.localIndexPair().global()); + iter_.insert(index); + last_ = index.localIndexPair().global(); + first_ = false; + } + + template<typename T, typename A, bool mode> + inline void RemoteIndexListModifier<T,A,mode>::insert(const RemoteIndex& index, const GlobalIndex& global) + { + static_assert(mode,"Not allowed if the mode indicates that no new indices" + "might be added to the underlying index set. Use " + "insert(const RemoteIndex&) instead"); #ifdef DUNE_ISTL_WITH_CHECKING -if(!first_ && global<last_) -DUNE_THROW(InvalidPosition, "Modification of remote indices have to occur with ascending global index."); + if(!first_ && global<last_) + DUNE_THROW(InvalidPosition, "Modification of remote indices have to occur with ascending global index."); #endif -// Move to the correct position -while(iter_ != end_ && *giter_ < global) { -++giter_; -++iter_; -} - -// No duplicate entries allowed -assert(iter_->localIndexPair().global() != global); -iter_.insert(index); -giter_.insert(global); - -last_ = global; -first_ = false; -} - -template<typename T, typename A, bool mode> -bool RemoteIndexListModifier<T,A,mode>::remove(const GlobalIndex& global) -{ + // Move to the correct position + while(iter_ != end_ && *giter_ < global) { + ++giter_; + ++iter_; + } + + // No duplicate entries allowed + assert(iter_->localIndexPair().global() != global); + iter_.insert(index); + giter_.insert(global); + + last_ = global; + first_ = false; + } + + template<typename T, typename A, bool mode> + bool RemoteIndexListModifier<T,A,mode>::remove(const GlobalIndex& global) + { #ifdef DUNE_ISTL_WITH_CHECKING -if(!first_ && global<last_) -DUNE_THROW(InvalidPosition, "Modifcation of remote indices have to occur with ascending global index."); + if(!first_ && global<last_) + DUNE_THROW(InvalidPosition, "Modifcation of remote indices have to occur with ascending global index."); #endif -bool found= false; - -if(MODIFYINDEXSET) { -// Move to the correct position -while(iter_!=end_ && *giter_< global) { -++giter_; -++iter_; -} -if(*giter_ == global) { -giter_.remove(); -iter_.remove(); -found=true; -} -}else{ -while(iter_!=end_ && iter_->localIndexPair().global() < global) -++iter_; - -if(iter_->localIndexPair().global()==global) { -iter_.remove(); -found = true; -} -} - -last_ = global; -first_ = false; -return found; -} - -template<typename T, typename A> -template<bool send> -inline typename RemoteIndices<T,A>::CollectiveIteratorT RemoteIndices<T,A>::iterator() const -{ -return CollectiveIterator<T,A>(remoteIndices_, send); -} - -template<typename T, typename A> -inline MPI_Comm RemoteIndices<T,A>::communicator() const -{ -return comm_; - -} - -template<typename T, typename A> -CollectiveIterator<T,A>::CollectiveIterator(const RemoteIndexMap& pmap, bool send) -{ -typedef typename RemoteIndexMap::const_iterator const_iterator; - -const const_iterator end=pmap.end(); -for(const_iterator process=pmap.begin(); process != end; ++process) { -const RemoteIndexList* list = send ? process->second.first : process->second.second; -typedef typename RemoteIndexList::const_iterator iterator; -map_.insert(std::make_pair(process->first, -std::pair<iterator, const iterator>(list->begin(), list->end()))); -} -} - -template<typename T, typename A> -inline void CollectiveIterator<T,A>::advance(const GlobalIndex& index) -{ -typedef typename Map::iterator iterator; -typedef typename Map::const_iterator const_iterator; -const const_iterator end = map_.end(); - -for(iterator iter = map_.begin(); iter != end;) { -// Step the iterator until we are >= index -typename RemoteIndexList::const_iterator current = iter->second.first; -typename RemoteIndexList::const_iterator rend = iter->second.second; -RemoteIndex remoteIndex; -if(current != rend) -remoteIndex = *current; - -while(iter->second.first!=iter->second.second && iter->second.first->localIndexPair().global()<index) -++(iter->second.first); - -// erase from the map if there are no more entries. -if(iter->second.first == iter->second.second) -map_.erase(iter++); -else{ -++iter; -} -} -index_=index; -noattribute=true; -} - -template<typename T, typename A> -inline void CollectiveIterator<T,A>::advance(const GlobalIndex& index, -const Attribute& attribute) -{ -typedef typename Map::iterator iterator; -typedef typename Map::const_iterator const_iterator; -const const_iterator end = map_.end(); - -for(iterator iter = map_.begin(); iter != end;) { -// Step the iterator until we are >= index -typename RemoteIndexList::const_iterator current = iter->second.first; -typename RemoteIndexList::const_iterator rend = iter->second.second; -RemoteIndex remoteIndex; -if(current != rend) -remoteIndex = *current; - -// Move to global index or bigger -while(iter->second.first!=iter->second.second && iter->second.first->localIndexPair().global()<index) -++(iter->second.first); - -// move to attribute or bigger -while(iter->second.first!=iter->second.second -&& iter->second.first->localIndexPair().global()==index -&& iter->second.first->localIndexPair().local().attribute()<attribute) -++(iter->second.first); - -// erase from the map if there are no more entries. -if(iter->second.first == iter->second.second) -map_.erase(iter++); -else{ -++iter; -} -} -index_=index; -attribute_=attribute; -noattribute=false; -} - -template<typename T, typename A> -inline CollectiveIterator<T,A>& CollectiveIterator<T,A>::operator++() -{ -typedef typename Map::iterator iterator; -typedef typename Map::const_iterator const_iterator; -const const_iterator end = map_.end(); - -for(iterator iter = map_.begin(); iter != end;) { -// Step the iterator until we are >= index -typename RemoteIndexList::const_iterator current = iter->second.first; -typename RemoteIndexList::const_iterator rend = iter->second.second; - -// move all iterators pointing to the current global index to next value -if(iter->second.first->localIndexPair().global()==index_ && -(noattribute || iter->second.first->localIndexPair().local().attribute() == attribute_)) -++(iter->second.first); - -// erase from the map if there are no more entries. -if(iter->second.first == iter->second.second) -map_.erase(iter++); -else{ -++iter; -} -} -return *this; -} - -template<typename T, typename A> -inline bool CollectiveIterator<T,A>::empty() -{ -return map_.empty(); -} - -template<typename T, typename A> -inline typename CollectiveIterator<T,A>::iterator -CollectiveIterator<T,A>::begin() -{ -if(noattribute) -return iterator(map_.begin(), map_.end(), index_); -else -return iterator(map_.begin(), map_.end(), index_, -attribute_); -} - -template<typename T, typename A> -inline typename CollectiveIterator<T,A>::iterator -CollectiveIterator<T,A>::end() -{ -return iterator(map_.end(), map_.end(), index_); -} - -template<typename TG, typename TA> -inline std::ostream& operator<<(std::ostream& os, const RemoteIndex<TG,TA>& index) -{ -os<<"[global="<<index.localIndexPair().global()<<", remote attribute="<<index.attribute()<<" local attribute="<<index.localIndexPair().local().attribute()<<"]"; -return os; -} - -template<typename T, typename A> -inline std::ostream& operator<<(std::ostream& os, const RemoteIndices<T,A>& indices) -{ -int rank; -MPI_Comm_rank(indices.comm_, &rank); - -typedef typename RemoteIndices<T,A>::RemoteIndexList RList; -typedef typename std::map<int,std::pair<RList*,RList*> >::const_iterator const_iterator; - -const const_iterator rend = indices.remoteIndices_.end(); - -for(const_iterator rindex = indices.remoteIndices_.begin(); rindex!=rend; ++rindex) { -os<<rank<<": Prozess "<<rindex->first<<":"; - -if(!rindex->second.first->empty()) { -os<<" send:"; - -const typename RList::const_iterator send= rindex->second.first->end(); - -for(typename RList::const_iterator index = rindex->second.first->begin(); -index != send; ++index) -os<<*index<<" "; -os<<std::endl; -} -if(!rindex->second.second->empty()) { -os<<rank<<": Prozess "<<rindex->first<<": "<<"receive: "; - -for(const auto& index : *(rindex->second.second)) -os << index << " "; -} -os<<std::endl<<std::flush; -} -return os; -} -/** @} */ + bool found= false; + + if(MODIFYINDEXSET) { + // Move to the correct position + while(iter_!=end_ && *giter_< global) { + ++giter_; + ++iter_; + } + if(*giter_ == global) { + giter_.remove(); + iter_.remove(); + found=true; + } + }else{ + while(iter_!=end_ && iter_->localIndexPair().global() < global) + ++iter_; + + if(iter_->localIndexPair().global()==global) { + iter_.remove(); + found = true; + } + } + + last_ = global; + first_ = false; + return found; + } + + template<typename T, typename A> + template<bool send> + inline typename RemoteIndices<T,A>::CollectiveIteratorT RemoteIndices<T,A>::iterator() const + { + return CollectiveIterator<T,A>(remoteIndices_, send); + } + + template<typename T, typename A> + inline MPI_Comm RemoteIndices<T,A>::communicator() const + { + return comm_; + + } + + template<typename T, typename A> + CollectiveIterator<T,A>::CollectiveIterator(const RemoteIndexMap& pmap, bool send) + { + typedef typename RemoteIndexMap::const_iterator const_iterator; + + const const_iterator end=pmap.end(); + for(const_iterator process=pmap.begin(); process != end; ++process) { + const RemoteIndexList* list = send ? process->second.first : process->second.second; + typedef typename RemoteIndexList::const_iterator iterator; + map_.insert(std::make_pair(process->first, + std::pair<iterator, const iterator>(list->begin(), list->end()))); + } + } + + template<typename T, typename A> + inline void CollectiveIterator<T,A>::advance(const GlobalIndex& index) + { + typedef typename Map::iterator iterator; + typedef typename Map::const_iterator const_iterator; + const const_iterator end = map_.end(); + + for(iterator iter = map_.begin(); iter != end;) { + // Step the iterator until we are >= index + typename RemoteIndexList::const_iterator current = iter->second.first; + typename RemoteIndexList::const_iterator rend = iter->second.second; + RemoteIndex remoteIndex; + if(current != rend) + remoteIndex = *current; + + while(iter->second.first!=iter->second.second && iter->second.first->localIndexPair().global()<index) + ++(iter->second.first); + + // erase from the map if there are no more entries. + if(iter->second.first == iter->second.second) + map_.erase(iter++); + else{ + ++iter; + } + } + index_=index; + noattribute=true; + } + + template<typename T, typename A> + inline void CollectiveIterator<T,A>::advance(const GlobalIndex& index, + const Attribute& attribute) + { + typedef typename Map::iterator iterator; + typedef typename Map::const_iterator const_iterator; + const const_iterator end = map_.end(); + + for(iterator iter = map_.begin(); iter != end;) { + // Step the iterator until we are >= index + typename RemoteIndexList::const_iterator current = iter->second.first; + typename RemoteIndexList::const_iterator rend = iter->second.second; + RemoteIndex remoteIndex; + if(current != rend) + remoteIndex = *current; + + // Move to global index or bigger + while(iter->second.first!=iter->second.second && iter->second.first->localIndexPair().global()<index) + ++(iter->second.first); + + // move to attribute or bigger + while(iter->second.first!=iter->second.second + && iter->second.first->localIndexPair().global()==index + && iter->second.first->localIndexPair().local().attribute()<attribute) + ++(iter->second.first); + + // erase from the map if there are no more entries. + if(iter->second.first == iter->second.second) + map_.erase(iter++); + else{ + ++iter; + } + } + index_=index; + attribute_=attribute; + noattribute=false; + } + + template<typename T, typename A> + inline CollectiveIterator<T,A>& CollectiveIterator<T,A>::operator++() + { + typedef typename Map::iterator iterator; + typedef typename Map::const_iterator const_iterator; + const const_iterator end = map_.end(); + + for(iterator iter = map_.begin(); iter != end;) { + // Step the iterator until we are >= index + typename RemoteIndexList::const_iterator current = iter->second.first; + typename RemoteIndexList::const_iterator rend = iter->second.second; + + // move all iterators pointing to the current global index to next value + if(iter->second.first->localIndexPair().global()==index_ && + (noattribute || iter->second.first->localIndexPair().local().attribute() == attribute_)) + ++(iter->second.first); + + // erase from the map if there are no more entries. + if(iter->second.first == iter->second.second) + map_.erase(iter++); + else{ + ++iter; + } + } + return *this; + } + + template<typename T, typename A> + inline bool CollectiveIterator<T,A>::empty() + { + return map_.empty(); + } + + template<typename T, typename A> + inline typename CollectiveIterator<T,A>::iterator + CollectiveIterator<T,A>::begin() + { + if(noattribute) + return iterator(map_.begin(), map_.end(), index_); + else + return iterator(map_.begin(), map_.end(), index_, + attribute_); + } + + template<typename T, typename A> + inline typename CollectiveIterator<T,A>::iterator + CollectiveIterator<T,A>::end() + { + return iterator(map_.end(), map_.end(), index_); + } + + template<typename TG, typename TA> + inline std::ostream& operator<<(std::ostream& os, const RemoteIndex<TG,TA>& index) + { + os<<"[global="<<index.localIndexPair().global()<<", remote attribute="<<index.attribute()<<" local attribute="<<index.localIndexPair().local().attribute()<<"]"; + return os; + } + + template<typename T, typename A> + inline std::ostream& operator<<(std::ostream& os, const RemoteIndices<T,A>& indices) + { + int rank; + MPI_Comm_rank(indices.comm_, &rank); + + typedef typename RemoteIndices<T,A>::RemoteIndexList RList; + typedef typename std::map<int,std::pair<RList*,RList*> >::const_iterator const_iterator; + + const const_iterator rend = indices.remoteIndices_.end(); + + for(const_iterator rindex = indices.remoteIndices_.begin(); rindex!=rend; ++rindex) { + os<<rank<<": Prozess "<<rindex->first<<":"; + + if(!rindex->second.first->empty()) { + os<<" send:"; + + const typename RList::const_iterator send= rindex->second.first->end(); + + for(typename RList::const_iterator index = rindex->second.first->begin(); + index != send; ++index) + os<<*index<<" "; + os<<std::endl; + } + if(!rindex->second.second->empty()) { + os<<rank<<": Prozess "<<rindex->first<<": "<<"receive: "; + + for(const auto& index : *(rindex->second.second)) + os << index << " "; + } + os<<std::endl<<std::flush; + } + return os; + } + /** @} */ } #endif // HAVE_MPI diff --git a/dune/common/parallel/selection.hh b/dune/common/parallel/selection.hh index 9ca24119ae9cb90b8a7af02cb8b2ff99481aa498..08656239512fdedbcb35d58169290776b12ff93b 100644 --- a/dune/common/parallel/selection.hh +++ b/dune/common/parallel/selection.hh @@ -9,333 +9,333 @@ namespace Dune { -/** @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief Provides classes for selecting -* indices based on attribute flags. -* @author Markus Blatt -*/ - -/** -* @brief A const iterator over an uncached selection. -*/ -template<typename TS, typename TG, typename TL, int N> -class SelectionIterator -{ -public: -/** -* @brief The type of the Set of attributes. -* -* It has to provide a static method -* \code bool contains(AttributeType a); \endcode -* that returns true if a is in the set. -* Such types are EnumItem, EnumRange, Combine. -*/ -typedef TS AttributeSet; - -/** -* @brief The type of the underlying index set. -*/ -typedef Dune::ParallelIndexSet<TG,TL,N> ParallelIndexSet; - -//typedef typename ParallelIndexSet::const_iterator ParallelIndexSetIterator; - -typedef ConstArrayListIterator<IndexPair<TG,TL>, N, std::allocator<Dune::IndexPair<TG,TL> > > ParallelIndexSetIterator; -/** -* @brief Constructor. -* @param iter The iterator over the index set. -* @param end The iterator over the index set positioned at the end. -*/ -SelectionIterator(const ParallelIndexSetIterator& iter, const ParallelIndexSetIterator& end) -: iter_(iter), end_(end) -{ -// Step to the first valid entry -while(iter_!=end_ && !AttributeSet::contains(iter_->local().attribute())) -++iter_; -} - -void operator++() -{ -assert(iter_!=end_); -for(++iter_; iter_!=end_; ++iter_) -if(AttributeSet::contains(iter_->local().attribute())) -break; -} - - -uint32_t operator*() const -{ -return iter_->local().local(); -} - -bool operator==(const SelectionIterator<TS,TG,TL,N>& other) const -{ -return iter_ == other.iter_; -} - -bool operator!=(const SelectionIterator<TS,TG,TL,N>& other) const -{ -return iter_ != other.iter_; -} - -private: -ParallelIndexSetIterator iter_; -const ParallelIndexSetIterator end_; -}; - - -/** -* @brief An uncached selection of indices. -*/ -template<typename TS, typename TG, typename TL, int N> -class UncachedSelection -{ -public: -/** -* @brief The type of the Set of attributes. -* -* It has to provide a static method -* \code bool contains(AttributeType a); \endcode -* that returns true if a is in the set. -* Such types are EnumItem, EnumRange, Combine. -*/ -typedef TS AttributeSet; - -/** -* @brief The type of the global index of the underlying index set. -*/ -typedef TG GlobalIndex; - -/** -* @brief The type of the local index of the underlying index set. -* -* It has to provide a function -* \code AttributeType attribute(); \endcode -*/ -typedef TL LocalIndex; - -/** -* @brief The type of the underlying index set. -*/ -typedef Dune::ParallelIndexSet<GlobalIndex,LocalIndex,N> ParallelIndexSet; - -/** -* @brief The type of the iterator of the selected indices. -*/ -typedef SelectionIterator<TS,TG,TL,N> iterator; - -/** -* @brief The type of the iterator of the selected indices. -*/ -typedef iterator const_iterator; - -UncachedSelection() -: indexSet_() -{} - -UncachedSelection(const ParallelIndexSet& indexset) -: indexSet_(&indexset) -{} -/** -* @brief Set the index set of the selection. -* @param indexset The index set to use. -*/ -void setIndexSet(const ParallelIndexSet& indexset); - -/** -* @brief Get the index set we are a selection for. -*/ -//const ParallelIndexSet& indexSet() const; - -/** -* @brief Get an iterator over the selected indices. -* @return An iterator positioned at the first selected index. -*/ -const_iterator begin() const; - -/** -* @brief Get an iterator over the selected indices. -* @return An iterator positioned at the first selected index. -*/ -const_iterator end() const; - - -private: -const ParallelIndexSet* indexSet_; - -}; - -/** -* @brief A cached selection of indices. -*/ -template<typename TS, typename TG, typename TL, int N> -class Selection -{ -public: -/** -* @brief The type of the set of attributes. -* -* It has to provide a static method -* \code bool contains(AttributeType a); \endcode -* that returns true if a is in the set. -* Such types are EnumItem, EnumRange, Combine. -*/ -typedef TS AttributeSet; - -/** -* @brief The type of the global index of the underlying index set. -*/ -typedef TG GlobalIndex; - -/** -* @brief The type of the local index of the underlying index set. -* -* It has to provide a function -* \code AttributeType attribute(); \endcode -*/ -typedef TL LocalIndex; - -/** -* @brief The type of the underlying index set. -*/ -typedef Dune::ParallelIndexSet<GlobalIndex,LocalIndex,N> ParallelIndexSet; - -/** -* @brief The type of the iterator of the selected indices. -*/ -typedef uint32_t* iterator; - -/** -* @brief The type of the iterator of the selected indices. -*/ -typedef uint32_t* const_iterator; - -Selection() -: selected_() -{} - -Selection(const ParallelIndexSet& indexset) -: selected_(), size_(0), built_(false) -{ -setIndexSet(indexset); -} - -~Selection(); - -/** -* @brief Set the index set of the selection. -* @param indexset The index set to use. -*/ -void setIndexSet(const ParallelIndexSet& indexset); - -/** -* @brief Free allocated memory. -*/ -void free(); - -/** -* @brief Get the index set we are a selection for. -*/ -//IndexSet indexSet() const; - -/** -* @brief Get an iterator over the selected indices. -* @return An iterator positioned at the first selected index. -*/ -const_iterator begin() const; - -/** -* @brief Get an iterator over the selected indices. -* @return An iterator positioned at the first selected index. -*/ -const_iterator end() const; - - -private: -uint32_t* selected_; -size_t size_; -bool built_; - -}; - -template<typename TS, typename TG, typename TL, int N> -inline void Selection<TS,TG,TL,N>::setIndexSet(const ParallelIndexSet& indexset) -{ -if(built_) -free(); - -// Count the number of entries the selection has to hold -typedef typename ParallelIndexSet::const_iterator const_iterator; -const const_iterator end = indexset.end(); -int entries = 0; - -for(const_iterator index = indexset.begin(); index != end; ++index) -if(AttributeSet::contains(index->local().attribute())) -++entries; - -selected_ = new uint32_t[entries]; -built_ = true; - -entries = 0; -for(const_iterator index = indexset.begin(); index != end; ++index) -if(AttributeSet::contains(index->local().attribute())) -selected_[entries++]= index->local().local(); - -size_=entries; -built_=true; -} - -template<typename TS, typename TG, typename TL, int N> -uint32_t* Selection<TS,TG,TL,N>::begin() const -{ -return selected_; -} - -template<typename TS, typename TG, typename TL, int N> -uint32_t* Selection<TS,TG,TL,N>::end() const -{ -return selected_+size_; -} - -template<typename TS, typename TG, typename TL, int N> -inline void Selection<TS,TG,TL,N>::free() -{ -delete[] selected_; -size_=0; -built_=false; -} - -template<typename TS, typename TG, typename TL, int N> -inline Selection<TS,TG,TL,N>::~Selection() -{ -if(built_) -free(); -} - -template<typename TS, typename TG, typename TL, int N> -SelectionIterator<TS,TG,TL,N> UncachedSelection<TS,TG,TL,N>::begin() const -{ -return SelectionIterator<TS,TG,TL,N>(indexSet_->begin(), -indexSet_->end()); -} - -template<typename TS, typename TG, typename TL, int N> -SelectionIterator<TS,TG,TL,N> UncachedSelection<TS,TG,TL,N>::end() const -{ -return SelectionIterator<TS,TG,TL,N>(indexSet_->end(), -indexSet_->end()); -} -template<typename TS, typename TG, typename TL, int N> -void UncachedSelection<TS,TG,TL,N>::setIndexSet(const ParallelIndexSet& indexset) -{ -indexSet_ = &indexset; -} - -/** @} */ + /** @addtogroup Common_Parallel + * + * @{ + */ + /** + * @file + * @brief Provides classes for selecting + * indices based on attribute flags. + * @author Markus Blatt + */ + + /** + * @brief A const iterator over an uncached selection. + */ + template<typename TS, typename TG, typename TL, int N> + class SelectionIterator + { + public: + /** + * @brief The type of the Set of attributes. + * + * It has to provide a static method + * \code bool contains(AttributeType a); \endcode + * that returns true if a is in the set. + * Such types are EnumItem, EnumRange, Combine. + */ + typedef TS AttributeSet; + + /** + * @brief The type of the underlying index set. + */ + typedef Dune::ParallelIndexSet<TG,TL,N> ParallelIndexSet; + + //typedef typename ParallelIndexSet::const_iterator ParallelIndexSetIterator; + + typedef ConstArrayListIterator<IndexPair<TG,TL>, N, std::allocator<Dune::IndexPair<TG,TL> > > ParallelIndexSetIterator; + /** + * @brief Constructor. + * @param iter The iterator over the index set. + * @param end The iterator over the index set positioned at the end. + */ + SelectionIterator(const ParallelIndexSetIterator& iter, const ParallelIndexSetIterator& end) + : iter_(iter), end_(end) + { + // Step to the first valid entry + while(iter_!=end_ && !AttributeSet::contains(iter_->local().attribute())) + ++iter_; + } + + void operator++() + { + assert(iter_!=end_); + for(++iter_; iter_!=end_; ++iter_) + if(AttributeSet::contains(iter_->local().attribute())) + break; + } + + + uint32_t operator*() const + { + return iter_->local().local(); + } + + bool operator==(const SelectionIterator<TS,TG,TL,N>& other) const + { + return iter_ == other.iter_; + } + + bool operator!=(const SelectionIterator<TS,TG,TL,N>& other) const + { + return iter_ != other.iter_; + } + + private: + ParallelIndexSetIterator iter_; + const ParallelIndexSetIterator end_; + }; + + + /** + * @brief An uncached selection of indices. + */ + template<typename TS, typename TG, typename TL, int N> + class UncachedSelection + { + public: + /** + * @brief The type of the Set of attributes. + * + * It has to provide a static method + * \code bool contains(AttributeType a); \endcode + * that returns true if a is in the set. + * Such types are EnumItem, EnumRange, Combine. + */ + typedef TS AttributeSet; + + /** + * @brief The type of the global index of the underlying index set. + */ + typedef TG GlobalIndex; + + /** + * @brief The type of the local index of the underlying index set. + * + * It has to provide a function + * \code AttributeType attribute(); \endcode + */ + typedef TL LocalIndex; + + /** + * @brief The type of the underlying index set. + */ + typedef Dune::ParallelIndexSet<GlobalIndex,LocalIndex,N> ParallelIndexSet; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef SelectionIterator<TS,TG,TL,N> iterator; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef iterator const_iterator; + + UncachedSelection() + : indexSet_() + {} + + UncachedSelection(const ParallelIndexSet& indexset) + : indexSet_(&indexset) + {} + /** + * @brief Set the index set of the selection. + * @param indexset The index set to use. + */ + void setIndexSet(const ParallelIndexSet& indexset); + + /** + * @brief Get the index set we are a selection for. + */ + //const ParallelIndexSet& indexSet() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator begin() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator end() const; + + + private: + const ParallelIndexSet* indexSet_; + + }; + + /** + * @brief A cached selection of indices. + */ + template<typename TS, typename TG, typename TL, int N> + class Selection + { + public: + /** + * @brief The type of the set of attributes. + * + * It has to provide a static method + * \code bool contains(AttributeType a); \endcode + * that returns true if a is in the set. + * Such types are EnumItem, EnumRange, Combine. + */ + typedef TS AttributeSet; + + /** + * @brief The type of the global index of the underlying index set. + */ + typedef TG GlobalIndex; + + /** + * @brief The type of the local index of the underlying index set. + * + * It has to provide a function + * \code AttributeType attribute(); \endcode + */ + typedef TL LocalIndex; + + /** + * @brief The type of the underlying index set. + */ + typedef Dune::ParallelIndexSet<GlobalIndex,LocalIndex,N> ParallelIndexSet; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef uint32_t* iterator; + + /** + * @brief The type of the iterator of the selected indices. + */ + typedef uint32_t* const_iterator; + + Selection() + : selected_() + {} + + Selection(const ParallelIndexSet& indexset) + : selected_(), size_(0), built_(false) + { + setIndexSet(indexset); + } + + ~Selection(); + + /** + * @brief Set the index set of the selection. + * @param indexset The index set to use. + */ + void setIndexSet(const ParallelIndexSet& indexset); + + /** + * @brief Free allocated memory. + */ + void free(); + + /** + * @brief Get the index set we are a selection for. + */ + //IndexSet indexSet() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator begin() const; + + /** + * @brief Get an iterator over the selected indices. + * @return An iterator positioned at the first selected index. + */ + const_iterator end() const; + + + private: + uint32_t* selected_; + size_t size_; + bool built_; + + }; + + template<typename TS, typename TG, typename TL, int N> + inline void Selection<TS,TG,TL,N>::setIndexSet(const ParallelIndexSet& indexset) + { + if(built_) + free(); + + // Count the number of entries the selection has to hold + typedef typename ParallelIndexSet::const_iterator const_iterator; + const const_iterator end = indexset.end(); + int entries = 0; + + for(const_iterator index = indexset.begin(); index != end; ++index) + if(AttributeSet::contains(index->local().attribute())) + ++entries; + + selected_ = new uint32_t[entries]; + built_ = true; + + entries = 0; + for(const_iterator index = indexset.begin(); index != end; ++index) + if(AttributeSet::contains(index->local().attribute())) + selected_[entries++]= index->local().local(); + + size_=entries; + built_=true; + } + + template<typename TS, typename TG, typename TL, int N> + uint32_t* Selection<TS,TG,TL,N>::begin() const + { + return selected_; + } + + template<typename TS, typename TG, typename TL, int N> + uint32_t* Selection<TS,TG,TL,N>::end() const + { + return selected_+size_; + } + + template<typename TS, typename TG, typename TL, int N> + inline void Selection<TS,TG,TL,N>::free() + { + delete[] selected_; + size_=0; + built_=false; + } + + template<typename TS, typename TG, typename TL, int N> + inline Selection<TS,TG,TL,N>::~Selection() + { + if(built_) + free(); + } + + template<typename TS, typename TG, typename TL, int N> + SelectionIterator<TS,TG,TL,N> UncachedSelection<TS,TG,TL,N>::begin() const + { + return SelectionIterator<TS,TG,TL,N>(indexSet_->begin(), + indexSet_->end()); + } + + template<typename TS, typename TG, typename TL, int N> + SelectionIterator<TS,TG,TL,N> UncachedSelection<TS,TG,TL,N>::end() const + { + return SelectionIterator<TS,TG,TL,N>(indexSet_->end(), + indexSet_->end()); + } + template<typename TS, typename TG, typename TL, int N> + void UncachedSelection<TS,TG,TL,N>::setIndexSet(const ParallelIndexSet& indexset) + { + indexSet_ = &indexset; + } + + /** @} */ } diff --git a/dune/common/parallel/variablesizecommunicator.hh b/dune/common/parallel/variablesizecommunicator.hh index 9c520aef8d2fcb229e6c07261d0fa83867e00856..d428d41657dbb16f840f52582aad9443a2f17d9a 100644 --- a/dune/common/parallel/variablesizecommunicator.hh +++ b/dune/common/parallel/variablesizecommunicator.hh @@ -23,562 +23,562 @@ #include <dune/common/unused.hh> /** -* @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @file -* @brief A communicator that only needs to know the number of elements per -* index at the sender side. -* @author Markus Blatt -* @} -*/ + * @addtogroup Common_Parallel + * + * @{ + */ +/** + * @file + * @brief A communicator that only needs to know the number of elements per + * index at the sender side. + * @author Markus Blatt + * @} + */ namespace Dune { namespace { /** -* @brief A message buffer. -* @tparam T The type of data that the buffer will hold. -*/ + * @brief A message buffer. + * @tparam T The type of data that the buffer will hold. + */ template<class T, class Allocator=std::allocator<T> > class MessageBuffer { public: -/** -* @brief Constructs a message. -* @param size The number of elements that buffer should hold, -*/ -explicit MessageBuffer(int size) -: buffer_(new T[size]), size_(size), position_(0) -{} -/** -* @brief Copy constructor. -* @param o The instance to copy. -*/ -explicit MessageBuffer(const MessageBuffer& o) -: buffer_(new T[o.size_]), size_(o.size_), position_(o.position_) -{ -} -/** @brief Destructor. */ -~MessageBuffer() -{ -delete[] buffer_; -} -/** -* @brief Write an item to the buffer. -* @param data The data item to write. -*/ -void write(const T& data) -{ -buffer_[position_++]=data; -} - -/** -* @brief Reads a data item from the buffer -* @param[out] data Reference to where to store the read data. -*/ -void read(T& data) -{ -data=buffer_[position_++]; -} - -/** -* @brief Reset the buffer. -* -* On return the buffer will be positioned at the start again. -*/ -void reset() -{ -position_=0; -} - -/** -* @brief Test whether the whole buffer was read. -* @return True if we read or wrot until the end of the buffer. -*/ -bool finished() -{ -return position_==size_; -} - -/** -* @brief Tests whether the buffer has enough space left to read/write data. -* @param notItems The number of items to read or write. -* @return True if there is enough space for noItems items. -*/ -bool hasSpaceForItems(int noItems) -{ -return position_+noItems<=size_; -} -/** -* @brief Get the size of the buffer. -* @return The number of elements the buffer can hold. -*/ -std::size_t size() const -{ -return size_; -} -/** -* @brief Converts the buffer to a C array. -* @return The underlying C array. -*/ -operator T*() -{ -return buffer_; -} + /** + * @brief Constructs a message. + * @param size The number of elements that buffer should hold, + */ + explicit MessageBuffer(int size) + : buffer_(new T[size]), size_(size), position_(0) + {} + /** + * @brief Copy constructor. + * @param o The instance to copy. + */ + explicit MessageBuffer(const MessageBuffer& o) + : buffer_(new T[o.size_]), size_(o.size_), position_(o.position_) + { + } + /** @brief Destructor. */ + ~MessageBuffer() + { + delete[] buffer_; + } + /** + * @brief Write an item to the buffer. + * @param data The data item to write. + */ + void write(const T& data) + { + buffer_[position_++]=data; + } + + /** + * @brief Reads a data item from the buffer + * @param[out] data Reference to where to store the read data. + */ + void read(T& data) + { + data=buffer_[position_++]; + } + + /** + * @brief Reset the buffer. + * + * On return the buffer will be positioned at the start again. + */ + void reset() + { + position_=0; + } + + /** + * @brief Test whether the whole buffer was read. + * @return True if we read or wrot until the end of the buffer. + */ + bool finished() + { + return position_==size_; + } + + /** + * @brief Tests whether the buffer has enough space left to read/write data. + * @param notItems The number of items to read or write. + * @return True if there is enough space for noItems items. + */ + bool hasSpaceForItems(int noItems) + { + return position_+noItems<=size_; + } + /** + * @brief Get the size of the buffer. + * @return The number of elements the buffer can hold. + */ + std::size_t size() const + { + return size_; + } + /** + * @brief Converts the buffer to a C array. + * @return The underlying C array. + */ + operator T*() + { + return buffer_; + } private: -/** -* @brief Pointer to the current insertion point of the buffer. -*/ -T* buffer_; -/** -* @brief The size of the buffer -*/ -std::size_t size_; -/** -* @brief The current position in the buffer. -*/ -std::size_t position_; + /** + * @brief Pointer to the current insertion point of the buffer. + */ + T* buffer_; + /** + * @brief The size of the buffer + */ + std::size_t size_; + /** + * @brief The current position in the buffer. + */ + std::size_t position_; }; /** -* @brief A tracker for the current position in a communication interface. -*/ + * @brief A tracker for the current position in a communication interface. + */ class InterfaceTracker { public: -/** -* @brief Constructor. -* @param rank The other rank that the interface communicates with. -* @param info A list of local indices belonging to this interface. -*/ -InterfaceTracker(int rank, InterfaceInformation info, std::size_t fixedsize=0, -bool allocateSizes=false) -: fixedSize(fixedsize),rank_(rank), index_(), interface_(info), sizes_() -{ -if(allocateSizes) -{ -sizes_.resize(info.size()); -} -} - -/** -* @brief Moves to the next index in the interface. -*/ -void moveToNextIndex() -{ -index_++; -assert(index_<=interface_.size()); -skipZeroIndices(); -} -/** -* @brief Increment index various times. -* @param i The number of times to increment. -*/ -void increment(std::size_t i) -{ -index_+=i; -assert(index_<=interface_.size()); -} -/** -* @brief Checks whether all indices have been visited. -* @return True if all indices have been visited. -*/ -bool finished() const -{ -return index_==interface_.size(); -} - -void skipZeroIndices() -{ -// skip indices with size zero! -while(sizes_.size() && index_!=interface_.size() &&!size()) -++index_; -} - -/** -* @brief Get the current local index of the interface. -* @return The current local index of the interface. -*/ -std::size_t index() const -{ -return interface_[index_]; -} -/** -* @brief Get the size at the current index. -*/ -std::size_t size() const -{ -assert(sizes_.size()); -return sizes_[index_]; -} -/** -* @brief Get a pointer to the array with the sizes. -*/ -std::size_t* getSizesPointer() -{ -return &sizes_[0]; -} -/** -* @brief Returns whether the interface is empty. -* @return True if there are no entries in the interface. -*/ -bool empty() const -{ -return !interface_.size(); -} - -/** -* @brief Checks whether there are still indices waiting to be processed. -* @return True if there are still indices waiting to be processed. -*/ -std::size_t indicesLeft() const -{ -return interface_.size()-index_; -} -/** -* @brief The number of data items per index if it is fixed, 0 otherwise. -*/ -std::size_t fixedSize; -/** -* @brief Get the process rank that this communication interface is with. -*/ -int rank() const -{ -return rank_; -} -/** -* @brief Get the offset to the first index. -*/ -std::size_t offset() const -{ -return index_; -} + /** + * @brief Constructor. + * @param rank The other rank that the interface communicates with. + * @param info A list of local indices belonging to this interface. + */ + InterfaceTracker(int rank, InterfaceInformation info, std::size_t fixedsize=0, + bool allocateSizes=false) + : fixedSize(fixedsize),rank_(rank), index_(), interface_(info), sizes_() + { + if(allocateSizes) + { + sizes_.resize(info.size()); + } + } + + /** + * @brief Moves to the next index in the interface. + */ + void moveToNextIndex() + { + index_++; + assert(index_<=interface_.size()); + skipZeroIndices(); + } + /** + * @brief Increment index various times. + * @param i The number of times to increment. + */ + void increment(std::size_t i) + { + index_+=i; + assert(index_<=interface_.size()); + } + /** + * @brief Checks whether all indices have been visited. + * @return True if all indices have been visited. + */ + bool finished() const + { + return index_==interface_.size(); + } + + void skipZeroIndices() + { + // skip indices with size zero! + while(sizes_.size() && index_!=interface_.size() &&!size()) + ++index_; + } + + /** + * @brief Get the current local index of the interface. + * @return The current local index of the interface. + */ + std::size_t index() const + { + return interface_[index_]; + } + /** + * @brief Get the size at the current index. + */ + std::size_t size() const + { + assert(sizes_.size()); + return sizes_[index_]; + } + /** + * @brief Get a pointer to the array with the sizes. + */ + std::size_t* getSizesPointer() + { + return &sizes_[0]; + } + /** + * @brief Returns whether the interface is empty. + * @return True if there are no entries in the interface. + */ + bool empty() const + { + return !interface_.size(); + } + + /** + * @brief Checks whether there are still indices waiting to be processed. + * @return True if there are still indices waiting to be processed. + */ + std::size_t indicesLeft() const + { + return interface_.size()-index_; + } + /** + * @brief The number of data items per index if it is fixed, 0 otherwise. + */ + std::size_t fixedSize; + /** + * @brief Get the process rank that this communication interface is with. + */ + int rank() const + { + return rank_; + } + /** + * @brief Get the offset to the first index. + */ + std::size_t offset() const + { + return index_; + } private: -/** @brief The process rank that this communication interface is with. */ -int rank_; -/** @brief The other rank that this interface communcates with. */ -std::size_t index_; -/** @brief The list of local indices of this interface. */ -InterfaceInformation interface_; -std::vector<std::size_t> sizes_; + /** @brief The process rank that this communication interface is with. */ + int rank_; + /** @brief The other rank that this interface communcates with. */ + std::size_t index_; + /** @brief The list of local indices of this interface. */ + InterfaceInformation interface_; + std::vector<std::size_t> sizes_; }; } // end unnamed namespace /** -* @addtogroup Common_Parallel -* -* @{ -*/ -/** -* @brief A buffered communicator where the amount of data sent does not have to be known a priori. -* -* In contrast to BufferedCommunicator the amount of data is determined by the container -* whose entries are sent and not known at the receiving side a priori. -* -* Note that there is no global index-space, only local index-spaces on each -* rank. Note also that each rank has two index-spaces, one used for -* gathering/sending, and one used for scattering/receiving. These may be the -* identical, but they do not have to be. -* -* For data send from rank A to rank B, the order that rank A inserts its -* indices into its send-interface for rank B has to be the same order that -* rank B inserts its matching indices into its receive interface for rank A. -* (This is because the `VariableSizeCommunicator` has no concept of a global -* index-space, so the order used to insert the indices into the interfaces is -* the only clue it has to know which source index should be communicated to -* which target index.) -* -* It is permissible for a rank to communicate with itself, i.e. it can define -* send- and receive-interfaces to itself. These interfaces do not need to -* contain the same indices, as the local send index-space can be different -* from the local receive index-space. This is useful for repartitioning or -* for aggregating in AMG. -* -* Do not assume that gathering to an index happens before scattering to the -* same index in the same communication, as `VariableSizeCommunicator` assumes -* they are from different index-spaces. This is a pitfall if you want do -* communicate a vector in-place, e.g. to sum up partial results from -* different ranks. Instead, have separate source and target vectors and copy -* the source vector to the target vector before communicating. -*/ + * @addtogroup Common_Parallel + * + * @{ + */ +/** + * @brief A buffered communicator where the amount of data sent does not have to be known a priori. + * + * In contrast to BufferedCommunicator the amount of data is determined by the container + * whose entries are sent and not known at the receiving side a priori. + * + * Note that there is no global index-space, only local index-spaces on each + * rank. Note also that each rank has two index-spaces, one used for + * gathering/sending, and one used for scattering/receiving. These may be the + * identical, but they do not have to be. + * + * For data send from rank A to rank B, the order that rank A inserts its + * indices into its send-interface for rank B has to be the same order that + * rank B inserts its matching indices into its receive interface for rank A. + * (This is because the `VariableSizeCommunicator` has no concept of a global + * index-space, so the order used to insert the indices into the interfaces is + * the only clue it has to know which source index should be communicated to + * which target index.) + * + * It is permissible for a rank to communicate with itself, i.e. it can define + * send- and receive-interfaces to itself. These interfaces do not need to + * contain the same indices, as the local send index-space can be different + * from the local receive index-space. This is useful for repartitioning or + * for aggregating in AMG. + * + * Do not assume that gathering to an index happens before scattering to the + * same index in the same communication, as `VariableSizeCommunicator` assumes + * they are from different index-spaces. This is a pitfall if you want do + * communicate a vector in-place, e.g. to sum up partial results from + * different ranks. Instead, have separate source and target vectors and copy + * the source vector to the target vector before communicating. + */ template<class Allocator=std::allocator<std::pair<InterfaceInformation,InterfaceInformation> > > class VariableSizeCommunicator { public: -/** -* @brief The type of the map from process number to InterfaceInformation for -* sending and receiving to and from it. -*/ -typedef std::map<int,std::pair<InterfaceInformation,InterfaceInformation>, -std::less<int>, -typename std::allocator_traits<Allocator>::template rebind_alloc< std::pair<const int,std::pair<InterfaceInformation,InterfaceInformation> > > > InterfaceMap; + /** + * @brief The type of the map from process number to InterfaceInformation for + * sending and receiving to and from it. + */ + typedef std::map<int,std::pair<InterfaceInformation,InterfaceInformation>, + std::less<int>, + typename std::allocator_traits<Allocator>::template rebind_alloc< std::pair<const int,std::pair<InterfaceInformation,InterfaceInformation> > > > InterfaceMap; #ifndef DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE -/** -* @brief Creates a communicator with the default maximum buffer size. -* -* The default size ist either what the macro DUNE_MAX_COMMUNICATION_BUFFER_SIZE -* is set to or 32768 if is not set. -*/ -VariableSizeCommunicator(MPI_Comm comm, const InterfaceMap& inf) -: maxBufferSize_(32768), interface_(&inf) -{ -MPI_Comm_dup(comm, &communicator_); -} -/** -* @brief Creates a communicator with the default maximum buffer size. -* @param inf The communication interface. -*/ -VariableSizeCommunicator(const Interface& inf) -: maxBufferSize_(32768), interface_(&inf.interfaces()) -{ -MPI_Comm_dup(inf.communicator(), &communicator_); -} + /** + * @brief Creates a communicator with the default maximum buffer size. + * + * The default size ist either what the macro DUNE_MAX_COMMUNICATION_BUFFER_SIZE + * is set to or 32768 if is not set. + */ + VariableSizeCommunicator(MPI_Comm comm, const InterfaceMap& inf) + : maxBufferSize_(32768), interface_(&inf) + { + MPI_Comm_dup(comm, &communicator_); + } + /** + * @brief Creates a communicator with the default maximum buffer size. + * @param inf The communication interface. + */ + VariableSizeCommunicator(const Interface& inf) + : maxBufferSize_(32768), interface_(&inf.interfaces()) + { + MPI_Comm_dup(inf.communicator(), &communicator_); + } #else -/** -* @brief Creates a communicator with the default maximum buffer size. -* -* The default size ist either what the macro DUNE_MAX_COMMUNICATION_BUFFER_SIZE -* is set to or 32768 if is not set. -*/ -VariableSizeCommunicator(MPI_Comm comm, InterfaceMap& inf) -: maxBufferSize_(DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE), -interface_(&inf) -{ -MPI_Comm_dup(comm, &communicator_); -} -/** -* @brief Creates a communicator with the default maximum buffer size. -* @param inf The communication interface. -*/ -VariableSizeCommunicator(const Interface& inf) -: maxBufferSize_(DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE), -interface_(&inf.interfaces()) -{ -MPI_Comm_dup(inf.communicator(), &communicator_); -} + /** + * @brief Creates a communicator with the default maximum buffer size. + * + * The default size ist either what the macro DUNE_MAX_COMMUNICATION_BUFFER_SIZE + * is set to or 32768 if is not set. + */ + VariableSizeCommunicator(MPI_Comm comm, InterfaceMap& inf) + : maxBufferSize_(DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE), + interface_(&inf) + { + MPI_Comm_dup(comm, &communicator_); + } + /** + * @brief Creates a communicator with the default maximum buffer size. + * @param inf The communication interface. + */ + VariableSizeCommunicator(const Interface& inf) + : maxBufferSize_(DUNE_PARALLEL_MAX_COMMUNICATION_BUFFER_SIZE), + interface_(&inf.interfaces()) + { + MPI_Comm_dup(inf.communicator(), &communicator_); + } #endif -/** -* @brief Creates a communicator with a specific maximum buffer size. -* @param comm The MPI communicator to use. -* @param inf The communication interface. -* @param max_buffer_size The maximum buffer size allowed. -*/ -VariableSizeCommunicator(MPI_Comm comm, const InterfaceMap& inf, std::size_t max_buffer_size) -: maxBufferSize_(max_buffer_size), interface_(&inf) -{ -MPI_Comm_dup(comm, &communicator_); -} - -/** -* @brief Creates a communicator with a specific maximum buffer size. -* @param inf The communication interface. -* @param max_buffer_size The maximum buffer size allowed. -*/ -VariableSizeCommunicator(const Interface& inf, std::size_t max_buffer_size) -: maxBufferSize_(max_buffer_size), interface_(&inf.interfaces()) -{ -MPI_Comm_dup(inf.communicator(), &communicator_); -} - -~VariableSizeCommunicator() -{ -MPI_Comm_free(&communicator_); -} - -/** -* @brief Copy-constructs a communicator -* @param other VariableSizeCommunicator that is copied. -*/ -VariableSizeCommunicator(const VariableSizeCommunicator& other) { -maxBufferSize_ = other.maxBufferSize_; -interface_ = other.interface_; -MPI_Comm_dup(other.communicator_, &communicator_); -} - -/** -* @brief Copy-assignes a communicator -* @param other VariableSizeCommunicator that is copied. -*/ -VariableSizeCommunicator& operator=(const VariableSizeCommunicator& other) { -if(this == &other) // don't do anything if objects are the same -return *this; - -maxBufferSize_ = other.maxBufferSize_; -interface_ = other.interface_; -MPI_Comm_free(&communicator_); -MPI_Comm_dup(other.communicator_, &communicator_); - -return *this; -} - -/** -* @brief Communicate forward. -* -* @tparam DataHandle The type of the handle describing the data. This type has to adhere -* to the following interface: -* \code{.cpp} -* // returns whether the number of data items per entry is fixed -* bool fixedsize(); -* // get the number of data items for an entry with index i -* std::size_t size(std::size_t i); -* // gather the data at index i -* template<class MessageBuffer> -* void gather(MessageBuffer& buf, std::size_t i); -* // scatter the n data items to index i -* template<class MessageBuffer> -* void scatter(MessageBuffer& buf, std::size_t i, std::size_t n); -* \endcode -* @param handle A handle responsible for describing the data, gathering, and scattering it. -*/ -template<class DataHandle> -void forward(DataHandle& handle) -{ -communicate<true>(handle); -} - -/** -* @brief Communicate backwards. -* -* @tparam DataHandle The type of the handle describing the data. This type has to adhere -* to the following interface: -* \code{.cpp} -* // returns whether the number of data items per entry is fixed -* bool fixedsize(); -* // get the number of data items for an entry with index i -* std::size_t size(std::size_t i); -* // gather the data at index i -* template<class MessageBuffer> -* void gather(MessageBuffer& buf, std::size_t i); -* // scatter the n data items to index i -* template<class MessageBuffer> -* void scatter(MessageBuffer& buf, std::size_t i, std::size_t n); -* \endcode -* @param handle A handle responsible for describing the data, gathering, and scattering it. -*/ -template<class DataHandle> -void backward(DataHandle& handle) -{ -communicate<false>(handle); -} + /** + * @brief Creates a communicator with a specific maximum buffer size. + * @param comm The MPI communicator to use. + * @param inf The communication interface. + * @param max_buffer_size The maximum buffer size allowed. + */ + VariableSizeCommunicator(MPI_Comm comm, const InterfaceMap& inf, std::size_t max_buffer_size) + : maxBufferSize_(max_buffer_size), interface_(&inf) + { + MPI_Comm_dup(comm, &communicator_); + } + + /** + * @brief Creates a communicator with a specific maximum buffer size. + * @param inf The communication interface. + * @param max_buffer_size The maximum buffer size allowed. + */ + VariableSizeCommunicator(const Interface& inf, std::size_t max_buffer_size) + : maxBufferSize_(max_buffer_size), interface_(&inf.interfaces()) + { + MPI_Comm_dup(inf.communicator(), &communicator_); + } + + ~VariableSizeCommunicator() + { + MPI_Comm_free(&communicator_); + } + + /** + * @brief Copy-constructs a communicator + * @param other VariableSizeCommunicator that is copied. + */ + VariableSizeCommunicator(const VariableSizeCommunicator& other) { + maxBufferSize_ = other.maxBufferSize_; + interface_ = other.interface_; + MPI_Comm_dup(other.communicator_, &communicator_); + } + + /** + * @brief Copy-assignes a communicator + * @param other VariableSizeCommunicator that is copied. + */ + VariableSizeCommunicator& operator=(const VariableSizeCommunicator& other) { + if(this == &other) // don't do anything if objects are the same + return *this; + + maxBufferSize_ = other.maxBufferSize_; + interface_ = other.interface_; + MPI_Comm_free(&communicator_); + MPI_Comm_dup(other.communicator_, &communicator_); + + return *this; + } + + /** + * @brief Communicate forward. + * + * @tparam DataHandle The type of the handle describing the data. This type has to adhere + * to the following interface: + * \code{.cpp} + * // returns whether the number of data items per entry is fixed + * bool fixedsize(); + * // get the number of data items for an entry with index i + * std::size_t size(std::size_t i); + * // gather the data at index i + * template<class MessageBuffer> + * void gather(MessageBuffer& buf, std::size_t i); + * // scatter the n data items to index i + * template<class MessageBuffer> + * void scatter(MessageBuffer& buf, std::size_t i, std::size_t n); + * \endcode + * @param handle A handle responsible for describing the data, gathering, and scattering it. + */ + template<class DataHandle> + void forward(DataHandle& handle) + { + communicate<true>(handle); + } + + /** + * @brief Communicate backwards. + * + * @tparam DataHandle The type of the handle describing the data. This type has to adhere + * to the following interface: + * \code{.cpp} + * // returns whether the number of data items per entry is fixed + * bool fixedsize(); + * // get the number of data items for an entry with index i + * std::size_t size(std::size_t i); + * // gather the data at index i + * template<class MessageBuffer> + * void gather(MessageBuffer& buf, std::size_t i); + * // scatter the n data items to index i + * template<class MessageBuffer> + * void scatter(MessageBuffer& buf, std::size_t i, std::size_t n); + * \endcode + * @param handle A handle responsible for describing the data, gathering, and scattering it. + */ + template<class DataHandle> + void backward(DataHandle& handle) + { + communicate<false>(handle); + } private: -template<bool FORWARD, class DataHandle> -void communicateSizes(DataHandle& handle, -std::vector<InterfaceTracker>& recv_trackers); - -/** -* @brief Communicates data according to the interface. -* @tparam forward If true sends data forwards, otherwise backwards along the interface. -* @tparame DataHandle The type of the data handle @see forward for a description of the interface. -* @param handle The handle describing the data and responsible for gather and scatter operations. -*/ -template<bool forward,class DataHandle> -void communicate(DataHandle& handle); -/** -* @brief Initialize the trackers along the interface for the communication. -* @tparam FORWARD If true we send in the forward direction. -* @tparam DataHandle DataHandle The type of the data handle. -* @param handle The handle describing the data and responsible for gather -* and scatter operations. -* @param[out] send_trackers The trackers for the sending side. -* @param[out] recv_trackers The trackers for the receiving side. -*/ -template<bool FORWARD, class DataHandle> -void setupInterfaceTrackers(DataHandle& handle, -std::vector<InterfaceTracker>& send_trackers, -std::vector<InterfaceTracker>& recv_trackers); -/** -* @brief Communicate data with a fixed amount of data per entry. -* @tparam FORWARD If true we send in the forward direction. -* @tparam DataHandle DataHandle The type of the data handle. -* @param handle The handle describing the data and responsible for gather -* and scatter operations. -*/ -template<bool FORWARD, class DataHandle> -void communicateFixedSize(DataHandle& handle); -/** -* @brief Communicate data with a variable amount of data per entry. -* @tparam FORWARD If true we send in the forward direction. -* @tparam DataHandle DataHandle The type of the data handle. -* @param handle The handle describing the data and responsible for gather -* and scatter operations. -*/ -template<bool FORWARD, class DataHandle> -void communicateVariableSize(DataHandle& handle); -/** -* @brief The maximum size if the buffers used for gather and scatter. -* -* @note If this process has n neighbours, then a maximum of 2n buffers of this size -* is allocate. Memory needed will be n*sizeof(std::size_t)+n*sizeof(Datahandle::DataType) -*/ -std::size_t maxBufferSize_; -/** -* @brief description of the interface. -* -* This is a map of the neighboring process number to a pair of local index lists. -* The first is a list of indices to gather data for sending from and the second is a list of -* indices to scatter received data to during forward. -*/ -const InterfaceMap* interface_; -/** -* @brief The communicator. -* -* This is a cloned communicator to ensure there are no interferences. -*/ -MPI_Comm communicator_; + template<bool FORWARD, class DataHandle> + void communicateSizes(DataHandle& handle, + std::vector<InterfaceTracker>& recv_trackers); + + /** + * @brief Communicates data according to the interface. + * @tparam forward If true sends data forwards, otherwise backwards along the interface. + * @tparame DataHandle The type of the data handle @see forward for a description of the interface. + * @param handle The handle describing the data and responsible for gather and scatter operations. + */ + template<bool forward,class DataHandle> + void communicate(DataHandle& handle); + /** + * @brief Initialize the trackers along the interface for the communication. + * @tparam FORWARD If true we send in the forward direction. + * @tparam DataHandle DataHandle The type of the data handle. + * @param handle The handle describing the data and responsible for gather + * and scatter operations. + * @param[out] send_trackers The trackers for the sending side. + * @param[out] recv_trackers The trackers for the receiving side. + */ + template<bool FORWARD, class DataHandle> + void setupInterfaceTrackers(DataHandle& handle, + std::vector<InterfaceTracker>& send_trackers, + std::vector<InterfaceTracker>& recv_trackers); + /** + * @brief Communicate data with a fixed amount of data per entry. + * @tparam FORWARD If true we send in the forward direction. + * @tparam DataHandle DataHandle The type of the data handle. + * @param handle The handle describing the data and responsible for gather + * and scatter operations. + */ + template<bool FORWARD, class DataHandle> + void communicateFixedSize(DataHandle& handle); + /** + * @brief Communicate data with a variable amount of data per entry. + * @tparam FORWARD If true we send in the forward direction. + * @tparam DataHandle DataHandle The type of the data handle. + * @param handle The handle describing the data and responsible for gather + * and scatter operations. + */ + template<bool FORWARD, class DataHandle> + void communicateVariableSize(DataHandle& handle); + /** + * @brief The maximum size if the buffers used for gather and scatter. + * + * @note If this process has n neighbours, then a maximum of 2n buffers of this size + * is allocate. Memory needed will be n*sizeof(std::size_t)+n*sizeof(Datahandle::DataType) + */ + std::size_t maxBufferSize_; + /** + * @brief description of the interface. + * + * This is a map of the neighboring process number to a pair of local index lists. + * The first is a list of indices to gather data for sending from and the second is a list of + * indices to scatter received data to during forward. + */ + const InterfaceMap* interface_; + /** + * @brief The communicator. + * + * This is a cloned communicator to ensure there are no interferences. + */ + MPI_Comm communicator_; }; /** @} */ namespace { /** -* @brief A data handle for comunicating the sizes of variable sized data. -*/ + * @brief A data handle for comunicating the sizes of variable sized data. + */ template<class DataHandle> class SizeDataHandle { public: -typedef std::size_t DataType; - -SizeDataHandle(DataHandle& data, -std::vector<InterfaceTracker>& trackers) -: data_(data), trackers_(trackers), index_() -{} -bool fixedsize() -{ -return true; -} -std::size_t size(std::size_t i) -{ -DUNE_UNUSED_PARAMETER(i); -return 1; -} -template<class B> -void gather(B& buf, int i) -{ -buf.write(data_.size(i)); -} -void setReceivingIndex(std::size_t i) -{ -index_=i; -} -std::size_t* getSizesPointer() -{ -return trackers_[index_].getSizesPointer(); -} + typedef std::size_t DataType; + + SizeDataHandle(DataHandle& data, + std::vector<InterfaceTracker>& trackers) + : data_(data), trackers_(trackers), index_() + {} + bool fixedsize() + { + return true; + } + std::size_t size(std::size_t i) + { + DUNE_UNUSED_PARAMETER(i); + return 1; + } + template<class B> + void gather(B& buf, int i) + { + buf.write(data_.size(i)); + } + void setReceivingIndex(std::size_t i) + { + index_=i; + } + std::size_t* getSizesPointer() + { + return trackers_[index_].getSizesPointer(); + } private: -DataHandle& data_; -std::vector<InterfaceTracker>& trackers_; -int index_; + DataHandle& data_; + std::vector<InterfaceTracker>& trackers_; + int index_; }; template<class T> @@ -588,577 +588,577 @@ void setReceivingIndex(T&, int) template<class T> void setReceivingIndex(SizeDataHandle<T>& t, int i) { -t.setReceivingIndex(i); + t.setReceivingIndex(i); } /** -* @brief Template meta program for choosing then send or receive interface -* information based on the direction. -* @tparam FORWARD If true the communication happens in the forward direction. -*/ + * @brief Template meta program for choosing then send or receive interface + * information based on the direction. + * @tparam FORWARD If true the communication happens in the forward direction. + */ template<bool FORWARD> struct InterfaceInformationChooser { -/** -* @brief Get the interface information for the sending side. -*/ -static const InterfaceInformation& -getSend(const std::pair<InterfaceInformation,InterfaceInformation>& info) -{ -return info.first; -} - -/** -* @brief Get the interface information for the receiving side. -*/ -static const InterfaceInformation& -getReceive(const std::pair<InterfaceInformation,InterfaceInformation>& info) -{ -return info.second; -} + /** + * @brief Get the interface information for the sending side. + */ + static const InterfaceInformation& + getSend(const std::pair<InterfaceInformation,InterfaceInformation>& info) + { + return info.first; + } + + /** + * @brief Get the interface information for the receiving side. + */ + static const InterfaceInformation& + getReceive(const std::pair<InterfaceInformation,InterfaceInformation>& info) + { + return info.second; + } }; template<> struct InterfaceInformationChooser<false> { -static const InterfaceInformation& -getSend(const std::pair<InterfaceInformation,InterfaceInformation>& info) -{ -return info.second; -} - -static const InterfaceInformation& -getReceive(const std::pair<InterfaceInformation,InterfaceInformation>& info) -{ -return info.first; -} + static const InterfaceInformation& + getSend(const std::pair<InterfaceInformation,InterfaceInformation>& info) + { + return info.second; + } + + static const InterfaceInformation& + getReceive(const std::pair<InterfaceInformation,InterfaceInformation>& info) + { + return info.first; + } }; /** -* @brief A functor that packs entries into the message buffer. -* @tparam DataHandle The type of the data handle that describes -* the communicated data. -*/ + * @brief A functor that packs entries into the message buffer. + * @tparam DataHandle The type of the data handle that describes + * the communicated data. + */ template<class DataHandle> struct PackEntries { -int operator()(DataHandle& handle, InterfaceTracker& tracker, -MessageBuffer<typename DataHandle::DataType>& buffer, -int i) const -{ -DUNE_UNUSED_PARAMETER(i); -return operator()(handle,tracker,buffer); -} - -/** -* @brief packs data. -* @param handle The handle describing the data and the gather and scatter operations. -* @param tracker The tracker of the interface to tell us where we are. -* @param buffer The buffer to use for packing. -* @return The number data entries that we packed. -*/ -int operator()(DataHandle& handle, InterfaceTracker& tracker, -MessageBuffer<typename DataHandle::DataType>& buffer) const -{ -if(tracker.fixedSize) // fixed size if variable is >0! -{ - -std::size_t noIndices=std::min(buffer.size()/tracker.fixedSize, tracker.indicesLeft()); -for(std::size_t i=0; i< noIndices; ++i) -{ -handle.gather(buffer, tracker.index()); -tracker.moveToNextIndex(); -} -return noIndices*tracker.fixedSize; -} -else -{ -int packed=0; -tracker.skipZeroIndices(); -while(!tracker.finished()) -if(buffer.hasSpaceForItems(handle.size(tracker.index()))) -{ -handle.gather(buffer, tracker.index()); -packed+=handle.size(tracker.index()); -tracker.moveToNextIndex(); -} -else -break; -return packed; -} -} + int operator()(DataHandle& handle, InterfaceTracker& tracker, + MessageBuffer<typename DataHandle::DataType>& buffer, + int i) const + { + DUNE_UNUSED_PARAMETER(i); + return operator()(handle,tracker,buffer); + } + + /** + * @brief packs data. + * @param handle The handle describing the data and the gather and scatter operations. + * @param tracker The tracker of the interface to tell us where we are. + * @param buffer The buffer to use for packing. + * @return The number data entries that we packed. + */ + int operator()(DataHandle& handle, InterfaceTracker& tracker, + MessageBuffer<typename DataHandle::DataType>& buffer) const + { + if(tracker.fixedSize) // fixed size if variable is >0! + { + + std::size_t noIndices=std::min(buffer.size()/tracker.fixedSize, tracker.indicesLeft()); + for(std::size_t i=0; i< noIndices; ++i) + { + handle.gather(buffer, tracker.index()); + tracker.moveToNextIndex(); + } + return noIndices*tracker.fixedSize; + } + else + { + int packed=0; + tracker.skipZeroIndices(); + while(!tracker.finished()) + if(buffer.hasSpaceForItems(handle.size(tracker.index()))) + { + handle.gather(buffer, tracker.index()); + packed+=handle.size(tracker.index()); + tracker.moveToNextIndex(); + } + else + break; + return packed; + } + } }; /** -* @brief A functor that unpacks entries from the message buffer. -* @tparam DataHandle The type of the data handle that describes -* the communicated data. -*/ + * @brief A functor that unpacks entries from the message buffer. + * @tparam DataHandle The type of the data handle that describes + * the communicated data. + */ template<class DataHandle> struct UnpackEntries{ -/** -* @brief packs data. -* @param handle The handle describing the data and the gather and scatter operations. -* @param tracker The tracker of the interface to tell us where we are. -* @param buffer The buffer to use for packing. -* @return The number data entries that we packed. -*/ -bool operator()(DataHandle& handle, InterfaceTracker& tracker, -MessageBuffer<typename DataHandle::DataType>& buffer, -int count=0) -{ -if(tracker.fixedSize) // fixed size if variable is >0! -{ -std::size_t noIndices=std::min(buffer.size()/tracker.fixedSize, tracker.indicesLeft()); - -for(std::size_t i=0; i< noIndices; ++i) -{ -handle.scatter(buffer, tracker.index(), tracker.fixedSize); -tracker.moveToNextIndex(); -} -return tracker.finished(); -} -else -{ -assert(count); -for(int unpacked=0;unpacked<count;) -{ -assert(!tracker.finished()); -assert(buffer.hasSpaceForItems(tracker.size())); -handle.scatter(buffer, tracker.index(), tracker.size()); -unpacked+=tracker.size(); -tracker.moveToNextIndex(); -} -return tracker.finished(); -} -} + /** + * @brief packs data. + * @param handle The handle describing the data and the gather and scatter operations. + * @param tracker The tracker of the interface to tell us where we are. + * @param buffer The buffer to use for packing. + * @return The number data entries that we packed. + */ + bool operator()(DataHandle& handle, InterfaceTracker& tracker, + MessageBuffer<typename DataHandle::DataType>& buffer, + int count=0) + { + if(tracker.fixedSize) // fixed size if variable is >0! + { + std::size_t noIndices=std::min(buffer.size()/tracker.fixedSize, tracker.indicesLeft()); + + for(std::size_t i=0; i< noIndices; ++i) + { + handle.scatter(buffer, tracker.index(), tracker.fixedSize); + tracker.moveToNextIndex(); + } + return tracker.finished(); + } + else + { + assert(count); + for(int unpacked=0;unpacked<count;) + { + assert(!tracker.finished()); + assert(buffer.hasSpaceForItems(tracker.size())); + handle.scatter(buffer, tracker.index(), tracker.size()); + unpacked+=tracker.size(); + tracker.moveToNextIndex(); + } + return tracker.finished(); + } + } }; /** -* @brief A functor that unpacks std::size_t from the message buffer. -*/ + * @brief A functor that unpacks std::size_t from the message buffer. + */ template<class DataHandle> struct UnpackSizeEntries{ -/** -* @brief packs data. -* @param handle The handle describing the data and the gather and scatter operations. -* @param tracker The tracker of the interface to tell us where we are. -* @param buffer The buffer to use for packing. -* @return The number data entries that we packed. -*/ -bool operator()(SizeDataHandle<DataHandle>& handle, InterfaceTracker& tracker, -MessageBuffer<typename SizeDataHandle<DataHandle>::DataType>& buffer) const -{ -std::size_t noIndices=std::min(buffer.size(), tracker.indicesLeft()); -std::copy(static_cast<std::size_t*>(buffer), static_cast<std::size_t*>(buffer)+noIndices, -handle.getSizesPointer()+tracker.offset()); -tracker.increment(noIndices); -return noIndices; -} -bool operator()(SizeDataHandle<DataHandle>& handle, InterfaceTracker& tracker, -MessageBuffer<typename SizeDataHandle<DataHandle>::DataType>& buffer, int) const -{ -return operator()(handle,tracker,buffer); -} + /** + * @brief packs data. + * @param handle The handle describing the data and the gather and scatter operations. + * @param tracker The tracker of the interface to tell us where we are. + * @param buffer The buffer to use for packing. + * @return The number data entries that we packed. + */ + bool operator()(SizeDataHandle<DataHandle>& handle, InterfaceTracker& tracker, + MessageBuffer<typename SizeDataHandle<DataHandle>::DataType>& buffer) const + { + std::size_t noIndices=std::min(buffer.size(), tracker.indicesLeft()); + std::copy(static_cast<std::size_t*>(buffer), static_cast<std::size_t*>(buffer)+noIndices, + handle.getSizesPointer()+tracker.offset()); + tracker.increment(noIndices); + return noIndices; + } + bool operator()(SizeDataHandle<DataHandle>& handle, InterfaceTracker& tracker, + MessageBuffer<typename SizeDataHandle<DataHandle>::DataType>& buffer, int) const + { + return operator()(handle,tracker,buffer); + } }; /** -* @brief Sends the size in case of communicating a fixed amount of data per entry. -* @param[in] send_trackers The trackers for the sending side. -* @param[out] send_requests The request for the asynchronous send operations. -* @param[in] recv_trackers The trackers for the receiving side. -* @param[out] recv_requests The request for the asynchronous receive operations. -*/ + * @brief Sends the size in case of communicating a fixed amount of data per entry. + * @param[in] send_trackers The trackers for the sending side. + * @param[out] send_requests The request for the asynchronous send operations. + * @param[in] recv_trackers The trackers for the receiving side. + * @param[out] recv_requests The request for the asynchronous receive operations. + */ void sendFixedSize(std::vector<InterfaceTracker>& send_trackers, -std::vector<MPI_Request>& send_requests, -std::vector<InterfaceTracker>& recv_trackers, -std::vector<MPI_Request>& recv_requests, -MPI_Comm communicator) + std::vector<MPI_Request>& send_requests, + std::vector<InterfaceTracker>& recv_trackers, + std::vector<MPI_Request>& recv_requests, + MPI_Comm communicator) { -typedef std::vector<InterfaceTracker>::iterator TIter; -std::vector<MPI_Request>::iterator mIter=recv_requests.begin(); + typedef std::vector<InterfaceTracker>::iterator TIter; + std::vector<MPI_Request>::iterator mIter=recv_requests.begin(); -for(TIter iter=recv_trackers.begin(), end=recv_trackers.end(); iter!=end; -++iter, ++mIter) -{ -MPI_Irecv(&(iter->fixedSize), 1, MPITraits<std::size_t>::getType(), -iter->rank(), 933881, communicator, &(*mIter)); -} + for(TIter iter=recv_trackers.begin(), end=recv_trackers.end(); iter!=end; + ++iter, ++mIter) + { + MPI_Irecv(&(iter->fixedSize), 1, MPITraits<std::size_t>::getType(), + iter->rank(), 933881, communicator, &(*mIter)); + } -// Send our size to all neighbours using non-blocking synchronous communication. -std::vector<MPI_Request>::iterator mIter1=send_requests.begin(); -for(TIter iter=send_trackers.begin(), end=send_trackers.end(); -iter!=end; -++iter, ++mIter1) -{ -MPI_Issend(&(iter->fixedSize), 1, MPITraits<std::size_t>::getType(), -iter->rank(), 933881, communicator, &(*mIter1)); -} + // Send our size to all neighbours using non-blocking synchronous communication. + std::vector<MPI_Request>::iterator mIter1=send_requests.begin(); + for(TIter iter=send_trackers.begin(), end=send_trackers.end(); + iter!=end; + ++iter, ++mIter1) + { + MPI_Issend(&(iter->fixedSize), 1, MPITraits<std::size_t>::getType(), + iter->rank(), 933881, communicator, &(*mIter1)); + } } /** -* @brief Functor for setting up send requests. -* @tparam DataHandle The type of the data handle for describing the data. -*/ + * @brief Functor for setting up send requests. + * @tparam DataHandle The type of the data handle for describing the data. + */ template<class DataHandle> struct SetupSendRequest{ -void operator()(DataHandle& handle, -InterfaceTracker& tracker, -MessageBuffer<typename DataHandle::DataType>& buffer, -MPI_Request& request, -MPI_Comm comm) const -{ -buffer.reset(); -int size=PackEntries<DataHandle>()(handle, tracker, buffer); -// Skip indices of zero size. -while(!tracker.finished() && !handle.size(tracker.index())) -tracker.moveToNextIndex(); -if(size) -MPI_Issend(buffer, size, MPITraits<typename DataHandle::DataType>::getType(), -tracker.rank(), 933399, comm, &request); -} + void operator()(DataHandle& handle, + InterfaceTracker& tracker, + MessageBuffer<typename DataHandle::DataType>& buffer, + MPI_Request& request, + MPI_Comm comm) const + { + buffer.reset(); + int size=PackEntries<DataHandle>()(handle, tracker, buffer); + // Skip indices of zero size. + while(!tracker.finished() && !handle.size(tracker.index())) + tracker.moveToNextIndex(); + if(size) + MPI_Issend(buffer, size, MPITraits<typename DataHandle::DataType>::getType(), + tracker.rank(), 933399, comm, &request); + } }; /** -* @brief Functor for setting up receive requests. -* @tparam DataHandle The type of the data handle for describing the data. -*/ + * @brief Functor for setting up receive requests. + * @tparam DataHandle The type of the data handle for describing the data. + */ template<class DataHandle> struct SetupRecvRequest{ -void operator()(DataHandle& /*handle*/, -InterfaceTracker& tracker, -MessageBuffer<typename DataHandle::DataType>& buffer, -MPI_Request& request, -MPI_Comm comm) const -{ -buffer.reset(); -if(tracker.indicesLeft()) -MPI_Irecv(buffer, buffer.size(), MPITraits<typename DataHandle::DataType>::getType(), -tracker.rank(), 933399, comm, &request); -} + void operator()(DataHandle& /*handle*/, + InterfaceTracker& tracker, + MessageBuffer<typename DataHandle::DataType>& buffer, + MPI_Request& request, + MPI_Comm comm) const + { + buffer.reset(); + if(tracker.indicesLeft()) + MPI_Irecv(buffer, buffer.size(), MPITraits<typename DataHandle::DataType>::getType(), + tracker.rank(), 933399, comm, &request); + } }; /** -* @brief A functor that does nothing. -*/ + * @brief A functor that does nothing. + */ template<class DataHandle> struct NullPackUnpackFunctor { -int operator()(DataHandle&, InterfaceTracker&, -MessageBuffer<typename DataHandle::DataType>&, int) -{ -return 0; -} -int operator()(DataHandle&, InterfaceTracker&, -MessageBuffer<typename DataHandle::DataType>&) -{ -return 0; -} + int operator()(DataHandle&, InterfaceTracker&, + MessageBuffer<typename DataHandle::DataType>&, int) + { + return 0; + } + int operator()(DataHandle&, InterfaceTracker&, + MessageBuffer<typename DataHandle::DataType>&) + { + return 0; + } }; /** -* @brief Check whether some of the requests finished and continue send/receive operation. -* @tparam DataHandle The type of the data handle describing the data. -* @tparam BufferFunctor A functor that packs or unpacks data from the buffer. -* E.g. NullPackUnpackFunctor. -* @tparam CommunicationFuntor A functor responsible for continuing the communication. -* @param handle The data handle describing the data. -* @param trackers The trackers indicating the current position in the communication. -* @param requests The requests to test whether they finished. -* @param requests2 The requests to use for setting up the continuing communication. Might -* be the same as requests. -* @param comm The MPI communicator to use. -* @param buffer_func The functor that does the packing or unpacking of the data. -*/ + * @brief Check whether some of the requests finished and continue send/receive operation. + * @tparam DataHandle The type of the data handle describing the data. + * @tparam BufferFunctor A functor that packs or unpacks data from the buffer. + * E.g. NullPackUnpackFunctor. + * @tparam CommunicationFuntor A functor responsible for continuing the communication. + * @param handle The data handle describing the data. + * @param trackers The trackers indicating the current position in the communication. + * @param requests The requests to test whether they finished. + * @param requests2 The requests to use for setting up the continuing communication. Might + * be the same as requests. + * @param comm The MPI communicator to use. + * @param buffer_func The functor that does the packing or unpacking of the data. + */ template<class DataHandle, class BufferFunctor, class CommunicationFunctor> std::size_t checkAndContinue(DataHandle& handle, -std::vector<InterfaceTracker>& trackers, -std::vector<MPI_Request>& requests, -std::vector<MPI_Request>& requests2, -std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, -MPI_Comm comm, -BufferFunctor buffer_func, -CommunicationFunctor comm_func, -bool valid=true, -bool getCount=false) -{ -std::size_t size=requests.size(); -std::vector<MPI_Status> statuses(size); -int no_completed; -std::vector<int> indices(size, -1); // the indices for which the communication finished. - -MPI_Testsome(size, &(requests[0]), &no_completed, &(indices[0]), &(statuses[0])); -indices.resize(no_completed); -for(std::vector<int>::iterator index=indices.begin(), end=indices.end(); -index!=end; ++index) -{ -InterfaceTracker& tracker=trackers[*index]; -setReceivingIndex(handle, *index); -if(getCount) -{ -// Get the number of entries received -int count; -MPI_Get_count(&(statuses[index-indices.begin()]), -MPITraits<typename DataHandle::DataType>::getType(), -&count); -// Communication completed, we can reuse the buffers, e.g. unpack or repack -buffer_func(handle, tracker, buffers[*index], count); -}else -buffer_func(handle, tracker, buffers[*index]); -tracker.skipZeroIndices(); -if(!tracker.finished()){ -// Maybe start another communication. -comm_func(handle, tracker, buffers[*index], requests2[*index], comm); -tracker.skipZeroIndices(); -if(valid) ---no_completed; // communication not finished, decrement counter for finished ones. -} -} -return no_completed; - -} - -/** -* @brief Receive the size per data entry and set up requests for receiving the data. -* @tparam DataHandle The type of the data handle. -* @param trackers The trackers indicating the indices where we send and from which rank. -* @param size_requests The requests for receiving the size. -* @param data_requests The requests for sending the data. -* @param buffers The buffers to use for sending. -* @param comm The mpi communicator to use. -*/ + std::vector<InterfaceTracker>& trackers, + std::vector<MPI_Request>& requests, + std::vector<MPI_Request>& requests2, + std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, + MPI_Comm comm, + BufferFunctor buffer_func, + CommunicationFunctor comm_func, + bool valid=true, + bool getCount=false) +{ + std::size_t size=requests.size(); + std::vector<MPI_Status> statuses(size); + int no_completed; + std::vector<int> indices(size, -1); // the indices for which the communication finished. + + MPI_Testsome(size, &(requests[0]), &no_completed, &(indices[0]), &(statuses[0])); + indices.resize(no_completed); + for(std::vector<int>::iterator index=indices.begin(), end=indices.end(); + index!=end; ++index) + { + InterfaceTracker& tracker=trackers[*index]; + setReceivingIndex(handle, *index); + if(getCount) + { + // Get the number of entries received + int count; + MPI_Get_count(&(statuses[index-indices.begin()]), + MPITraits<typename DataHandle::DataType>::getType(), + &count); + // Communication completed, we can reuse the buffers, e.g. unpack or repack + buffer_func(handle, tracker, buffers[*index], count); + }else + buffer_func(handle, tracker, buffers[*index]); + tracker.skipZeroIndices(); + if(!tracker.finished()){ + // Maybe start another communication. + comm_func(handle, tracker, buffers[*index], requests2[*index], comm); + tracker.skipZeroIndices(); + if(valid) + --no_completed; // communication not finished, decrement counter for finished ones. + } + } + return no_completed; + +} + +/** + * @brief Receive the size per data entry and set up requests for receiving the data. + * @tparam DataHandle The type of the data handle. + * @param trackers The trackers indicating the indices where we send and from which rank. + * @param size_requests The requests for receiving the size. + * @param data_requests The requests for sending the data. + * @param buffers The buffers to use for sending. + * @param comm The mpi communicator to use. + */ template<class DataHandle> std::size_t receiveSizeAndSetupReceive(DataHandle& handle, -std::vector<InterfaceTracker>& trackers, -std::vector<MPI_Request>& size_requests, -std::vector<MPI_Request>& data_requests, -std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, -MPI_Comm comm) + std::vector<InterfaceTracker>& trackers, + std::vector<MPI_Request>& size_requests, + std::vector<MPI_Request>& data_requests, + std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, + MPI_Comm comm) { -return checkAndContinue(handle, trackers, size_requests, data_requests, buffers, comm, -NullPackUnpackFunctor<DataHandle>(), SetupRecvRequest<DataHandle>(), false); + return checkAndContinue(handle, trackers, size_requests, data_requests, buffers, comm, + NullPackUnpackFunctor<DataHandle>(), SetupRecvRequest<DataHandle>(), false); } /** -* @brief Check whether send request completed and continue sending if necessary. -* @tparam DataHandle The type of the data handle. -* @param trackers The trackers indicating the indices where we send and from which rank. -* @param requests The requests for the asynchronous communication. -* @param buffers The buffers to use for sending. -* @param comm The mpi communicator to use. -*/ + * @brief Check whether send request completed and continue sending if necessary. + * @tparam DataHandle The type of the data handle. + * @param trackers The trackers indicating the indices where we send and from which rank. + * @param requests The requests for the asynchronous communication. + * @param buffers The buffers to use for sending. + * @param comm The mpi communicator to use. + */ template<class DataHandle> std::size_t checkSendAndContinueSending(DataHandle& handle, -std::vector<InterfaceTracker>& trackers, -std::vector<MPI_Request>& requests, -std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, -MPI_Comm comm) + std::vector<InterfaceTracker>& trackers, + std::vector<MPI_Request>& requests, + std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, + MPI_Comm comm) { -return checkAndContinue(handle, trackers, requests, requests, buffers, comm, -NullPackUnpackFunctor<DataHandle>(), SetupSendRequest<DataHandle>()); + return checkAndContinue(handle, trackers, requests, requests, buffers, comm, + NullPackUnpackFunctor<DataHandle>(), SetupSendRequest<DataHandle>()); } /** -* @brief Check whether receive request completed and continue receiving if necessary. -* @tparam DataHandle The type of the data handle. -* @param trackers The trackers indicating the indices where we receive and from which rank. -* @param requests The requests for the asynchronous communication. -* @param buffers The buffers to use for receiving. -* @param comm The mpi communicator to use. -*/ + * @brief Check whether receive request completed and continue receiving if necessary. + * @tparam DataHandle The type of the data handle. + * @param trackers The trackers indicating the indices where we receive and from which rank. + * @param requests The requests for the asynchronous communication. + * @param buffers The buffers to use for receiving. + * @param comm The mpi communicator to use. + */ template<class DataHandle> std::size_t checkReceiveAndContinueReceiving(DataHandle& handle, -std::vector<InterfaceTracker>& trackers, -std::vector<MPI_Request>& requests, -std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, -MPI_Comm comm) + std::vector<InterfaceTracker>& trackers, + std::vector<MPI_Request>& requests, + std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, + MPI_Comm comm) { -return checkAndContinue(handle, trackers, requests, requests, buffers, comm, -UnpackEntries<DataHandle>(), SetupRecvRequest<DataHandle>(), -true, !handle.fixedsize()); + return checkAndContinue(handle, trackers, requests, requests, buffers, comm, + UnpackEntries<DataHandle>(), SetupRecvRequest<DataHandle>(), + true, !handle.fixedsize()); } bool validRecvRequests(const std::vector<MPI_Request> reqs) { -for(std::vector<MPI_Request>::const_iterator i=reqs.begin(), end=reqs.end(); -i!=end; ++i) -if(*i!=MPI_REQUEST_NULL) -return true; -return false; + for(std::vector<MPI_Request>::const_iterator i=reqs.begin(), end=reqs.end(); + i!=end; ++i) + if(*i!=MPI_REQUEST_NULL) + return true; + return false; } /** -* @brief Sets up all the send requests for the data. -* @tparam DataHandle The type of the data handle. -* @tparam Functor The type of the functor to set up the request. -* @param handle The data handle describing the data. -* @param trackers The trackers for the communication interfaces. -* @param buffers The buffers for the comunication. One for each neighbour. -* @param requests The send requests for each neighbour. -* @param setupFunctor The functor responsible for setting up the request. -*/ + * @brief Sets up all the send requests for the data. + * @tparam DataHandle The type of the data handle. + * @tparam Functor The type of the functor to set up the request. + * @param handle The data handle describing the data. + * @param trackers The trackers for the communication interfaces. + * @param buffers The buffers for the comunication. One for each neighbour. + * @param requests The send requests for each neighbour. + * @param setupFunctor The functor responsible for setting up the request. + */ template<class DataHandle, class Functor> std::size_t setupRequests(DataHandle& handle, -std::vector<InterfaceTracker>& trackers, -std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, -std::vector<MPI_Request>& requests, -const Functor& setupFunctor, -MPI_Comm communicator) -{ -typedef typename std::vector<InterfaceTracker>::iterator TIter; -typename std::vector<MessageBuffer<typename DataHandle::DataType> >::iterator -biter=buffers.begin(); -typename std::vector<MPI_Request>::iterator riter=requests.begin(); -std::size_t complete=0; -for(TIter titer=trackers.begin(), end=trackers.end(); titer!=end; ++titer, ++biter, ++riter) -{ -setupFunctor(handle, *titer, *biter, *riter, communicator); -complete+=titer->finished(); -} -return complete; + std::vector<InterfaceTracker>& trackers, + std::vector<MessageBuffer<typename DataHandle::DataType> >& buffers, + std::vector<MPI_Request>& requests, + const Functor& setupFunctor, + MPI_Comm communicator) +{ + typedef typename std::vector<InterfaceTracker>::iterator TIter; + typename std::vector<MessageBuffer<typename DataHandle::DataType> >::iterator + biter=buffers.begin(); + typename std::vector<MPI_Request>::iterator riter=requests.begin(); + std::size_t complete=0; + for(TIter titer=trackers.begin(), end=trackers.end(); titer!=end; ++titer, ++biter, ++riter) + { + setupFunctor(handle, *titer, *biter, *riter, communicator); + complete+=titer->finished(); + } + return complete; } } // end unnamed namespace template<class Allocator> template<bool FORWARD, class DataHandle> void VariableSizeCommunicator<Allocator>::setupInterfaceTrackers(DataHandle& handle, -std::vector<InterfaceTracker>& send_trackers, -std::vector<InterfaceTracker>& recv_trackers) + std::vector<InterfaceTracker>& send_trackers, + std::vector<InterfaceTracker>& recv_trackers) { -if(interface_->size()==0) -return; -send_trackers.reserve(interface_->size()); -recv_trackers.reserve(interface_->size()); + if(interface_->size()==0) + return; + send_trackers.reserve(interface_->size()); + recv_trackers.reserve(interface_->size()); -int fixedsize=0; -if(handle.fixedsize()) -++fixedsize; + int fixedsize=0; + if(handle.fixedsize()) + ++fixedsize; -typedef typename InterfaceMap::const_iterator IIter; -for(IIter inf=interface_->begin(), end=interface_->end(); inf!=end; ++inf) -{ + typedef typename InterfaceMap::const_iterator IIter; + for(IIter inf=interface_->begin(), end=interface_->end(); inf!=end; ++inf) + { -if(handle.fixedsize() && InterfaceInformationChooser<FORWARD>::getSend(inf->second).size()) -fixedsize=handle.size(InterfaceInformationChooser<FORWARD>::getSend(inf->second)[0]); -assert(!handle.fixedsize()||fixedsize>0); -send_trackers.push_back(InterfaceTracker(inf->first, -InterfaceInformationChooser<FORWARD>::getSend(inf->second), fixedsize)); -recv_trackers.push_back(InterfaceTracker(inf->first, -InterfaceInformationChooser<FORWARD>::getReceive(inf->second), fixedsize, fixedsize==0)); -} + if(handle.fixedsize() && InterfaceInformationChooser<FORWARD>::getSend(inf->second).size()) + fixedsize=handle.size(InterfaceInformationChooser<FORWARD>::getSend(inf->second)[0]); + assert(!handle.fixedsize()||fixedsize>0); + send_trackers.push_back(InterfaceTracker(inf->first, + InterfaceInformationChooser<FORWARD>::getSend(inf->second), fixedsize)); + recv_trackers.push_back(InterfaceTracker(inf->first, + InterfaceInformationChooser<FORWARD>::getReceive(inf->second), fixedsize, fixedsize==0)); + } } template<class Allocator> template<bool FORWARD, class DataHandle> void VariableSizeCommunicator<Allocator>::communicateFixedSize(DataHandle& handle) { -std::vector<MPI_Request> size_send_req(interface_->size()); -std::vector<MPI_Request> size_recv_req(interface_->size()); + std::vector<MPI_Request> size_send_req(interface_->size()); + std::vector<MPI_Request> size_recv_req(interface_->size()); -std::vector<InterfaceTracker> send_trackers; -std::vector<InterfaceTracker> recv_trackers; -setupInterfaceTrackers<FORWARD>(handle,send_trackers, recv_trackers); -sendFixedSize(send_trackers, size_send_req, recv_trackers, size_recv_req, communicator_); + std::vector<InterfaceTracker> send_trackers; + std::vector<InterfaceTracker> recv_trackers; + setupInterfaceTrackers<FORWARD>(handle,send_trackers, recv_trackers); + sendFixedSize(send_trackers, size_send_req, recv_trackers, size_recv_req, communicator_); -std::vector<MPI_Request> data_send_req(interface_->size(), MPI_REQUEST_NULL); -std::vector<MPI_Request> data_recv_req(interface_->size(), MPI_REQUEST_NULL); -typedef typename DataHandle::DataType DataType; -std::vector<MessageBuffer<DataType> > send_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)), -recv_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)); + std::vector<MPI_Request> data_send_req(interface_->size(), MPI_REQUEST_NULL); + std::vector<MPI_Request> data_recv_req(interface_->size(), MPI_REQUEST_NULL); + typedef typename DataHandle::DataType DataType; + std::vector<MessageBuffer<DataType> > send_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)), + recv_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)); -setupRequests(handle, send_trackers, send_buffers, data_send_req, -SetupSendRequest<DataHandle>(), communicator_); + setupRequests(handle, send_trackers, send_buffers, data_send_req, + SetupSendRequest<DataHandle>(), communicator_); -std::size_t no_size_to_recv, no_to_send, no_to_recv, old_size; -no_size_to_recv = no_to_send = no_to_recv = old_size = interface_->size(); + std::size_t no_size_to_recv, no_to_send, no_to_recv, old_size; + no_size_to_recv = no_to_send = no_to_recv = old_size = interface_->size(); -// Skip empty interfaces. -typedef typename std::vector<InterfaceTracker>::const_iterator Iter; -for(Iter i=recv_trackers.begin(), end=recv_trackers.end(); i!=end; ++i) -if(i->empty()) ---no_to_recv; -for(Iter i=send_trackers.begin(), end=send_trackers.end(); i!=end; ++i) -if(i->empty()) ---no_to_send; + // Skip empty interfaces. + typedef typename std::vector<InterfaceTracker>::const_iterator Iter; + for(Iter i=recv_trackers.begin(), end=recv_trackers.end(); i!=end; ++i) + if(i->empty()) + --no_to_recv; + for(Iter i=send_trackers.begin(), end=send_trackers.end(); i!=end; ++i) + if(i->empty()) + --no_to_send; -while(no_size_to_recv+no_to_send+no_to_recv) -{ -// Receive the fixedsize and setup receives accordingly -if(no_size_to_recv) -no_size_to_recv -= receiveSizeAndSetupReceive(handle,recv_trackers, size_recv_req, -data_recv_req, recv_buffers, -communicator_); - -// Check send completion and initiate other necessary sends -if(no_to_send) -no_to_send -= checkSendAndContinueSending(handle, send_trackers, data_send_req, -send_buffers, communicator_); -if(validRecvRequests(data_recv_req)) -// Receive data and setup new unblocking receives if necessary -no_to_recv -= checkReceiveAndContinueReceiving(handle, recv_trackers, data_recv_req, -recv_buffers, communicator_); -} + while(no_size_to_recv+no_to_send+no_to_recv) + { + // Receive the fixedsize and setup receives accordingly + if(no_size_to_recv) + no_size_to_recv -= receiveSizeAndSetupReceive(handle,recv_trackers, size_recv_req, + data_recv_req, recv_buffers, + communicator_); + + // Check send completion and initiate other necessary sends + if(no_to_send) + no_to_send -= checkSendAndContinueSending(handle, send_trackers, data_send_req, + send_buffers, communicator_); + if(validRecvRequests(data_recv_req)) + // Receive data and setup new unblocking receives if necessary + no_to_recv -= checkReceiveAndContinueReceiving(handle, recv_trackers, data_recv_req, + recv_buffers, communicator_); + } -// Wait for completion of sending the size. -//std::vector<MPI_Status> statuses(interface_->size(), MPI_STATUSES_IGNORE); -MPI_Waitall(size_send_req.size(), &(size_send_req[0]), MPI_STATUSES_IGNORE); + // Wait for completion of sending the size. + //std::vector<MPI_Status> statuses(interface_->size(), MPI_STATUSES_IGNORE); + MPI_Waitall(size_send_req.size(), &(size_send_req[0]), MPI_STATUSES_IGNORE); } template<class Allocator> template<bool FORWARD, class DataHandle> void VariableSizeCommunicator<Allocator>::communicateSizes(DataHandle& handle, -std::vector<InterfaceTracker>& data_recv_trackers) -{ -std::vector<InterfaceTracker> send_trackers; -std::vector<InterfaceTracker> recv_trackers; -std::size_t size = interface_->size(); -std::vector<MPI_Request> send_requests(size, MPI_REQUEST_NULL); -std::vector<MPI_Request> recv_requests(size, MPI_REQUEST_NULL); -std::vector<MessageBuffer<std::size_t> > -send_buffers(size, MessageBuffer<std::size_t>(maxBufferSize_)), -recv_buffers(size, MessageBuffer<std::size_t>(maxBufferSize_)); -SizeDataHandle<DataHandle> size_handle(handle,data_recv_trackers); -setupInterfaceTrackers<FORWARD>(size_handle,send_trackers, recv_trackers); -setupRequests(size_handle, send_trackers, send_buffers, send_requests, -SetupSendRequest<SizeDataHandle<DataHandle> >(), communicator_); -setupRequests(size_handle, recv_trackers, recv_buffers, recv_requests, -SetupRecvRequest<SizeDataHandle<DataHandle> >(), communicator_); - -// Count valid requests that we have to wait for. -auto valid_req_func = -[](const MPI_Request& req) { return req != MPI_REQUEST_NULL; }; - -auto size_to_send = std::count_if(send_requests.begin(), send_requests.end(), -valid_req_func); -auto size_to_recv = std::count_if(recv_requests.begin(), recv_requests.end(), -valid_req_func); - -while(size_to_send+size_to_recv) -{ -if(size_to_send) -size_to_send -= -checkSendAndContinueSending(size_handle, send_trackers, send_requests, -send_buffers, communicator_); -if(size_to_recv) -// Could have done this using checkSendAndContinueSending -// But the call below is more efficient as UnpackSizeEntries -// uses std::copy. -size_to_recv -= -checkAndContinue(size_handle, recv_trackers, recv_requests, recv_requests, -recv_buffers, communicator_, UnpackSizeEntries<DataHandle>(), -SetupRecvRequest<SizeDataHandle<DataHandle> >()); -} + std::vector<InterfaceTracker>& data_recv_trackers) +{ + std::vector<InterfaceTracker> send_trackers; + std::vector<InterfaceTracker> recv_trackers; + std::size_t size = interface_->size(); + std::vector<MPI_Request> send_requests(size, MPI_REQUEST_NULL); + std::vector<MPI_Request> recv_requests(size, MPI_REQUEST_NULL); + std::vector<MessageBuffer<std::size_t> > + send_buffers(size, MessageBuffer<std::size_t>(maxBufferSize_)), + recv_buffers(size, MessageBuffer<std::size_t>(maxBufferSize_)); + SizeDataHandle<DataHandle> size_handle(handle,data_recv_trackers); + setupInterfaceTrackers<FORWARD>(size_handle,send_trackers, recv_trackers); + setupRequests(size_handle, send_trackers, send_buffers, send_requests, + SetupSendRequest<SizeDataHandle<DataHandle> >(), communicator_); + setupRequests(size_handle, recv_trackers, recv_buffers, recv_requests, + SetupRecvRequest<SizeDataHandle<DataHandle> >(), communicator_); + + // Count valid requests that we have to wait for. + auto valid_req_func = + [](const MPI_Request& req) { return req != MPI_REQUEST_NULL; }; + + auto size_to_send = std::count_if(send_requests.begin(), send_requests.end(), + valid_req_func); + auto size_to_recv = std::count_if(recv_requests.begin(), recv_requests.end(), + valid_req_func); + + while(size_to_send+size_to_recv) + { + if(size_to_send) + size_to_send -= + checkSendAndContinueSending(size_handle, send_trackers, send_requests, + send_buffers, communicator_); + if(size_to_recv) + // Could have done this using checkSendAndContinueSending + // But the call below is more efficient as UnpackSizeEntries + // uses std::copy. + size_to_recv -= + checkAndContinue(size_handle, recv_trackers, recv_requests, recv_requests, + recv_buffers, communicator_, UnpackSizeEntries<DataHandle>(), + SetupRecvRequest<SizeDataHandle<DataHandle> >()); + } } template<class Allocator> @@ -1166,58 +1166,58 @@ template<bool FORWARD, class DataHandle> void VariableSizeCommunicator<Allocator>::communicateVariableSize(DataHandle& handle) { -std::vector<InterfaceTracker> send_trackers; -std::vector<InterfaceTracker> recv_trackers; -setupInterfaceTrackers<FORWARD>(handle, send_trackers, recv_trackers); - -std::vector<MPI_Request> send_requests(interface_->size(), MPI_REQUEST_NULL); -std::vector<MPI_Request> recv_requests(interface_->size(), MPI_REQUEST_NULL); -typedef typename DataHandle::DataType DataType; -std::vector<MessageBuffer<DataType> > -send_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)), -recv_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)); - -communicateSizes<FORWARD>(handle, recv_trackers); -// Setup requests for sending and receiving. -setupRequests(handle, send_trackers, send_buffers, send_requests, -SetupSendRequest<DataHandle>(), communicator_); -setupRequests(handle, recv_trackers, recv_buffers, recv_requests, -SetupRecvRequest<DataHandle>(), communicator_); - -// Determine number of valid requests. -auto valid_req_func = -[](const MPI_Request& req) { return req != MPI_REQUEST_NULL;}; - -auto no_to_send = std::count_if(send_requests.begin(), send_requests.end(), -valid_req_func); -auto no_to_recv = std::count_if(recv_requests.begin(), recv_requests.end(), -valid_req_func); -while(no_to_send+no_to_recv) -{ -// Check send completion and initiate other necessary sends -if(no_to_send) -no_to_send -= checkSendAndContinueSending(handle, send_trackers, send_requests, -send_buffers, communicator_); -if(no_to_recv) -// Receive data and setup new unblocking receives if necessary -no_to_recv -= checkReceiveAndContinueReceiving(handle, recv_trackers, recv_requests, -recv_buffers, communicator_); -} + std::vector<InterfaceTracker> send_trackers; + std::vector<InterfaceTracker> recv_trackers; + setupInterfaceTrackers<FORWARD>(handle, send_trackers, recv_trackers); + + std::vector<MPI_Request> send_requests(interface_->size(), MPI_REQUEST_NULL); + std::vector<MPI_Request> recv_requests(interface_->size(), MPI_REQUEST_NULL); + typedef typename DataHandle::DataType DataType; + std::vector<MessageBuffer<DataType> > + send_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)), + recv_buffers(interface_->size(), MessageBuffer<DataType>(maxBufferSize_)); + + communicateSizes<FORWARD>(handle, recv_trackers); + // Setup requests for sending and receiving. + setupRequests(handle, send_trackers, send_buffers, send_requests, + SetupSendRequest<DataHandle>(), communicator_); + setupRequests(handle, recv_trackers, recv_buffers, recv_requests, + SetupRecvRequest<DataHandle>(), communicator_); + + // Determine number of valid requests. + auto valid_req_func = + [](const MPI_Request& req) { return req != MPI_REQUEST_NULL;}; + + auto no_to_send = std::count_if(send_requests.begin(), send_requests.end(), + valid_req_func); + auto no_to_recv = std::count_if(recv_requests.begin(), recv_requests.end(), + valid_req_func); + while(no_to_send+no_to_recv) + { + // Check send completion and initiate other necessary sends + if(no_to_send) + no_to_send -= checkSendAndContinueSending(handle, send_trackers, send_requests, + send_buffers, communicator_); + if(no_to_recv) + // Receive data and setup new unblocking receives if necessary + no_to_recv -= checkReceiveAndContinueReceiving(handle, recv_trackers, recv_requests, + recv_buffers, communicator_); + } } template<class Allocator> template<bool FORWARD, class DataHandle> void VariableSizeCommunicator<Allocator>::communicate(DataHandle& handle) { -if( interface_->size() == 0) -// Simply return as otherwise we will index an empty container -// either for MPI_Wait_all or MPI_Test_some. -return; - -if(handle.fixedsize()) -communicateFixedSize<FORWARD>(handle); -else -communicateVariableSize<FORWARD>(handle); + if( interface_->size() == 0) + // Simply return as otherwise we will index an empty container + // either for MPI_Wait_all or MPI_Test_some. + return; + + if(handle.fixedsize()) + communicateFixedSize<FORWARD>(handle); + else + communicateVariableSize<FORWARD>(handle); } } // end namespace Dune diff --git a/dune/common/parameterizedobject.hh b/dune/common/parameterizedobject.hh index 35ba15b77f2e63da1810e59b8e731d0b760857b0..d515dff2ff11d81bd973b516431bd3ecca6812a0 100644 --- a/dune/common/parameterizedobject.hh +++ b/dune/common/parameterizedobject.hh @@ -14,176 +14,176 @@ namespace Dune { /** -* @brief A factory class for parameterized objects. -* -* It allows the construction of objects adhering to a certain interface that -* might be constructed quite differently for one another. -* -* The Signature parameter defined the "virtual" constructor signature -* in the form of Interface(Args...), where Interface is the type of -* the (abstract) interface class and Args... is the set of -* constrcutor parameters. -* -* Each type constructed by this factory is identified by a different key. This class -* allows for easy registration of type with new keys. -* -* @tparam Signature Signature of the "virtual" constructor call in the form for Interface(Args...). For default constructors one can omit the ()-brackets. -* @tparam KeyT The type of the objects that are used as keys in the lookup [DEFAULT: std::string]. -*/ + * @brief A factory class for parameterized objects. + * + * It allows the construction of objects adhering to a certain interface that + * might be constructed quite differently for one another. + * + * The Signature parameter defined the "virtual" constructor signature + * in the form of Interface(Args...), where Interface is the type of + * the (abstract) interface class and Args... is the set of + * constrcutor parameters. + * + * Each type constructed by this factory is identified by a different key. This class + * allows for easy registration of type with new keys. + * + * @tparam Signature Signature of the "virtual" constructor call in the form for Interface(Args...). For default constructors one can omit the ()-brackets. + * @tparam KeyT The type of the objects that are used as keys in the lookup [DEFAULT: std::string]. + */ template<typename Signature, -typename KeyT = std::string> + typename KeyT = std::string> class ParameterizedObjectFactory; template<typename TypeT, -typename KeyT, -typename... Args> + typename KeyT, + typename... Args> class ParameterizedObjectFactory<TypeT(Args...), KeyT> { -public: - -/** @brief The typ of the keys. */ -typedef KeyT Key; - -/** @brief The type of objects created by the factory. */ -using Type = TypeT; - -protected: - -using Creator = std::function<Type(Args...)>; - -template<class F> -static constexpr auto has_proper_signature(Dune::PriorityTag<1>) --> decltype( std::declval<F>()(std::declval<Args>()...), std::true_type()) -{ -return {}; -} - -template<class F> -static constexpr std::false_type has_proper_signature(Dune::PriorityTag<0>) -{ -return {}; -} - -public: - -/** -* @brief Creates an object identified by a key from given parameters -* -* @param key The key the object is registered with @see define. -* @param args The parameters used for the construction. -* @return The object wrapped as Type -*/ -Type create(Key const& key, Args ... args) const { -typename Registry::const_iterator i = registry_.find(key); -if (i == registry_.end()) { -DUNE_THROW(Dune::InvalidStateException, -"ParametrizedObjectFactory: key ``" << -key << "'' not registered"); -} -else return i->second(args...); -} - -/** -* @brief Registers a new type with a key. -* -* After registration objects of this type can be constructed with the -* specified key using a matching default creation function. If Type -* is a unique_ptr or shared_ptr, the object is created via make_unique -* or make_shared, respectively. Otherwise a constructor of Impl -* is called. -* -* @tparam Impl The type of objects to create. -* -* @param key The key associated with this type. -*/ -template<class Impl> -void define(Key const& key) -{ -registry_[key] = DefaultCreator<Impl>(); -} - -/** -* @brief Registers a new creator with a key. -* -* After registration objects can be constructed using -* the given creator function. -* -* @tparam F Type of creator function. This must be callable with Args... . -* -* @param key The key associated with this type. -* @param f Function for creation of objects of type Impl -* -* \todo Replace has_proper_signature by concept check -*/ -template<class F, -typename std::enable_if<has_proper_signature<F>(PriorityTag<42>()), int>::type = 0> -void define(Key const& key, F&& f) -{ -registry_[key] = f; -} - -/** -* @brief Registers a new type with a key. -* -* After registration objects of this type can be created. -* This method will store a copy of the given object and -* create will hand out a copy to this. -* -* @tparam Impl The type of objects to create. -* -* @param key The key associated with this type. -* @param t reference object, "create" will call the copy-constructor -* -* note, this does not work fundamental types -*/ -template<class Impl, -typename std::enable_if< -std::is_convertible<Impl, Type>::value -and not std::is_convertible<Impl, Creator>::value, -int>::type = 0> -void define(Key const& key, Impl&& t) -{ -registry_[key] = [=](Args...) { return t;}; -} - -bool contains(Key const& key) const -{ -return registry_.count(key); -} - -private: - -template<class T> -struct Tag{}; - -template<class Impl> -struct DefaultCreator -{ -template<class... T> -Type operator()(T&&... args) const -{ -return DefaultCreator::create(Tag<Type>(), PriorityTag<42>(), std::forward<T>(args)...); -} - -template<class Target, class... T> -static Type create(Tag<Target>, PriorityTag<1>, T&& ... args) { -return Impl(std::forward<T>(args)...); -} - -template<class Target, class... T> -static Type create(Tag<std::unique_ptr<Target>>, PriorityTag<2>, T&& ... args) { -return std::make_unique<Impl>(std::forward<T>(args)...); -} - -template<class Target, class... T> -static Type create(Tag<std::shared_ptr<Target>>, PriorityTag<3>, T&& ... args) { -return std::make_shared<Impl>(std::forward<T>(args)...); -} - -}; - -typedef std::map<Key, Creator> Registry; -Registry registry_; + public: + + /** @brief The typ of the keys. */ + typedef KeyT Key; + + /** @brief The type of objects created by the factory. */ + using Type = TypeT; + + protected: + + using Creator = std::function<Type(Args...)>; + + template<class F> + static constexpr auto has_proper_signature(Dune::PriorityTag<1>) + -> decltype( std::declval<F>()(std::declval<Args>()...), std::true_type()) + { + return {}; + } + + template<class F> + static constexpr std::false_type has_proper_signature(Dune::PriorityTag<0>) + { + return {}; + } + + public: + + /** + * @brief Creates an object identified by a key from given parameters + * + * @param key The key the object is registered with @see define. + * @param args The parameters used for the construction. + * @return The object wrapped as Type + */ + Type create(Key const& key, Args ... args) const { + typename Registry::const_iterator i = registry_.find(key); + if (i == registry_.end()) { + DUNE_THROW(Dune::InvalidStateException, + "ParametrizedObjectFactory: key ``" << + key << "'' not registered"); + } + else return i->second(args...); + } + + /** + * @brief Registers a new type with a key. + * + * After registration objects of this type can be constructed with the + * specified key using a matching default creation function. If Type + * is a unique_ptr or shared_ptr, the object is created via make_unique + * or make_shared, respectively. Otherwise a constructor of Impl + * is called. + * + * @tparam Impl The type of objects to create. + * + * @param key The key associated with this type. + */ + template<class Impl> + void define(Key const& key) + { + registry_[key] = DefaultCreator<Impl>(); + } + + /** + * @brief Registers a new creator with a key. + * + * After registration objects can be constructed using + * the given creator function. + * + * @tparam F Type of creator function. This must be callable with Args... . + * + * @param key The key associated with this type. + * @param f Function for creation of objects of type Impl + * + * \todo Replace has_proper_signature by concept check + */ + template<class F, + typename std::enable_if<has_proper_signature<F>(PriorityTag<42>()), int>::type = 0> + void define(Key const& key, F&& f) + { + registry_[key] = f; + } + + /** + * @brief Registers a new type with a key. + * + * After registration objects of this type can be created. + * This method will store a copy of the given object and + * create will hand out a copy to this. + * + * @tparam Impl The type of objects to create. + * + * @param key The key associated with this type. + * @param t reference object, "create" will call the copy-constructor + * + * note, this does not work fundamental types + */ + template<class Impl, + typename std::enable_if< + std::is_convertible<Impl, Type>::value + and not std::is_convertible<Impl, Creator>::value, + int>::type = 0> + void define(Key const& key, Impl&& t) + { + registry_[key] = [=](Args...) { return t;}; + } + + bool contains(Key const& key) const + { + return registry_.count(key); + } + + private: + + template<class T> + struct Tag{}; + + template<class Impl> + struct DefaultCreator + { + template<class... T> + Type operator()(T&&... args) const + { + return DefaultCreator::create(Tag<Type>(), PriorityTag<42>(), std::forward<T>(args)...); + } + + template<class Target, class... T> + static Type create(Tag<Target>, PriorityTag<1>, T&& ... args) { + return Impl(std::forward<T>(args)...); + } + + template<class Target, class... T> + static Type create(Tag<std::unique_ptr<Target>>, PriorityTag<2>, T&& ... args) { + return std::make_unique<Impl>(std::forward<T>(args)...); + } + + template<class Target, class... T> + static Type create(Tag<std::shared_ptr<Target>>, PriorityTag<3>, T&& ... args) { + return std::make_shared<Impl>(std::forward<T>(args)...); + } + + }; + + typedef std::map<Key, Creator> Registry; + Registry registry_; }; diff --git a/dune/common/parametertree.hh b/dune/common/parametertree.hh index 27209a255f0d092fb151023caef84dacb50e095b..343a217c8296afbb8750676e22868589884e76da 100644 --- a/dune/common/parametertree.hh +++ b/dune/common/parametertree.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief A hierarchical structure of string parameters -*/ + * \brief A hierarchical structure of string parameters + */ #include <array> #include <cstddef> @@ -29,330 +29,330 @@ namespace Dune { -/** \brief Hierarchical structure of string parameters -* \ingroup Common -*/ -class ParameterTree -{ -// class providing a single static parse() function, used by the -// generic get() method -template<typename T> -struct Parser; - -public: - -/** \brief storage for key lists -*/ -typedef std::vector<std::string> KeyVector; - -/** \brief Create new empty ParameterTree -*/ -ParameterTree(); - - -/** \brief test for key -* -* Tests whether given key exists. -* -* \param key key name -* \return true if key exists in structure, otherwise false -*/ -bool hasKey(const std::string& key) const; - - -/** \brief test for substructure -* -* Tests whether given substructure exists. -* -* \param sub substructure name -* \return true if substructure exists in structure, otherwise false -*/ -bool hasSub(const std::string& sub) const; - - -/** \brief get value reference for key -* -* Returns reference to value for given key name. -* This creates the key, if not existent. -* -* \param key key name -* \return reference to corresponding value -*/ -std::string& operator[] (const std::string& key); - - -/** \brief get value reference for key -* -* Returns reference to value for given key name. -* This creates the key, if not existent. -* -* \param key key name -* \return reference to corresponding value -* \throw Dune::RangeError if key is not found -*/ -const std::string& operator[] (const std::string& key) const; - - -/** \brief print distinct substructure to stream -* -* Prints all entries with given prefix. -* -* \param stream Stream to print to -* \param prefix for key and substructure names -*/ -void report(std::ostream& stream = std::cout, -const std::string& prefix = "") const; - - -/** \brief get substructure by name -* -* \param sub substructure name -* \return reference to substructure -*/ -ParameterTree& sub(const std::string& sub); - - -/** \brief get const substructure by name -* -* \param sub substructure name -* \param fail_if_missing if true, throw an error if substructure is missing -* \return reference to substructure -*/ -const ParameterTree& sub(const std::string& sub, bool fail_if_missing = false) const; - - -/** \brief get value as string -* -* Returns pure string value for given key. -* -* \param key key name -* \param defaultValue default if key does not exist -* \return value as string -*/ -std::string get(const std::string& key, const std::string& defaultValue) const; - -/** \brief get value as string -* -* Returns pure string value for given key. -* -* \todo This is a hack so get("my_key", "xyz") compiles -* (without this method "xyz" resolves to bool instead of std::string) -* \param key key name -* \param defaultValue default if key does not exist -* \return value as string -*/ -std::string get(const std::string& key, const char* defaultValue) const; - - -/** \brief get value converted to a certain type -* -* Returns value as type T for given key. -* -* \tparam T type of returned value. -* \param key key name -* \param defaultValue default if key does not exist -* \return value converted to T -*/ -template<typename T> -T get(const std::string& key, const T& defaultValue) const { -if(hasKey(key)) -return get<T>(key); -else -return defaultValue; -} - -/** \brief Get value -* -* \tparam T Type of the value -* \param key Key name -* \throws RangeError if key does not exist -* \throws NotImplemented Type is not supported -* \return value as T -*/ -template <class T> -T get(const std::string& key) const { -if(not hasKey(key)) -DUNE_THROW(Dune::RangeError, "Key '" << key -<< "' not found in ParameterTree (prefix " + prefix_ + ")"); -try { -return Parser<T>::parse((*this)[key]); -} -catch(const RangeError& e) { -// rethrow the error and add more information -DUNE_THROW(RangeError, "Cannot parse value \"" << (*this)[key] -<< "\" for key \"" << prefix_ << "." << key << "\"" -<< e.what()); -} -} - -/** \brief get value keys -* -* Returns a vector of all keys associated to (key,values) entries in -* order of appearance -* -* \return reference to entry vector -*/ -const KeyVector& getValueKeys() const; - - -/** \brief get substructure keys -* -* Returns a vector of all keys associated to (key,substructure) entries -* in order of appearance -* -* \return reference to entry vector -*/ -const KeyVector& getSubKeys() const; - -protected: - -static const ParameterTree empty_; - -std::string prefix_; - -KeyVector valueKeys_; -KeyVector subKeys_; - -std::map<std::string, std::string> values_; -std::map<std::string, ParameterTree> subs_; - -static std::string ltrim(const std::string& s); -static std::string rtrim(const std::string& s); -static std::vector<std::string> split(const std::string & s); - -// parse into a fixed-size range of iterators -template<class Iterator> -static void parseRange(const std::string &str, -Iterator it, const Iterator &end) -{ -typedef typename std::iterator_traits<Iterator>::value_type Value; -std::istringstream s(str); -// make sure we are in locale "C" -s.imbue(std::locale::classic()); -std::size_t n = 0; -for(; it != end; ++it, ++n) { -s >> *it; -if(!s) -DUNE_THROW(RangeError, "as a range of items of type " -<< className<Value>() -<< " (" << n << " items were extracted successfully)"); -} -Value dummy; -s >> dummy; -// now extraction should have failed, and eof should be set -if(not s.fail() or not s.eof()) -DUNE_THROW(RangeError, "as a range of " -<< n << " items of type " -<< className<Value>() << " (more items than the range can hold)"); -} -}; - -template<typename T> -struct ParameterTree::Parser { -static T parse(const std::string& str) { -T val; -std::istringstream s(str); -// make sure we are in locale "C" -s.imbue(std::locale::classic()); -s >> val; -if(!s) -DUNE_THROW(RangeError, " as a " << className<T>()); -char dummy; -s >> dummy; -// now extraction should have failed, and eof should be set -if ((! s.fail()) || (! s.eof())) -DUNE_THROW(RangeError, " as a " << className<T>()); -return val; -} -}; - -// "How do I convert a string into a wstring in C++?" "Why, that very simple -// son. You just need a these hundred lines of code." -// Instead im gonna restrict myself to string with charT=char here -template<typename traits, typename Allocator> -struct ParameterTree::Parser<std::basic_string<char, traits, Allocator> > { -static std::basic_string<char, traits, Allocator> -parse(const std::string& str) { -std::string trimmed = ltrim(rtrim(str)); -return std::basic_string<char, traits, Allocator>(trimmed.begin(), -trimmed.end()); -} -}; - -template<> -struct ParameterTree::Parser< bool > { -struct ToLower { -char operator()(char c) -{ -return std::tolower(c, std::locale::classic()); -} -}; - -static bool -parse(const std::string& str) { -std::string ret = str; - -std::transform(ret.begin(), ret.end(), ret.begin(), ToLower()); - -if (ret == "yes" || ret == "true") -return true; - -if (ret == "no" || ret == "false") -return false; - -return (Parser<int>::parse(ret) != 0); -} -}; - -template<typename T, int n> -struct ParameterTree::Parser<FieldVector<T, n> > { -static FieldVector<T, n> -parse(const std::string& str) { -FieldVector<T, n> val; -parseRange(str, val.begin(), val.end()); -return val; -} -}; - -template<typename T, std::size_t n> -struct ParameterTree::Parser<std::array<T, n> > { -static std::array<T, n> -parse(const std::string& str) { -std::array<T, n> val; -parseRange(str, val.begin(), val.end()); -return val; -} -}; - -template<std::size_t n> -struct ParameterTree::Parser<std::bitset<n> > { -static std::bitset<n> -parse(const std::string& str) { -std::bitset<n> val; -std::vector<std::string> sub = split(str); -if (sub.size() != n) -DUNE_THROW(RangeError, "as a bitset<" << n << "> " -<< "because of unmatching size " << sub.size()); -for (std::size_t i=0; i<n; ++i) { -val[i] = ParameterTree::Parser<bool>::parse(sub[i]); -} -return val; -} -}; - -template<typename T, typename A> -struct ParameterTree::Parser<std::vector<T, A> > { -static std::vector<T, A> -parse(const std::string& str) { -std::vector<std::string> sub = split(str); -std::vector<T, A> vec; -for (unsigned int i=0; i<sub.size(); ++i) { -T val = ParameterTree::Parser<T>::parse(sub[i]); -vec.push_back(val); -} -return vec; -} -}; + /** \brief Hierarchical structure of string parameters + * \ingroup Common + */ + class ParameterTree + { + // class providing a single static parse() function, used by the + // generic get() method + template<typename T> + struct Parser; + + public: + + /** \brief storage for key lists + */ + typedef std::vector<std::string> KeyVector; + + /** \brief Create new empty ParameterTree + */ + ParameterTree(); + + + /** \brief test for key + * + * Tests whether given key exists. + * + * \param key key name + * \return true if key exists in structure, otherwise false + */ + bool hasKey(const std::string& key) const; + + + /** \brief test for substructure + * + * Tests whether given substructure exists. + * + * \param sub substructure name + * \return true if substructure exists in structure, otherwise false + */ + bool hasSub(const std::string& sub) const; + + + /** \brief get value reference for key + * + * Returns reference to value for given key name. + * This creates the key, if not existent. + * + * \param key key name + * \return reference to corresponding value + */ + std::string& operator[] (const std::string& key); + + + /** \brief get value reference for key + * + * Returns reference to value for given key name. + * This creates the key, if not existent. + * + * \param key key name + * \return reference to corresponding value + * \throw Dune::RangeError if key is not found + */ + const std::string& operator[] (const std::string& key) const; + + + /** \brief print distinct substructure to stream + * + * Prints all entries with given prefix. + * + * \param stream Stream to print to + * \param prefix for key and substructure names + */ + void report(std::ostream& stream = std::cout, + const std::string& prefix = "") const; + + + /** \brief get substructure by name + * + * \param sub substructure name + * \return reference to substructure + */ + ParameterTree& sub(const std::string& sub); + + + /** \brief get const substructure by name + * + * \param sub substructure name + * \param fail_if_missing if true, throw an error if substructure is missing + * \return reference to substructure + */ + const ParameterTree& sub(const std::string& sub, bool fail_if_missing = false) const; + + + /** \brief get value as string + * + * Returns pure string value for given key. + * + * \param key key name + * \param defaultValue default if key does not exist + * \return value as string + */ + std::string get(const std::string& key, const std::string& defaultValue) const; + + /** \brief get value as string + * + * Returns pure string value for given key. + * + * \todo This is a hack so get("my_key", "xyz") compiles + * (without this method "xyz" resolves to bool instead of std::string) + * \param key key name + * \param defaultValue default if key does not exist + * \return value as string + */ + std::string get(const std::string& key, const char* defaultValue) const; + + + /** \brief get value converted to a certain type + * + * Returns value as type T for given key. + * + * \tparam T type of returned value. + * \param key key name + * \param defaultValue default if key does not exist + * \return value converted to T + */ + template<typename T> + T get(const std::string& key, const T& defaultValue) const { + if(hasKey(key)) + return get<T>(key); + else + return defaultValue; + } + + /** \brief Get value + * + * \tparam T Type of the value + * \param key Key name + * \throws RangeError if key does not exist + * \throws NotImplemented Type is not supported + * \return value as T + */ + template <class T> + T get(const std::string& key) const { + if(not hasKey(key)) + DUNE_THROW(Dune::RangeError, "Key '" << key + << "' not found in ParameterTree (prefix " + prefix_ + ")"); + try { + return Parser<T>::parse((*this)[key]); + } + catch(const RangeError& e) { + // rethrow the error and add more information + DUNE_THROW(RangeError, "Cannot parse value \"" << (*this)[key] + << "\" for key \"" << prefix_ << "." << key << "\"" + << e.what()); + } + } + + /** \brief get value keys + * + * Returns a vector of all keys associated to (key,values) entries in + * order of appearance + * + * \return reference to entry vector + */ + const KeyVector& getValueKeys() const; + + + /** \brief get substructure keys + * + * Returns a vector of all keys associated to (key,substructure) entries + * in order of appearance + * + * \return reference to entry vector + */ + const KeyVector& getSubKeys() const; + + protected: + + static const ParameterTree empty_; + + std::string prefix_; + + KeyVector valueKeys_; + KeyVector subKeys_; + + std::map<std::string, std::string> values_; + std::map<std::string, ParameterTree> subs_; + + static std::string ltrim(const std::string& s); + static std::string rtrim(const std::string& s); + static std::vector<std::string> split(const std::string & s); + + // parse into a fixed-size range of iterators + template<class Iterator> + static void parseRange(const std::string &str, + Iterator it, const Iterator &end) + { + typedef typename std::iterator_traits<Iterator>::value_type Value; + std::istringstream s(str); + // make sure we are in locale "C" + s.imbue(std::locale::classic()); + std::size_t n = 0; + for(; it != end; ++it, ++n) { + s >> *it; + if(!s) + DUNE_THROW(RangeError, "as a range of items of type " + << className<Value>() + << " (" << n << " items were extracted successfully)"); + } + Value dummy; + s >> dummy; + // now extraction should have failed, and eof should be set + if(not s.fail() or not s.eof()) + DUNE_THROW(RangeError, "as a range of " + << n << " items of type " + << className<Value>() << " (more items than the range can hold)"); + } + }; + + template<typename T> + struct ParameterTree::Parser { + static T parse(const std::string& str) { + T val; + std::istringstream s(str); + // make sure we are in locale "C" + s.imbue(std::locale::classic()); + s >> val; + if(!s) + DUNE_THROW(RangeError, " as a " << className<T>()); + char dummy; + s >> dummy; + // now extraction should have failed, and eof should be set + if ((! s.fail()) || (! s.eof())) + DUNE_THROW(RangeError, " as a " << className<T>()); + return val; + } + }; + + // "How do I convert a string into a wstring in C++?" "Why, that very simple + // son. You just need a these hundred lines of code." + // Instead im gonna restrict myself to string with charT=char here + template<typename traits, typename Allocator> + struct ParameterTree::Parser<std::basic_string<char, traits, Allocator> > { + static std::basic_string<char, traits, Allocator> + parse(const std::string& str) { + std::string trimmed = ltrim(rtrim(str)); + return std::basic_string<char, traits, Allocator>(trimmed.begin(), + trimmed.end()); + } + }; + + template<> + struct ParameterTree::Parser< bool > { + struct ToLower { + char operator()(char c) + { + return std::tolower(c, std::locale::classic()); + } + }; + + static bool + parse(const std::string& str) { + std::string ret = str; + + std::transform(ret.begin(), ret.end(), ret.begin(), ToLower()); + + if (ret == "yes" || ret == "true") + return true; + + if (ret == "no" || ret == "false") + return false; + + return (Parser<int>::parse(ret) != 0); + } + }; + + template<typename T, int n> + struct ParameterTree::Parser<FieldVector<T, n> > { + static FieldVector<T, n> + parse(const std::string& str) { + FieldVector<T, n> val; + parseRange(str, val.begin(), val.end()); + return val; + } + }; + + template<typename T, std::size_t n> + struct ParameterTree::Parser<std::array<T, n> > { + static std::array<T, n> + parse(const std::string& str) { + std::array<T, n> val; + parseRange(str, val.begin(), val.end()); + return val; + } + }; + + template<std::size_t n> + struct ParameterTree::Parser<std::bitset<n> > { + static std::bitset<n> + parse(const std::string& str) { + std::bitset<n> val; + std::vector<std::string> sub = split(str); + if (sub.size() != n) + DUNE_THROW(RangeError, "as a bitset<" << n << "> " + << "because of unmatching size " << sub.size()); + for (std::size_t i=0; i<n; ++i) { + val[i] = ParameterTree::Parser<bool>::parse(sub[i]); + } + return val; + } + }; + + template<typename T, typename A> + struct ParameterTree::Parser<std::vector<T, A> > { + static std::vector<T, A> + parse(const std::string& str) { + std::vector<std::string> sub = split(str); + std::vector<T, A> vec; + for (unsigned int i=0; i<sub.size(); ++i) { + T val = ParameterTree::Parser<T>::parse(sub[i]); + vec.push_back(val); + } + return vec; + } + }; } // end namespace Dune diff --git a/dune/common/parametertreeparser.hh b/dune/common/parametertreeparser.hh index 6e033d519475c8a24f1332e3b4a6d07b29b7325e..abf81e7c97cfce0488bc05bde1333acf692bd311 100644 --- a/dune/common/parametertreeparser.hh +++ b/dune/common/parametertreeparser.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Various parser methods to get data into a ParameterTree object -*/ + * \brief Various parser methods to get data into a ParameterTree object + */ #include <istream> #include <string> @@ -17,161 +17,161 @@ namespace Dune { -/** \brief report parser error while reading ParameterTree */ -class ParameterTreeParserError : public RangeError {}; -/** \brief exception thrown if the user wants to see help string - -this exception is only thrown if the command line parameters -contain an option --help or -h -*/ -class HelpRequest : public Exception {}; - -/** \brief Parsers to set up a ParameterTree from various input sources -* \ingroup Common -* -*/ -class ParameterTreeParser -{ - -static std::string ltrim(const std::string& s); -static std::string rtrim(const std::string& s); - -public: - -/** @name Parsing methods for the INITree file format -* -* INITree files should look like this -* \verbatim -* # this file configures fruit colors in fruitsalad -* -* -* #these are no fruit but could also appear in fruit salad -* honeydewmelon = yellow -* watermelon = green -* -* fruit.tropicalfruit.orange = orange -* -* [fruit] -* strawberry = red -* pomegranate = red -* -* [fruit.pipfruit] -* apple = green/red/yellow -* pear = green -* -* [fruit.stonefruit] -* cherry = red -* plum = purple -* -* \endverbatim -* -* -* If a '[prefix]' statement appears all following entries use this prefix -* until the next '[prefix]' statement. Fruitsalads for example contain: -* \verbatim -* honeydewmelon = yellow -* fruit.tropicalfruit.orange = orange -* fruit.pipfruit.apple = green/red/yellow -* fruit.stonefruit.cherry = red -* \endverbatim -* -* All keys with a common 'prefix.' belong to the same substructure called -* 'prefix'. Leading and trailing spaces and tabs are removed from the -* values unless you use single or double quotes around them. Using single -* or double quotes you can also have multiline values. -*/ -//@{ - -/** \brief parse C++ stream -* -* Parses C++ stream and build hierarchical config structure. -* -* \param in The stream to parse -* \param[out] pt The parameter tree to store the config structure. -* \param overwrite Whether to overwrite already existing values. -* If false, values in the stream will be ignored -* if the key is already present. -* -* \note This method is identical to parseStream(std::istream&, -* const std::string&, bool) with the exception that that -* method allows one to give a custom name for the stream. -*/ -static void readINITree(std::istream& in, ParameterTree& pt, -bool overwrite); - - -/** \brief parse C++ stream -* -* Parses C++ stream and build hierarchical config structure. -* -* \param in The stream to parse -* \param[out] pt The parameter tree to store the config structure. -* \param srcname Name of the configuration source for error -* messages, "stdin" or a filename. -* \param overwrite Whether to overwrite already existing values. -* If false, values in the stream will be ignored -* if the key is already present. -*/ -static void readINITree(std::istream& in, ParameterTree& pt, -const std::string srcname = "stream", -bool overwrite = true); - - -/** \brief parse file -* -* Parses file with given name and build hierarchical config structure. -* -* \param file filename -* \param[out] pt The parameter tree to store the config structure. -* \param overwrite Whether to overwrite already existing values. -* If false, values in the stream will be ignored -* if the key is already present. -*/ -static void readINITree(std::string file, ParameterTree& pt, bool overwrite = true); - -//@} - -/** \brief parse command line options and build hierarchical ParameterTree structure -* -* The list of command line options is searched for pairs of the type <kbd>-key value</kbd> -* (note the hyphen in front of the key). -* For each such pair of options a key-value pair with the corresponding names -* is then created in the ParameterTree. -* -* \param argc arg count -* \param argv arg values -* \param[out] pt The parameter tree to store the config structure. -*/ -static void readOptions(int argc, char* argv [], ParameterTree& pt); - -/** -* \brief read [named] command line options and build hierarchical ParameterTree structure -* -* Similar to pythons named options we expect the parameters in the -* ordering induced by keywords, but allow the user to pass named options -* in the form of --key=value. Optionally the user can pass an additional -* vector with help strings. -* -* \param argc arg count -* \param argv arg values -* \param[out] pt The parameter tree to store the config structure. -* \param keywords vector with keywords names -* \param required number of required options (the first n keywords are required, default is all are required) -* \param allow_more allow more options than these listed in keywords (default = true) -* \param overwrite allow to overwrite existing options (default = true) -* \param help vector containing help strings -*/ -static void readNamedOptions(int argc, char* argv[], -ParameterTree& pt, -std::vector<std::string> keywords, -unsigned int required = std::numeric_limits<unsigned int>::max(), -bool allow_more = true, -bool overwrite = true, -std::vector<std::string> help = std::vector<std::string>()); - -private: -static std::string generateHelpString(std::string progname, std::vector<std::string> keywords, unsigned int required, std::vector<std::string> help); -}; + /** \brief report parser error while reading ParameterTree */ + class ParameterTreeParserError : public RangeError {}; + /** \brief exception thrown if the user wants to see help string + + this exception is only thrown if the command line parameters + contain an option --help or -h + */ + class HelpRequest : public Exception {}; + + /** \brief Parsers to set up a ParameterTree from various input sources + * \ingroup Common + * + */ + class ParameterTreeParser + { + + static std::string ltrim(const std::string& s); + static std::string rtrim(const std::string& s); + + public: + + /** @name Parsing methods for the INITree file format + * + * INITree files should look like this + * \verbatim + * # this file configures fruit colors in fruitsalad + * + * + * #these are no fruit but could also appear in fruit salad + * honeydewmelon = yellow + * watermelon = green + * + * fruit.tropicalfruit.orange = orange + * + * [fruit] + * strawberry = red + * pomegranate = red + * + * [fruit.pipfruit] + * apple = green/red/yellow + * pear = green + * + * [fruit.stonefruit] + * cherry = red + * plum = purple + * + * \endverbatim + * + * + * If a '[prefix]' statement appears all following entries use this prefix + * until the next '[prefix]' statement. Fruitsalads for example contain: + * \verbatim + * honeydewmelon = yellow + * fruit.tropicalfruit.orange = orange + * fruit.pipfruit.apple = green/red/yellow + * fruit.stonefruit.cherry = red + * \endverbatim + * + * All keys with a common 'prefix.' belong to the same substructure called + * 'prefix'. Leading and trailing spaces and tabs are removed from the + * values unless you use single or double quotes around them. Using single + * or double quotes you can also have multiline values. + */ + //@{ + + /** \brief parse C++ stream + * + * Parses C++ stream and build hierarchical config structure. + * + * \param in The stream to parse + * \param[out] pt The parameter tree to store the config structure. + * \param overwrite Whether to overwrite already existing values. + * If false, values in the stream will be ignored + * if the key is already present. + * + * \note This method is identical to parseStream(std::istream&, + * const std::string&, bool) with the exception that that + * method allows one to give a custom name for the stream. + */ + static void readINITree(std::istream& in, ParameterTree& pt, + bool overwrite); + + + /** \brief parse C++ stream + * + * Parses C++ stream and build hierarchical config structure. + * + * \param in The stream to parse + * \param[out] pt The parameter tree to store the config structure. + * \param srcname Name of the configuration source for error + * messages, "stdin" or a filename. + * \param overwrite Whether to overwrite already existing values. + * If false, values in the stream will be ignored + * if the key is already present. + */ + static void readINITree(std::istream& in, ParameterTree& pt, + const std::string srcname = "stream", + bool overwrite = true); + + + /** \brief parse file + * + * Parses file with given name and build hierarchical config structure. + * + * \param file filename + * \param[out] pt The parameter tree to store the config structure. + * \param overwrite Whether to overwrite already existing values. + * If false, values in the stream will be ignored + * if the key is already present. + */ + static void readINITree(std::string file, ParameterTree& pt, bool overwrite = true); + + //@} + + /** \brief parse command line options and build hierarchical ParameterTree structure + * + * The list of command line options is searched for pairs of the type <kbd>-key value</kbd> + * (note the hyphen in front of the key). + * For each such pair of options a key-value pair with the corresponding names + * is then created in the ParameterTree. + * + * \param argc arg count + * \param argv arg values + * \param[out] pt The parameter tree to store the config structure. + */ + static void readOptions(int argc, char* argv [], ParameterTree& pt); + + /** + * \brief read [named] command line options and build hierarchical ParameterTree structure + * + * Similar to pythons named options we expect the parameters in the + * ordering induced by keywords, but allow the user to pass named options + * in the form of --key=value. Optionally the user can pass an additional + * vector with help strings. + * + * \param argc arg count + * \param argv arg values + * \param[out] pt The parameter tree to store the config structure. + * \param keywords vector with keywords names + * \param required number of required options (the first n keywords are required, default is all are required) + * \param allow_more allow more options than these listed in keywords (default = true) + * \param overwrite allow to overwrite existing options (default = true) + * \param help vector containing help strings + */ + static void readNamedOptions(int argc, char* argv[], + ParameterTree& pt, + std::vector<std::string> keywords, + unsigned int required = std::numeric_limits<unsigned int>::max(), + bool allow_more = true, + bool overwrite = true, + std::vector<std::string> help = std::vector<std::string>()); + + private: + static std::string generateHelpString(std::string progname, std::vector<std::string> keywords, unsigned int required, std::vector<std::string> help); + }; } // end namespace Dune diff --git a/dune/common/path.hh b/dune/common/path.hh index 2a880295df80da3c715b6c4967a11c97e368cda7..c4dc75efcc15a1a9b5c93e005c168f3b8601d556 100644 --- a/dune/common/path.hh +++ b/dune/common/path.hh @@ -7,177 +7,177 @@ #include <string> namespace Dune { -/** -* @addtogroup Path -* @{ -*/ + /** + * @addtogroup Path + * @{ + */ -/** -* @file -* @author Jö Fahlke <jorrit@jorrit.de> -* @brief Utilities for handling filesystem paths -*/ + /** + * @file + * @author Jö Fahlke <jorrit@jorrit.de> + * @brief Utilities for handling filesystem paths + */ -//! concatenate two paths -/** -* \param base The base path. -* \param p The path to concatenate onto base. -* -* If p is an absolute path, return p. Otherwise return the -* string-concatenation of base and path, possibly with a '/' in between, if -* necessary. -* -* Some examples: -* <table> -* <tr><th> base </th><th> p </th><th> result </th></tr> -* <tr><td> anything </td><td> "/abs/path" </td><td> "/abs/path" </td></tr> -* <tr><td> "a" </td><td> "b" </td><td> "a/b" </td></tr> -* <tr><td> "/a" </td><td> "b" </td><td> "/a/b" </td></tr> -* <tr><td> "a/" </td><td> "b" </td><td> "a/b" </td></tr> -* <tr><td> "a" </td><td> "b/" </td><td> "a/b/" </td></tr> -* <tr><td> ".." </td><td> "b" </td><td> "../b" </td></tr> -* <tr><td> "a" </td><td> ".." </td><td> "a/.." </td></tr> -* <tr><td> "." </td><td> "b" </td><td> "./b" </td></tr> -* <tr><td> "a" </td><td> "." </td><td> "a/." </td></tr> -* <tr><td> "" </td><td> "b" </td><td> "b" </td></tr> -* <tr><td> "a" </td><td> "" </td><td> "a" </td></tr> -* <tr><td> "" </td><td> "" </td><td> "" </td></tr> -* </table> -* -* If both base and p are sanitized as per processPath(), and if p does not -* contain any leading "../", then the result will also be sanitized. -*/ -std::string concatPaths(const std::string& base, const std::string& p); + //! concatenate two paths + /** + * \param base The base path. + * \param p The path to concatenate onto base. + * + * If p is an absolute path, return p. Otherwise return the + * string-concatenation of base and path, possibly with a '/' in between, if + * necessary. + * + * Some examples: + * <table> + * <tr><th> base </th><th> p </th><th> result </th></tr> + * <tr><td> anything </td><td> "/abs/path" </td><td> "/abs/path" </td></tr> + * <tr><td> "a" </td><td> "b" </td><td> "a/b" </td></tr> + * <tr><td> "/a" </td><td> "b" </td><td> "/a/b" </td></tr> + * <tr><td> "a/" </td><td> "b" </td><td> "a/b" </td></tr> + * <tr><td> "a" </td><td> "b/" </td><td> "a/b/" </td></tr> + * <tr><td> ".." </td><td> "b" </td><td> "../b" </td></tr> + * <tr><td> "a" </td><td> ".." </td><td> "a/.." </td></tr> + * <tr><td> "." </td><td> "b" </td><td> "./b" </td></tr> + * <tr><td> "a" </td><td> "." </td><td> "a/." </td></tr> + * <tr><td> "" </td><td> "b" </td><td> "b" </td></tr> + * <tr><td> "a" </td><td> "" </td><td> "a" </td></tr> + * <tr><td> "" </td><td> "" </td><td> "" </td></tr> + * </table> + * + * If both base and p are sanitized as per processPath(), and if p does not + * contain any leading "../", then the result will also be sanitized. + */ + std::string concatPaths(const std::string& base, const std::string& p); -//! sanitize a path for further processing -/** -* Sanitize the path as far as possible to make further processing easier. -* The resulting path has the following properties: -* <ul> -* <li> The path is a series of components, each followed by a single '/'. -* <li> An absolute path starts with an empty component followed by a '/', -* so its first character will be '/'. This is the only case where an -* empty component can occur. -* <li> The path does not contain any component ".". Any such component in -* the input is removed. -* <li> A ".." component may only occur in the following case: A relative -* path may contain a series of ".." in the beginning. Any other -* occurrences of ".." in the input is collapsed with a preceding -* component or simply removed if it is at the beginning of an absolute -* path. -* </ul> -* -* \note The result is really meant for processing only since it has two -* unusual properties: First, any path denoting the current directory -* in the input, such as "." will result in an empty path "". Second, -* any non-empty result path will have a trailing '/'. For other -* uses, prettyPath() may be more appropriate. -* -* Some examples: -* <table> -* <tr><th> p </th><th> result </th></tr> -* <tr><td> "" </td><td> "" </td></tr> -* <tr><td> "." </td><td> "" </td></tr> -* <tr><td> "./" </td><td> "" </td></tr> -* <tr><td> "a/.." </td><td> "" </td></tr> -* <tr><td> ".." </td><td> "../" </td></tr> -* <tr><td> "../a" </td><td> "../a/" </td></tr> -* <tr><td> "a" </td><td> "a/" </td></tr> -* <tr><td> "a//" </td><td> "a/" </td></tr> -* <tr><td> "a///b" </td><td> "a/b/" </td></tr> -* <tr><td> "/" </td><td> "/" </td></tr> -* <tr><td> "/." </td><td> "/" </td></tr> -* <tr><td> "/.." </td><td> "/" </td></tr> -* <tr><td> "/a/.." </td><td> "/" </td></tr> -* <tr><td> "/a" </td><td> "/a/" </td></tr> -* <tr><td> "/a/" </td><td> "/a/" </td></tr> -* <tr><td> "/../a/" </td><td> "/a/" </td></tr> -* </table> -*/ -std::string processPath(const std::string& p); + //! sanitize a path for further processing + /** + * Sanitize the path as far as possible to make further processing easier. + * The resulting path has the following properties: + * <ul> + * <li> The path is a series of components, each followed by a single '/'. + * <li> An absolute path starts with an empty component followed by a '/', + * so its first character will be '/'. This is the only case where an + * empty component can occur. + * <li> The path does not contain any component ".". Any such component in + * the input is removed. + * <li> A ".." component may only occur in the following case: A relative + * path may contain a series of ".." in the beginning. Any other + * occurrences of ".." in the input is collapsed with a preceding + * component or simply removed if it is at the beginning of an absolute + * path. + * </ul> + * + * \note The result is really meant for processing only since it has two + * unusual properties: First, any path denoting the current directory + * in the input, such as "." will result in an empty path "". Second, + * any non-empty result path will have a trailing '/'. For other + * uses, prettyPath() may be more appropriate. + * + * Some examples: + * <table> + * <tr><th> p </th><th> result </th></tr> + * <tr><td> "" </td><td> "" </td></tr> + * <tr><td> "." </td><td> "" </td></tr> + * <tr><td> "./" </td><td> "" </td></tr> + * <tr><td> "a/.." </td><td> "" </td></tr> + * <tr><td> ".." </td><td> "../" </td></tr> + * <tr><td> "../a" </td><td> "../a/" </td></tr> + * <tr><td> "a" </td><td> "a/" </td></tr> + * <tr><td> "a//" </td><td> "a/" </td></tr> + * <tr><td> "a///b" </td><td> "a/b/" </td></tr> + * <tr><td> "/" </td><td> "/" </td></tr> + * <tr><td> "/." </td><td> "/" </td></tr> + * <tr><td> "/.." </td><td> "/" </td></tr> + * <tr><td> "/a/.." </td><td> "/" </td></tr> + * <tr><td> "/a" </td><td> "/a/" </td></tr> + * <tr><td> "/a/" </td><td> "/a/" </td></tr> + * <tr><td> "/../a/" </td><td> "/a/" </td></tr> + * </table> + */ + std::string processPath(const std::string& p); -//! check whether the given path indicates that it is a directory -/** -* In particular the following kinds of paths indicate a directory: -* <ul> -* <li> The empty path (denotes the current directory), -* <li> any path with a trailing '/', -* <li> any path whose last component is "." or "..". -* </ul> -*/ -bool pathIndicatesDirectory(const std::string& p); + //! check whether the given path indicates that it is a directory + /** + * In particular the following kinds of paths indicate a directory: + * <ul> + * <li> The empty path (denotes the current directory), + * <li> any path with a trailing '/', + * <li> any path whose last component is "." or "..". + * </ul> + */ + bool pathIndicatesDirectory(const std::string& p); -//! pretty print path -/** -* \param p Path to pretty-print. -* \param isDirectory Whether to append a '/' to make clear this is a -* directory. -* -* Pretty print the path. This removes any duplicate '/' and any -* superfluous occurrences of ".." and ".". The resulting path will have a -* trailing '/' if it is the root path or if isDirectory is true. It will -* however not have a trailing '/' if it is otherwise clear that it is a -* directory -- i.e. if its last component is "." or "..". -* -* Some examples: -* <table> -* <tr><th> p </th><th> isDirectory </th><th> result </th></tr> -* <tr><td> "" </td><td> anything </td><td> "." </td></tr> -* <tr><td> "." </td><td> anything </td><td> "." </td></tr> -* <tr><td> "./" </td><td> anything </td><td> "." </td></tr> -* <tr><td> "a/.." </td><td> anything </td><td> "." </td></tr> -* <tr><td> ".." </td><td> anything </td><td> ".." </td></tr> -* <tr><td> "../a" </td><td> true </td><td> "../a/" </td></tr> -* <tr><td> "../a" </td><td> false </td><td> "../a" </td></tr> -* <tr><td> "a" </td><td> true </td><td> "a/" </td></tr> -* <tr><td> "a" </td><td> false </td><td> "a" </td></tr> -* <tr><td> "a//" </td><td> true </td><td> "a/" </td></tr> -* <tr><td> "a//" </td><td> false </td><td> "a" </td></tr> -* <tr><td> "a///b" </td><td> true </td><td> "a/b/" </td></tr> -* <tr><td> "a///b" </td><td> false </td><td> "a/b" </td></tr> -* <tr><td> "/" </td><td> anything </td><td> "/" </td></tr> -* <tr><td> "/." </td><td> anything </td><td> "/" </td></tr> -* <tr><td> "/.." </td><td> anything </td><td> "/" </td></tr> -* <tr><td> "/a/.." </td><td> anything </td><td> "/" </td></tr> -* <tr><td> "/a" </td><td> true </td><td> "/a/" </td></tr> -* <tr><td> "/a" </td><td> false </td><td> "/a" </td></tr> -* <tr><td> "/a/" </td><td> true </td><td> "/a/" </td></tr> -* <tr><td> "/a/" </td><td> false </td><td> "/a" </td></tr> -* <tr><td> "/../a/" </td><td> true </td><td> "/a/" </td></tr> -* <tr><td> "/../a/" </td><td> false </td><td> "/a" </td></tr> -* </table> -*/ -std::string prettyPath(const std::string& p, bool isDirectory); + //! pretty print path + /** + * \param p Path to pretty-print. + * \param isDirectory Whether to append a '/' to make clear this is a + * directory. + * + * Pretty print the path. This removes any duplicate '/' and any + * superfluous occurrences of ".." and ".". The resulting path will have a + * trailing '/' if it is the root path or if isDirectory is true. It will + * however not have a trailing '/' if it is otherwise clear that it is a + * directory -- i.e. if its last component is "." or "..". + * + * Some examples: + * <table> + * <tr><th> p </th><th> isDirectory </th><th> result </th></tr> + * <tr><td> "" </td><td> anything </td><td> "." </td></tr> + * <tr><td> "." </td><td> anything </td><td> "." </td></tr> + * <tr><td> "./" </td><td> anything </td><td> "." </td></tr> + * <tr><td> "a/.." </td><td> anything </td><td> "." </td></tr> + * <tr><td> ".." </td><td> anything </td><td> ".." </td></tr> + * <tr><td> "../a" </td><td> true </td><td> "../a/" </td></tr> + * <tr><td> "../a" </td><td> false </td><td> "../a" </td></tr> + * <tr><td> "a" </td><td> true </td><td> "a/" </td></tr> + * <tr><td> "a" </td><td> false </td><td> "a" </td></tr> + * <tr><td> "a//" </td><td> true </td><td> "a/" </td></tr> + * <tr><td> "a//" </td><td> false </td><td> "a" </td></tr> + * <tr><td> "a///b" </td><td> true </td><td> "a/b/" </td></tr> + * <tr><td> "a///b" </td><td> false </td><td> "a/b" </td></tr> + * <tr><td> "/" </td><td> anything </td><td> "/" </td></tr> + * <tr><td> "/." </td><td> anything </td><td> "/" </td></tr> + * <tr><td> "/.." </td><td> anything </td><td> "/" </td></tr> + * <tr><td> "/a/.." </td><td> anything </td><td> "/" </td></tr> + * <tr><td> "/a" </td><td> true </td><td> "/a/" </td></tr> + * <tr><td> "/a" </td><td> false </td><td> "/a" </td></tr> + * <tr><td> "/a/" </td><td> true </td><td> "/a/" </td></tr> + * <tr><td> "/a/" </td><td> false </td><td> "/a" </td></tr> + * <tr><td> "/../a/" </td><td> true </td><td> "/a/" </td></tr> + * <tr><td> "/../a/" </td><td> false </td><td> "/a" </td></tr> + * </table> + */ + std::string prettyPath(const std::string& p, bool isDirectory); -//! pretty print path -/** -* \param p Path to pretty-print. -* -* This is like prettyPath(const std::string& p, bool isDirectory) with -* isDirectory automatically determined using pathIndicatesDirectory(p). -*/ -std::string prettyPath(const std::string& p); + //! pretty print path + /** + * \param p Path to pretty-print. + * + * This is like prettyPath(const std::string& p, bool isDirectory) with + * isDirectory automatically determined using pathIndicatesDirectory(p). + */ + std::string prettyPath(const std::string& p); -//! compute a relative path between two paths -/** -* \param newbase Base path for the resulting relative path. -* \param p Path re sulting path should resolve to, when taken -* reltively to newbase. -* -* Compute a relative path from newbase to p. newbase is assumed to be a -* directory. p and newbase should either both be absolute, or both be -* relative. In the latter case they are assumed to both be relative to -* the same unspecified directory. The has the form of something sanitized -* by processPath(). -* -* \throw NotImplemented The condition that newbase and p must both be -* relative or both be absolute does not hold. -* \throw NotImplemented After sanitization newbase has more leading ".." -* components than p. -*/ -std::string relativePath(const std::string& newbase, const std::string& p); + //! compute a relative path between two paths + /** + * \param newbase Base path for the resulting relative path. + * \param p Path re sulting path should resolve to, when taken + * reltively to newbase. + * + * Compute a relative path from newbase to p. newbase is assumed to be a + * directory. p and newbase should either both be absolute, or both be + * relative. In the latter case they are assumed to both be relative to + * the same unspecified directory. The has the form of something sanitized + * by processPath(). + * + * \throw NotImplemented The condition that newbase and p must both be + * relative or both be absolute does not hold. + * \throw NotImplemented After sanitization newbase has more leading ".." + * components than p. + */ + std::string relativePath(const std::string& newbase, const std::string& p); -/** @} group Path */ + /** @} group Path */ } #endif // DUNE_COMMON_PATH_HH diff --git a/dune/common/poolallocator.hh b/dune/common/poolallocator.hh index fc02a2da1deb430ffc57cedd1873602909d96bb5..477ba3995da5445d5a87210aa84cf2a42969689b 100644 --- a/dune/common/poolallocator.hh +++ b/dune/common/poolallocator.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief An stl-compliant pool allocator -*/ + * \brief An stl-compliant pool allocator + */ #include <numeric> #include <typeinfo> @@ -24,549 +24,549 @@ struct testPoolMain; namespace Dune { -template<typename T, std::size_t s> -class Pool; + template<typename T, std::size_t s> + class Pool; -template<typename T, std::size_t s> -class PoolAllocator; + template<typename T, std::size_t s> + class PoolAllocator; } namespace std { -/* -template<class T, std::size_t S> -inline ostream& operator<<(ostream& os, Dune::Pool<T,S>& pool) -{ -os<<"pool="<<&pool<<" allocated_="<<pool.allocated_; -return os; -} - -template<class T, std::size_t S> -inline ostream& operator<<(ostream& os, Dune::PoolAllocator<T,S>& pool) -{ -os<<pool.memoryPool_<<std::endl; -return os; -} -*/ + /* + template<class T, std::size_t S> + inline ostream& operator<<(ostream& os, Dune::Pool<T,S>& pool) + { + os<<"pool="<<&pool<<" allocated_="<<pool.allocated_; + return os; + } + + template<class T, std::size_t S> + inline ostream& operator<<(ostream& os, Dune::PoolAllocator<T,S>& pool) + { + os<<pool.memoryPool_<<std::endl; + return os; + } + */ } namespace Dune { -/** -* @file -* This file implements the classes Pool and PoolAllocator providing -* memory allocation for objects in chunks. -* @author Markus Blatt -*/ -/** -* @addtogroup Allocators -* -* @{ -*/ - -/** -* @brief A memory pool of objects. -* -* The memory for the objects is organized in chunks. -* Each chunks is capable of holding a specified number of -* objects. The allocated objects will be properly aligned -* for fast access. -* Deallocated objects are cached for reuse to prevent memory -* fragmentation. -* @warning If the size of the objects allocated is less than the -* size of a pointer memory is wasted. -* @warning Due to aligned issues at the number of bytes of the -* alignment prerequisite (< 4 bytes) are wasted. This effect -* becomes negligible for big sizes of chunkSize. -* -* \tparam T The type that is allocated by us. -* \tparam s The size of a memory chunk in bytes. -*/ -template<class T, std::size_t s> -class Pool -{ -// make the test function friend -friend struct ::testPoolMain<s,T>; - -//friend std::ostream& std::operator<<<>(std::ostream&,Pool<T,s>&); -template< class, std::size_t > friend class PoolAllocator; - -private: - -/** @brief Reference to next free element. */ -struct Reference -{ -Reference *next_; -}; - -public: - -/** @brief The type of object we allocate memory for. */ -typedef T MemberType; -enum -{ -/** -* @brief The size of a union of Reference and MemberType. -*/ -unionSize = ((sizeof(MemberType) < sizeof(Reference)) ? -sizeof(Reference) : sizeof(MemberType)), - -/** -* @brief Size requirement. At least one object has to -* stored. -*/ -size = ((sizeof(MemberType) <= s && sizeof(Reference) <= s) ? -s : unionSize), - -/** -* @brief The alignment that suits both the MemberType and -* the Reference (i.e. their least common multiple). -*/ -alignment = std::lcm(alignof(MemberType), alignof(Reference)), - -/** -* @brief The aligned size of the type. -* -* This size is bigger than sizeof of the type and a multiple of -* the alignment requirement. -*/ -alignedSize = ((unionSize % alignment == 0) ? -unionSize : -((unionSize / alignment + 1) * alignment)), - -/** -* @brief The size of each chunk memory chunk. -* -* Will be adapted to be a multiple of the alignment -*/ -chunkSize = ((size % alignment == 0) ? -size : ((size / alignment + 1)* alignment)), - -/** -* @brief The number of element each chunk can hold. -*/ -elements = (chunkSize / alignedSize) -}; - -private: -/** @brief Chunk of memory managed by the pool. */ -struct Chunk -{ - -//friend int testPool<s,T>(); - -/** @brief The memory we hold. */ -alignas(alignment) char chunk_[chunkSize]; - -/** @brief The next element */ -Chunk *next_; -}; - -public: -/** @brief Constructor. */ -inline Pool(); -/** @brief Destructor. */ -inline ~Pool(); -/** -* @brief Get a new or recycled object -* @return A pointer to the object memory. -*/ -inline void* allocate(); -/** -* @brief Free an object. -* @param o The pointer to memory block of the object. -*/ -inline void free(void* o); - -/** -* @brief Print elements in pool for debugging. -*/ -inline void print(std::ostream& os); - -private: - -// Prevent Copying! -Pool(const Pool<MemberType,s>&); - -void operator=(const Pool<MemberType,s>& pool) const; -/** @brief Grow our pool.*/ -inline void grow(); -/** @brief The first free element. */ -Reference *head_; -/** @brief Our memory chunks. */ -Chunk *chunks_; -/* @brief The number of currently allocated elements. */ -//size_t allocated_; - -}; - -/** -* @brief An allocator managing a pool of objects for reuse. -* -* This allocator is specifically useful for small data types -* where new and delete are too expensive. -* -* It uses a pool of memory chunks where the objects will be allocated. -* This means that assuming that N objects fit into memory only every N-th -* request for an object will result in memory allocation. -* -* @warning It is not suitable -* for the use in standard containers as it cannot allocate -* arrays of arbitrary size -* -* \tparam T The type that will be allocated. -* \tparam s The number of elements to fit into one memory chunk. -*/ -template<class T, std::size_t s> -class PoolAllocator -{ -//friend std::ostream& std::operator<<<>(std::ostream&,PoolAllocator<T,s>&); - -public: -/** -* @brief Type of the values we construct and allocate. -*/ -typedef T value_type; - -enum -{ -/** -* @brief The number of objects to fit into one memory chunk -* allocated. -*/ -size=s*sizeof(value_type) -}; - -/** -* @brief The pointer type. -*/ -typedef T* pointer; - -/** -* @brief The constant pointer type. -*/ -typedef const T* const_pointer; - -/** -* @brief The reference type. -*/ -typedef T& reference; - -/** -* @brief The constant reference type. -*/ -typedef const T& const_reference; - -/** -* @brief The size type. -*/ -typedef std::size_t size_type; - -/** -* @brief The difference_type. -*/ -typedef std::ptrdiff_t difference_type; - -/** -* @brief Constructor. -*/ -inline PoolAllocator(); - -/** -* @brief Copy Constructor that does not copy the memory pool. -*/ -template<typename U, std::size_t u> -inline PoolAllocator(const PoolAllocator<U,u>&) -{ -// we allow copying but never copy the pool -// to have a clear ownership of allocated pointers. -} - -/// \brief Copy constructor that does not copy the memory pool. -PoolAllocator(const PoolAllocator&) -{ -// we allow copying but never copy the pool -// to have a clear ownership of allocated pointers. -// For this behaviour we have to implement -// the copy constructor, because the default -// one would copy the pool and deallocation -// of it would break. -} -/** -* @brief Allocates objects. -* @param n The number of objects to allocate. Has to be one! -* @param hint Ignored hint. -* @return A pointer tp the allocated elements. -*/ -inline pointer allocate(std::size_t n, const_pointer hint=0); - -/** -* @brief Free objects. -* -* Does not call the destructor! -* @param n The number of objects to free. Has to be one! -* @param p Pointer to the first object. -*/ -inline void deallocate(pointer p, std::size_t n); - -/** -* @brief Construct an object. -* @param p Pointer to the object. -* @param value The value to initialize it to. -*/ -inline void construct(pointer p, const_reference value); - -/** -* @brief Destroy an object without freeing memory. -* @param p Pointer to the object. -*/ -inline void destroy(pointer p); - -/** -* @brief Convert a reference to a pointer. -*/ -inline pointer address(reference x) const { return &x; } - - -/** -* @brief Convert a reference to a pointer. -*/ -inline const_pointer address(const_reference x) const { return &x; } - -/** -* @brief Not correctly implemented, yet! -*/ -inline int max_size() const noexcept { return 1; } - -/** -* @brief Rebind the allocator to another type. -*/ -template<class U> -struct rebind -{ -typedef PoolAllocator<U,s> other; -}; - -/** @brief The type of the memory pool we use. */ -typedef Pool<T,size> PoolType; - -private: -/** -* @brief The underlying memory pool. -*/ -PoolType memoryPool_; -}; - -// specialization for void -template <std::size_t s> -class PoolAllocator<void,s> -{ -public: -typedef void* pointer; -typedef const void* const_pointer; -// reference to void members are impossible. -typedef void value_type; -template <class U> struct rebind -{ -typedef PoolAllocator<U,s> other; -}; -}; - - -template<typename T1, std::size_t t1, typename T2, std::size_t t2> -bool operator==(const PoolAllocator<T1,t1>&, const PoolAllocator<T2,t2>&) -{ -return false; -} - - -template<typename T1, std::size_t t1, typename T2, std::size_t t2> -bool operator!=(const PoolAllocator<T1,t1>&, const PoolAllocator<T2,t2>&) -{ -return true; -} - -template<typename T, std::size_t t1, std::size_t t2> -bool operator==(const PoolAllocator<T,t1>& p1, const PoolAllocator<T,t2>& p2) -{ -return &p1==&p2; -} - - -template<typename T, std::size_t t1, std::size_t t2> -bool operator!=(const PoolAllocator<T,t1>& p1, const PoolAllocator<T,t2>& p2) -{ -return &p1 != &p2; -} - -template<typename T, std::size_t t1, std::size_t t2> -bool operator==(const PoolAllocator<void,t1>&, const PoolAllocator<T,t2>&) -{ -return false; -} - - -template<typename T, std::size_t t1, std::size_t t2> -bool operator!=(const PoolAllocator<void,t1>&, const PoolAllocator<T,t2>&) -{ -return true; -} - -template<std::size_t t1, std::size_t t2> -bool operator==(const PoolAllocator<void,t1>& p1, const PoolAllocator<void,t2>& p2) -{ -return &p1==&p2; -} - -template<std::size_t t1, std::size_t t2> -bool operator!=(const PoolAllocator<void,t1>& p1, const PoolAllocator<void,t2>& p2) -{ -return &p1!=&p2; -} - -template<class T, std::size_t S> -inline Pool<T,S>::Pool() -: head_(0), chunks_(0) //, allocated_(0) -{ -static_assert(sizeof(T)<=unionSize, "Library Error: type T is too big"); -static_assert(sizeof(Reference)<=unionSize, "Library Error: type of referene is too big"); -static_assert(unionSize<=alignedSize, "Library Error: alignedSize too small"); -static_assert(sizeof(T)<=chunkSize, "Library Error: chunkSize must be able to hold at least one value"); -static_assert(sizeof(Reference)<=chunkSize, "Library Error: chunkSize must be able to hold at least one reference"); -static_assert(chunkSize % alignment == 0, "Library Error: compiler cannot calculate!"); -static_assert(elements>=1, "Library Error: we need to hold at least one element!"); -static_assert(elements*alignedSize<=chunkSize, "Library Error: aligned elements must fit into chuck!"); -} - -template<class T, std::size_t S> -inline Pool<T,S>::~Pool() -{ -/* -if(allocated_!=0) -std::cerr<<"There are still "<<allocated_<<" allocated elements by the Pool<"<<typeid(T).name()<<","<<S<<"> " -<<static_cast<void*>(this)<<"! This is a memory leak and might result in segfaults" -<<std::endl; -*/ -// delete the allocated chunks. -Chunk *current=chunks_; - -while(current!=0) -{ -Chunk *tmp = current; -current = current->next_; -delete tmp; -} -} - -template<class T, std::size_t S> -inline void Pool<T,S>::print(std::ostream& os) -{ -Chunk* current=chunks_; -while(current) { -os<<current<<" "; -current=current->next_; -} -os<<current<<" "; -} - -template<class T, std::size_t S> -inline void Pool<T,S>::grow() -{ -Chunk *newChunk = new Chunk; -newChunk->next_ = chunks_; -chunks_ = newChunk; - -char* start = chunks_->chunk_; -char* last = &start[elements*alignedSize]; -Reference* ref = new (start) (Reference); - -// grow is only called if head==0, -assert(!head_); - -head_ = ref; - -for(char* element=start+alignedSize; element<last; element=element+alignedSize) { -Reference* next = new (element) (Reference); -ref->next_ = next; -ref = next; -} -ref->next_=0; -} - -template<class T, std::size_t S> -inline void Pool<T,S>::free(void* b) -{ -if(b) { + /** + * @file + * This file implements the classes Pool and PoolAllocator providing + * memory allocation for objects in chunks. + * @author Markus Blatt + */ + /** + * @addtogroup Allocators + * + * @{ + */ + + /** + * @brief A memory pool of objects. + * + * The memory for the objects is organized in chunks. + * Each chunks is capable of holding a specified number of + * objects. The allocated objects will be properly aligned + * for fast access. + * Deallocated objects are cached for reuse to prevent memory + * fragmentation. + * @warning If the size of the objects allocated is less than the + * size of a pointer memory is wasted. + * @warning Due to aligned issues at the number of bytes of the + * alignment prerequisite (< 4 bytes) are wasted. This effect + * becomes negligible for big sizes of chunkSize. + * + * \tparam T The type that is allocated by us. + * \tparam s The size of a memory chunk in bytes. + */ + template<class T, std::size_t s> + class Pool + { + // make the test function friend + friend struct ::testPoolMain<s,T>; + + //friend std::ostream& std::operator<<<>(std::ostream&,Pool<T,s>&); + template< class, std::size_t > friend class PoolAllocator; + + private: + + /** @brief Reference to next free element. */ + struct Reference + { + Reference *next_; + }; + + public: + + /** @brief The type of object we allocate memory for. */ + typedef T MemberType; + enum + { + /** + * @brief The size of a union of Reference and MemberType. + */ + unionSize = ((sizeof(MemberType) < sizeof(Reference)) ? + sizeof(Reference) : sizeof(MemberType)), + + /** + * @brief Size requirement. At least one object has to + * stored. + */ + size = ((sizeof(MemberType) <= s && sizeof(Reference) <= s) ? + s : unionSize), + + /** + * @brief The alignment that suits both the MemberType and + * the Reference (i.e. their least common multiple). + */ + alignment = std::lcm(alignof(MemberType), alignof(Reference)), + + /** + * @brief The aligned size of the type. + * + * This size is bigger than sizeof of the type and a multiple of + * the alignment requirement. + */ + alignedSize = ((unionSize % alignment == 0) ? + unionSize : + ((unionSize / alignment + 1) * alignment)), + + /** + * @brief The size of each chunk memory chunk. + * + * Will be adapted to be a multiple of the alignment + */ + chunkSize = ((size % alignment == 0) ? + size : ((size / alignment + 1)* alignment)), + + /** + * @brief The number of element each chunk can hold. + */ + elements = (chunkSize / alignedSize) + }; + + private: + /** @brief Chunk of memory managed by the pool. */ + struct Chunk + { + + //friend int testPool<s,T>(); + + /** @brief The memory we hold. */ + alignas(alignment) char chunk_[chunkSize]; + + /** @brief The next element */ + Chunk *next_; + }; + + public: + /** @brief Constructor. */ + inline Pool(); + /** @brief Destructor. */ + inline ~Pool(); + /** + * @brief Get a new or recycled object + * @return A pointer to the object memory. + */ + inline void* allocate(); + /** + * @brief Free an object. + * @param o The pointer to memory block of the object. + */ + inline void free(void* o); + + /** + * @brief Print elements in pool for debugging. + */ + inline void print(std::ostream& os); + + private: + + // Prevent Copying! + Pool(const Pool<MemberType,s>&); + + void operator=(const Pool<MemberType,s>& pool) const; + /** @brief Grow our pool.*/ + inline void grow(); + /** @brief The first free element. */ + Reference *head_; + /** @brief Our memory chunks. */ + Chunk *chunks_; + /* @brief The number of currently allocated elements. */ + //size_t allocated_; + + }; + + /** + * @brief An allocator managing a pool of objects for reuse. + * + * This allocator is specifically useful for small data types + * where new and delete are too expensive. + * + * It uses a pool of memory chunks where the objects will be allocated. + * This means that assuming that N objects fit into memory only every N-th + * request for an object will result in memory allocation. + * + * @warning It is not suitable + * for the use in standard containers as it cannot allocate + * arrays of arbitrary size + * + * \tparam T The type that will be allocated. + * \tparam s The number of elements to fit into one memory chunk. + */ + template<class T, std::size_t s> + class PoolAllocator + { + //friend std::ostream& std::operator<<<>(std::ostream&,PoolAllocator<T,s>&); + + public: + /** + * @brief Type of the values we construct and allocate. + */ + typedef T value_type; + + enum + { + /** + * @brief The number of objects to fit into one memory chunk + * allocated. + */ + size=s*sizeof(value_type) + }; + + /** + * @brief The pointer type. + */ + typedef T* pointer; + + /** + * @brief The constant pointer type. + */ + typedef const T* const_pointer; + + /** + * @brief The reference type. + */ + typedef T& reference; + + /** + * @brief The constant reference type. + */ + typedef const T& const_reference; + + /** + * @brief The size type. + */ + typedef std::size_t size_type; + + /** + * @brief The difference_type. + */ + typedef std::ptrdiff_t difference_type; + + /** + * @brief Constructor. + */ + inline PoolAllocator(); + + /** + * @brief Copy Constructor that does not copy the memory pool. + */ + template<typename U, std::size_t u> + inline PoolAllocator(const PoolAllocator<U,u>&) + { + // we allow copying but never copy the pool + // to have a clear ownership of allocated pointers. + } + + /// \brief Copy constructor that does not copy the memory pool. + PoolAllocator(const PoolAllocator&) + { + // we allow copying but never copy the pool + // to have a clear ownership of allocated pointers. + // For this behaviour we have to implement + // the copy constructor, because the default + // one would copy the pool and deallocation + // of it would break. + } + /** + * @brief Allocates objects. + * @param n The number of objects to allocate. Has to be one! + * @param hint Ignored hint. + * @return A pointer tp the allocated elements. + */ + inline pointer allocate(std::size_t n, const_pointer hint=0); + + /** + * @brief Free objects. + * + * Does not call the destructor! + * @param n The number of objects to free. Has to be one! + * @param p Pointer to the first object. + */ + inline void deallocate(pointer p, std::size_t n); + + /** + * @brief Construct an object. + * @param p Pointer to the object. + * @param value The value to initialize it to. + */ + inline void construct(pointer p, const_reference value); + + /** + * @brief Destroy an object without freeing memory. + * @param p Pointer to the object. + */ + inline void destroy(pointer p); + + /** + * @brief Convert a reference to a pointer. + */ + inline pointer address(reference x) const { return &x; } + + + /** + * @brief Convert a reference to a pointer. + */ + inline const_pointer address(const_reference x) const { return &x; } + + /** + * @brief Not correctly implemented, yet! + */ + inline int max_size() const noexcept { return 1; } + + /** + * @brief Rebind the allocator to another type. + */ + template<class U> + struct rebind + { + typedef PoolAllocator<U,s> other; + }; + + /** @brief The type of the memory pool we use. */ + typedef Pool<T,size> PoolType; + + private: + /** + * @brief The underlying memory pool. + */ + PoolType memoryPool_; + }; + + // specialization for void + template <std::size_t s> + class PoolAllocator<void,s> + { + public: + typedef void* pointer; + typedef const void* const_pointer; + // reference to void members are impossible. + typedef void value_type; + template <class U> struct rebind + { + typedef PoolAllocator<U,s> other; + }; + }; + + + template<typename T1, std::size_t t1, typename T2, std::size_t t2> + bool operator==(const PoolAllocator<T1,t1>&, const PoolAllocator<T2,t2>&) + { + return false; + } + + + template<typename T1, std::size_t t1, typename T2, std::size_t t2> + bool operator!=(const PoolAllocator<T1,t1>&, const PoolAllocator<T2,t2>&) + { + return true; + } + + template<typename T, std::size_t t1, std::size_t t2> + bool operator==(const PoolAllocator<T,t1>& p1, const PoolAllocator<T,t2>& p2) + { + return &p1==&p2; + } + + + template<typename T, std::size_t t1, std::size_t t2> + bool operator!=(const PoolAllocator<T,t1>& p1, const PoolAllocator<T,t2>& p2) + { + return &p1 != &p2; + } + + template<typename T, std::size_t t1, std::size_t t2> + bool operator==(const PoolAllocator<void,t1>&, const PoolAllocator<T,t2>&) + { + return false; + } + + + template<typename T, std::size_t t1, std::size_t t2> + bool operator!=(const PoolAllocator<void,t1>&, const PoolAllocator<T,t2>&) + { + return true; + } + + template<std::size_t t1, std::size_t t2> + bool operator==(const PoolAllocator<void,t1>& p1, const PoolAllocator<void,t2>& p2) + { + return &p1==&p2; + } + + template<std::size_t t1, std::size_t t2> + bool operator!=(const PoolAllocator<void,t1>& p1, const PoolAllocator<void,t2>& p2) + { + return &p1!=&p2; + } + + template<class T, std::size_t S> + inline Pool<T,S>::Pool() + : head_(0), chunks_(0) //, allocated_(0) + { + static_assert(sizeof(T)<=unionSize, "Library Error: type T is too big"); + static_assert(sizeof(Reference)<=unionSize, "Library Error: type of referene is too big"); + static_assert(unionSize<=alignedSize, "Library Error: alignedSize too small"); + static_assert(sizeof(T)<=chunkSize, "Library Error: chunkSize must be able to hold at least one value"); + static_assert(sizeof(Reference)<=chunkSize, "Library Error: chunkSize must be able to hold at least one reference"); + static_assert(chunkSize % alignment == 0, "Library Error: compiler cannot calculate!"); + static_assert(elements>=1, "Library Error: we need to hold at least one element!"); + static_assert(elements*alignedSize<=chunkSize, "Library Error: aligned elements must fit into chuck!"); + } + + template<class T, std::size_t S> + inline Pool<T,S>::~Pool() + { + /* + if(allocated_!=0) + std::cerr<<"There are still "<<allocated_<<" allocated elements by the Pool<"<<typeid(T).name()<<","<<S<<"> " + <<static_cast<void*>(this)<<"! This is a memory leak and might result in segfaults" + <<std::endl; + */ + // delete the allocated chunks. + Chunk *current=chunks_; + + while(current!=0) + { + Chunk *tmp = current; + current = current->next_; + delete tmp; + } + } + + template<class T, std::size_t S> + inline void Pool<T,S>::print(std::ostream& os) + { + Chunk* current=chunks_; + while(current) { + os<<current<<" "; + current=current->next_; + } + os<<current<<" "; + } + + template<class T, std::size_t S> + inline void Pool<T,S>::grow() + { + Chunk *newChunk = new Chunk; + newChunk->next_ = chunks_; + chunks_ = newChunk; + + char* start = chunks_->chunk_; + char* last = &start[elements*alignedSize]; + Reference* ref = new (start) (Reference); + + // grow is only called if head==0, + assert(!head_); + + head_ = ref; + + for(char* element=start+alignedSize; element<last; element=element+alignedSize) { + Reference* next = new (element) (Reference); + ref->next_ = next; + ref = next; + } + ref->next_=0; + } + + template<class T, std::size_t S> + inline void Pool<T,S>::free(void* b) + { + if(b) { #ifndef NDEBUG -Chunk* current=chunks_; -while(current) { -if(static_cast<void*>(current->chunk_)<=b && -static_cast<void*>(current->chunk_+chunkSize)>b) -break; -current=current->next_; -} -if(!current) -throw std::bad_alloc(); + Chunk* current=chunks_; + while(current) { + if(static_cast<void*>(current->chunk_)<=b && + static_cast<void*>(current->chunk_+chunkSize)>b) + break; + current=current->next_; + } + if(!current) + throw std::bad_alloc(); #endif -Reference* freed = static_cast<Reference*>(b); -freed->next_ = head_; -head_ = freed; -//--allocated_; -} -else -{ -std::cerr<< "Tried to free null pointer! "<<b<<std::endl; -throw std::bad_alloc(); -} -} - -template<class T, std::size_t S> -inline void* Pool<T,S>::allocate() -{ -if(!head_) -grow(); - -Reference* p = head_; -head_ = p->next_; -//++allocated_; -return p; -} - -template<class T, std::size_t s> -inline PoolAllocator<T,s>::PoolAllocator() -{ } - -template<class T, std::size_t s> -inline typename PoolAllocator<T,s>::pointer -PoolAllocator<T,s>::allocate(std::size_t n, const_pointer) -{ -if(n==1) -return static_cast<T*>(memoryPool_.allocate()); -else -throw std::bad_alloc(); -} - -template<class T, std::size_t s> -inline void PoolAllocator<T,s>::deallocate(pointer p, std::size_t n) -{ -for(size_t i=0; i<n; i++) -memoryPool_.free(p++); -} - -template<class T, std::size_t s> -inline void PoolAllocator<T,s>::construct(pointer p, const_reference value) -{ -::new (static_cast<void*>(p))T(value); -} - -template<class T, std::size_t s> -inline void PoolAllocator<T,s>::destroy(pointer p) -{ -p->~T(); -} - -/** @} */ + Reference* freed = static_cast<Reference*>(b); + freed->next_ = head_; + head_ = freed; + //--allocated_; + } + else + { + std::cerr<< "Tried to free null pointer! "<<b<<std::endl; + throw std::bad_alloc(); + } + } + + template<class T, std::size_t S> + inline void* Pool<T,S>::allocate() + { + if(!head_) + grow(); + + Reference* p = head_; + head_ = p->next_; + //++allocated_; + return p; + } + + template<class T, std::size_t s> + inline PoolAllocator<T,s>::PoolAllocator() + { } + + template<class T, std::size_t s> + inline typename PoolAllocator<T,s>::pointer + PoolAllocator<T,s>::allocate(std::size_t n, const_pointer) + { + if(n==1) + return static_cast<T*>(memoryPool_.allocate()); + else + throw std::bad_alloc(); + } + + template<class T, std::size_t s> + inline void PoolAllocator<T,s>::deallocate(pointer p, std::size_t n) + { + for(size_t i=0; i<n; i++) + memoryPool_.free(p++); + } + + template<class T, std::size_t s> + inline void PoolAllocator<T,s>::construct(pointer p, const_reference value) + { + ::new (static_cast<void*>(p))T(value); + } + + template<class T, std::size_t s> + inline void PoolAllocator<T,s>::destroy(pointer p) + { + p->~T(); + } + + /** @} */ } #endif diff --git a/dune/common/power.hh b/dune/common/power.hh index bc52c0225a9f3d8fdd6218f352d8aaf17f481541..5fc8b13472b731d3112b478b7882254d34835add 100644 --- a/dune/common/power.hh +++ b/dune/common/power.hh @@ -5,44 +5,44 @@ #include <dune/internal/dune-common.hh> /** \file -\brief Various implementations of the power function for run-time and static arguments -*/ + \brief Various implementations of the power function for run-time and static arguments + */ #include <dune/common/math.hh> namespace Dune { -/** @addtogroup Common - -@{ -*/ - -/** \brief Calculates m^p at compile time -* \deprecated Please use the method `power` from `math.hh` instead! -*/ -template <int m, int p> -struct StaticPower -{ -/** \brief power stores m^p */ -static constexpr int power = Dune::power(m,p); -}; - - -/** \brief Compute power for a run-time mantissa and a compile-time integer exponent -* -* \deprecated Please use the method `power` from `math.hh` instead! -* -* \tparam p The exponent -*/ -template <int p> -struct Power -{ -template <typename T> -static constexpr auto eval(const T & a) -{ -return power(a,p); -} -}; + /** @addtogroup Common + + @{ + */ + + /** \brief Calculates m^p at compile time + * \deprecated Please use the method `power` from `math.hh` instead! + */ + template <int m, int p> + struct StaticPower + { + /** \brief power stores m^p */ + static constexpr int power = Dune::power(m,p); + }; + + + /** \brief Compute power for a run-time mantissa and a compile-time integer exponent + * + * \deprecated Please use the method `power` from `math.hh` instead! + * + * \tparam p The exponent + */ + template <int p> + struct Power + { + template <typename T> + static constexpr auto eval(const T & a) + { + return power(a,p); + } + }; } diff --git a/dune/common/precision.hh b/dune/common/precision.hh index a1f454a9be647cb292fcdccc5d443cbcb83b9523..97260393644a0e2640f4bd6e177f4f471c6ec730 100644 --- a/dune/common/precision.hh +++ b/dune/common/precision.hh @@ -5,45 +5,45 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Various precision settings for calculations with FieldMatrix and FieldVector -*/ + * \brief Various precision settings for calculations with FieldMatrix and FieldVector + */ #include <stdlib.h> namespace Dune { -/** -@addtogroup DenseMatVec -@{ -*/ - -/** -* @brief Precisions for calculations with FieldMatrix and FieldVector. -*/ -template <class ctype = double> -class FMatrixPrecision { -public: -//! return threshold to declare matrix singular -static ctype absolute_limit () -{ -return _absolute; -} - -//! set singular threshold -static void set_absolute_limit (ctype absthres) -{ -_absolute = absthres; -} - -private: -// just to demonstrate some state information -static ctype _absolute; -}; - -template <class ctype> -ctype FMatrixPrecision<ctype>::_absolute = 1E-80; - -/** @} end documentation */ + /** + @addtogroup DenseMatVec + @{ + */ + + /** + * @brief Precisions for calculations with FieldMatrix and FieldVector. + */ + template <class ctype = double> + class FMatrixPrecision { + public: + //! return threshold to declare matrix singular + static ctype absolute_limit () + { + return _absolute; + } + + //! set singular threshold + static void set_absolute_limit (ctype absthres) + { + _absolute = absthres; + } + + private: + // just to demonstrate some state information + static ctype _absolute; + }; + + template <class ctype> + ctype FMatrixPrecision<ctype>::_absolute = 1E-80; + + /** @} end documentation */ } // end namespace diff --git a/dune/common/promotiontraits.hh b/dune/common/promotiontraits.hh index d1ae0430005059432db552292a898f1b33b3037e..639c421cd3726e2bb6687afe42b462b6f28e66af 100644 --- a/dune/common/promotiontraits.hh +++ b/dune/common/promotiontraits.hh @@ -7,33 +7,33 @@ #include <utility> namespace Dune { -/** -* @file -* @brief Compute type of the result of an arithmetic operation involving two different number types. -* -* @author Matthias Wohlmuth -*/ - -/** @addtogroup Common -* -* @{ -*/ - -/** \brief Compute type of the result of an arithmetic operation involving two different number types. -*/ -template <typename T1, typename T2> -struct PromotionTraits -{ -typedef decltype(std::declval<T1>()+std::declval<T2>()) PromotedType; -}; - -// Specialization for the case of two equal types -// One should think that the generic template should handle this case as well. -// However, the fvectortest.cc unit test fails without it if ENABLE_GMP is set. -template <typename T1> -struct PromotionTraits<T1,T1> { typedef T1 PromotedType; }; - -/** @} */ + /** + * @file + * @brief Compute type of the result of an arithmetic operation involving two different number types. + * + * @author Matthias Wohlmuth + */ + + /** @addtogroup Common + * + * @{ + */ + + /** \brief Compute type of the result of an arithmetic operation involving two different number types. + */ + template <typename T1, typename T2> + struct PromotionTraits + { + typedef decltype(std::declval<T1>()+std::declval<T2>()) PromotedType; + }; + + // Specialization for the case of two equal types + // One should think that the generic template should handle this case as well. + // However, the fvectortest.cc unit test fails without it if ENABLE_GMP is set. + template <typename T1> + struct PromotionTraits<T1,T1> { typedef T1 PromotedType; }; + + /** @} */ } // end namespace diff --git a/dune/common/propertymap.hh b/dune/common/propertymap.hh index 90d3369e9c7b85b5b85ea34d09cb61f0e8f54ff4..f57d19741cd4e78c792ed48f82a3238f9aec7f76 100644 --- a/dune/common/propertymap.hh +++ b/dune/common/propertymap.hh @@ -11,322 +11,322 @@ namespace Dune { -template<class PM> -struct PropertyMapTraits -{ -/** -* @brief The type of the key of the property map. -*/ -typedef typename PM::KeyType KeyType; -/** -* @brief The type of the values of the property map. -*/ -typedef typename PM::ValueType ValueType; -/** -* @brief The type of the reference to the values. -*/ -typedef typename PM::Reference Reference; -/** -* @brief The category the property map belongs to. -*/ -typedef typename PM::Category Category; -}; - -/** @brief Tag for the category of readable property maps. */ -struct ReadablePropertyMapTag -{}; - -/** @brief Tag for the category of writable property maps. */ -struct WritablePropertyMapTag -{}; - -/** -* @brief Tag for the category of readable and writable property -* maps. -*/ -struct ReadWritePropertyMapTag -: public ReadablePropertyMapTag, public WritablePropertyMapTag -{}; - -/** -* @brief Tag for the category of lvalue property maps. -*/ -struct LvaluePropertyMapTag -: public ReadWritePropertyMapTag -{}; - -template<class T> -struct PropertyMapTraits<T*> -{ -typedef T ValueType; -typedef ValueType& Reference; -typedef std::ptrdiff_t KeyType; -typedef LvaluePropertyMapTag Category; -}; - - -template<class T> -struct PropertyMapTraits<const T*> -{ -typedef T ValueType; -typedef const ValueType& Reference; -typedef std::ptrdiff_t KeyType; -typedef LvaluePropertyMapTag Category; -}; - -template<class Reference, class PropertyMap> -struct RAPropertyMapHelper -{}; - -template<class Reference, class PropertyMap, class Key> -inline Reference -get(const RAPropertyMapHelper<Reference,PropertyMap>& pmap, -const Key& key) -{ -return static_cast<const PropertyMap&>(pmap)[key]; -} - -template<class Reference, class PropertyMap, class Key, class Value> -inline void -put(const RAPropertyMapHelper<Reference,PropertyMap>& pmap, -const Key& key, const Value& value) -{ -static_assert(std::is_convertible<typename PropertyMap::Category,WritablePropertyMapTag>::value, -"WritablePropertyMapTag required!"); -static_cast<const PropertyMap&>(pmap)[key] = value; -} - -/** -* @brief Adapter to turn a random access iterator into a property map. -*/ -template<class RAI, class IM, -class T = typename std::iterator_traits<RAI>::value_type, -class R = typename std::iterator_traits<RAI>::reference> -class IteratorPropertyMap -: public RAPropertyMapHelper<R,IteratorPropertyMap<RAI,IM,T,R> > -{ -public: -/** -* @brief The type of the random access iterator. -*/ -typedef RAI RandomAccessIterator; - -/** -* @brief The type of the index map. -* -* This will convert the KeyType to std::ptrdiff_t via operator[](). -*/ -typedef IM IndexMap; - -/** -* @brief The key type of the property map. -*/ -typedef typename IndexMap::KeyType KeyType; - -/** -* @brief The value type of the property map. -*/ -typedef T ValueType; - -/** -* @brief The reference type of the property map. -*/ -typedef R Reference; - -/** -* @brief The category of this property map. -*/ -typedef LvaluePropertyMapTag Category; - -/** -* @brief Constructor. -* @param iter The random access iterator that -* provides the mapping. -* @param im The index map that maps the KeyType -* to the difference_type of the iterator. -*/ -inline IteratorPropertyMap(RandomAccessIterator iter, -const IndexMap& im=IndexMap()) -: iter_(iter), indexMap_(im) -{} - -/** @brief Constructor. */ -inline IteratorPropertyMap() -: iter_(), indexMap_() -{} - -/** @brief Access the a value by reference. */ -inline Reference operator[](KeyType key) const -{ -return *(iter_ + get(indexMap_, key)); -} - -private: -/** @brief The underlying iterator. */ -RandomAccessIterator iter_; -/** @brief The index map to use for the lookup. */ -IndexMap indexMap_; -}; - -/** -* @brief An adapter to turn an unique associative container -* into a property map. -*/ -template<typename T> -class AssociativePropertyMap -: RAPropertyMapHelper<typename T::value_type::second_type&, -AssociativePropertyMap<T> > -{ -/** -* @brief The type of the unique associative container. -*/ -typedef T UniqueAssociativeContainer; - -/** -* @brief The key type of the property map. -*/ -typedef typename UniqueAssociativeContainer::value_type::first_type -KeyType; - -/** -* @brief The value type of the property map. -*/ -typedef typename UniqueAssociativeContainer::value_type::second_type -ValueType; - -/** -* @brief The reference type of the property map. -*/ -typedef ValueType& Reference; - -/** -* @brief The category of the property map. -*/ -typedef LvaluePropertyMapTag Category; - -/** @brief Constructor */ -inline AssociativePropertyMap() -: map_(0) -{} - -/** @brief Constructor. */ -inline AssociativePropertyMap(UniqueAssociativeContainer& map) -: map_(&map) -{} - -/** -* @brief Access a property. -* @param key The key of the property. -*/ -inline Reference operator[](KeyType key) const -{ -return map_->find(key)->second; -} -private: -UniqueAssociativeContainer* map_; -}; - -/** -* @brief An adaptor to turn an unique associative container -* into a property map. -*/ -template<typename T> -class ConstAssociativePropertyMap -: RAPropertyMapHelper<const typename T::value_type::second_type&, -ConstAssociativePropertyMap<T> > -{ -/** -* @brief The type of the unique associative container. -*/ -typedef T UniqueAssociativeContainer; - -/** -* @brief The key type of the property map. -*/ -typedef typename UniqueAssociativeContainer::value_type::first_type -KeyType; - -/** -* @brief The value type of the property map. -*/ -typedef typename UniqueAssociativeContainer::value_type::second_type -ValueType; - -/** -* @brief The reference type of the property map. -*/ -typedef const ValueType& Reference; - -/** -* @brief The category of the property map. -*/ -typedef LvaluePropertyMapTag Category; - -/** @brief Constructor */ -inline ConstAssociativePropertyMap() -: map_(0) -{} - -/** @brief Constructor. */ -inline ConstAssociativePropertyMap(const UniqueAssociativeContainer& map) -: map_(&map) -{} - -/** -* @brief Access a property. -* @param key The key of the property. -*/ -inline Reference operator[](KeyType key) const -{ -return map_->find(key)->second; -} -private: -const UniqueAssociativeContainer* map_; -}; - -/** -* @brief A property map that applies the identity function to integers. -*/ -struct IdentityMap -: public RAPropertyMapHelper<std::size_t, IdentityMap> -{ -/** @brief The key type of the map. */ -typedef std::size_t KeyType; - -/** @brief The value type of the map. */ -typedef std::size_t ValueType; - -/** @brief The reference type of the map. */ -typedef std::size_t Reference; - -/** @brief The category of the map. */ -typedef ReadablePropertyMapTag Category; - -inline ValueType operator[](const KeyType& key) const -{ -return key; -} -}; - - -/** -* @brief Selector for the property map type. -* -* If present the type of the property map is accessible via the typedef Type. -*/ -template<typename T, typename C> -struct PropertyMapTypeSelector -{ -/** -* @brief the tag identifying the property. -*/ -typedef T Tag; -/** -* @brief The container type to whose entries the properties -* are attached. -*/ -typedef C Container; -}; + template<class PM> + struct PropertyMapTraits + { + /** + * @brief The type of the key of the property map. + */ + typedef typename PM::KeyType KeyType; + /** + * @brief The type of the values of the property map. + */ + typedef typename PM::ValueType ValueType; + /** + * @brief The type of the reference to the values. + */ + typedef typename PM::Reference Reference; + /** + * @brief The category the property map belongs to. + */ + typedef typename PM::Category Category; + }; + + /** @brief Tag for the category of readable property maps. */ + struct ReadablePropertyMapTag + {}; + + /** @brief Tag for the category of writable property maps. */ + struct WritablePropertyMapTag + {}; + + /** + * @brief Tag for the category of readable and writable property + * maps. + */ + struct ReadWritePropertyMapTag + : public ReadablePropertyMapTag, public WritablePropertyMapTag + {}; + + /** + * @brief Tag for the category of lvalue property maps. + */ + struct LvaluePropertyMapTag + : public ReadWritePropertyMapTag + {}; + + template<class T> + struct PropertyMapTraits<T*> + { + typedef T ValueType; + typedef ValueType& Reference; + typedef std::ptrdiff_t KeyType; + typedef LvaluePropertyMapTag Category; + }; + + + template<class T> + struct PropertyMapTraits<const T*> + { + typedef T ValueType; + typedef const ValueType& Reference; + typedef std::ptrdiff_t KeyType; + typedef LvaluePropertyMapTag Category; + }; + + template<class Reference, class PropertyMap> + struct RAPropertyMapHelper + {}; + + template<class Reference, class PropertyMap, class Key> + inline Reference + get(const RAPropertyMapHelper<Reference,PropertyMap>& pmap, + const Key& key) + { + return static_cast<const PropertyMap&>(pmap)[key]; + } + + template<class Reference, class PropertyMap, class Key, class Value> + inline void + put(const RAPropertyMapHelper<Reference,PropertyMap>& pmap, + const Key& key, const Value& value) + { + static_assert(std::is_convertible<typename PropertyMap::Category,WritablePropertyMapTag>::value, + "WritablePropertyMapTag required!"); + static_cast<const PropertyMap&>(pmap)[key] = value; + } + + /** + * @brief Adapter to turn a random access iterator into a property map. + */ + template<class RAI, class IM, + class T = typename std::iterator_traits<RAI>::value_type, + class R = typename std::iterator_traits<RAI>::reference> + class IteratorPropertyMap + : public RAPropertyMapHelper<R,IteratorPropertyMap<RAI,IM,T,R> > + { + public: + /** + * @brief The type of the random access iterator. + */ + typedef RAI RandomAccessIterator; + + /** + * @brief The type of the index map. + * + * This will convert the KeyType to std::ptrdiff_t via operator[](). + */ + typedef IM IndexMap; + + /** + * @brief The key type of the property map. + */ + typedef typename IndexMap::KeyType KeyType; + + /** + * @brief The value type of the property map. + */ + typedef T ValueType; + + /** + * @brief The reference type of the property map. + */ + typedef R Reference; + + /** + * @brief The category of this property map. + */ + typedef LvaluePropertyMapTag Category; + + /** + * @brief Constructor. + * @param iter The random access iterator that + * provides the mapping. + * @param im The index map that maps the KeyType + * to the difference_type of the iterator. + */ + inline IteratorPropertyMap(RandomAccessIterator iter, + const IndexMap& im=IndexMap()) + : iter_(iter), indexMap_(im) + {} + + /** @brief Constructor. */ + inline IteratorPropertyMap() + : iter_(), indexMap_() + {} + + /** @brief Access the a value by reference. */ + inline Reference operator[](KeyType key) const + { + return *(iter_ + get(indexMap_, key)); + } + + private: + /** @brief The underlying iterator. */ + RandomAccessIterator iter_; + /** @brief The index map to use for the lookup. */ + IndexMap indexMap_; + }; + + /** + * @brief An adapter to turn an unique associative container + * into a property map. + */ + template<typename T> + class AssociativePropertyMap + : RAPropertyMapHelper<typename T::value_type::second_type&, + AssociativePropertyMap<T> > + { + /** + * @brief The type of the unique associative container. + */ + typedef T UniqueAssociativeContainer; + + /** + * @brief The key type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::first_type + KeyType; + + /** + * @brief The value type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::second_type + ValueType; + + /** + * @brief The reference type of the property map. + */ + typedef ValueType& Reference; + + /** + * @brief The category of the property map. + */ + typedef LvaluePropertyMapTag Category; + + /** @brief Constructor */ + inline AssociativePropertyMap() + : map_(0) + {} + + /** @brief Constructor. */ + inline AssociativePropertyMap(UniqueAssociativeContainer& map) + : map_(&map) + {} + + /** + * @brief Access a property. + * @param key The key of the property. + */ + inline Reference operator[](KeyType key) const + { + return map_->find(key)->second; + } + private: + UniqueAssociativeContainer* map_; + }; + + /** + * @brief An adaptor to turn an unique associative container + * into a property map. + */ + template<typename T> + class ConstAssociativePropertyMap + : RAPropertyMapHelper<const typename T::value_type::second_type&, + ConstAssociativePropertyMap<T> > + { + /** + * @brief The type of the unique associative container. + */ + typedef T UniqueAssociativeContainer; + + /** + * @brief The key type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::first_type + KeyType; + + /** + * @brief The value type of the property map. + */ + typedef typename UniqueAssociativeContainer::value_type::second_type + ValueType; + + /** + * @brief The reference type of the property map. + */ + typedef const ValueType& Reference; + + /** + * @brief The category of the property map. + */ + typedef LvaluePropertyMapTag Category; + + /** @brief Constructor */ + inline ConstAssociativePropertyMap() + : map_(0) + {} + + /** @brief Constructor. */ + inline ConstAssociativePropertyMap(const UniqueAssociativeContainer& map) + : map_(&map) + {} + + /** + * @brief Access a property. + * @param key The key of the property. + */ + inline Reference operator[](KeyType key) const + { + return map_->find(key)->second; + } + private: + const UniqueAssociativeContainer* map_; + }; + + /** + * @brief A property map that applies the identity function to integers. + */ + struct IdentityMap + : public RAPropertyMapHelper<std::size_t, IdentityMap> + { + /** @brief The key type of the map. */ + typedef std::size_t KeyType; + + /** @brief The value type of the map. */ + typedef std::size_t ValueType; + + /** @brief The reference type of the map. */ + typedef std::size_t Reference; + + /** @brief The category of the map. */ + typedef ReadablePropertyMapTag Category; + + inline ValueType operator[](const KeyType& key) const + { + return key; + } + }; + + + /** + * @brief Selector for the property map type. + * + * If present the type of the property map is accessible via the typedef Type. + */ + template<typename T, typename C> + struct PropertyMapTypeSelector + { + /** + * @brief the tag identifying the property. + */ + typedef T Tag; + /** + * @brief The container type to whose entries the properties + * are attached. + */ + typedef C Container; + }; } diff --git a/dune/common/proxymemberaccess.hh b/dune/common/proxymemberaccess.hh index 92d6f67cf7bcd1585878a758d3e4986f0aff202c..1256f2f54f8d70de10e978c71817d2f37f67a663 100644 --- a/dune/common/proxymemberaccess.hh +++ b/dune/common/proxymemberaccess.hh @@ -5,115 +5,115 @@ #include <dune/internal/dune-common.hh> /** -* \file -* \brief infrastructure for supporting operator->() on both references and proxies -* \ingroup CxxUtilities -*/ + * \file + * \brief infrastructure for supporting operator->() on both references and proxies + * \ingroup CxxUtilities + */ #include <type_traits> #include <utility> namespace Dune { -namespace Impl { + namespace Impl { -// helper struct to store a temporary / proxy -// for the duration of the member access -template<typename T> -struct member_access_proxy_holder -{ + // helper struct to store a temporary / proxy + // for the duration of the member access + template<typename T> + struct member_access_proxy_holder + { -// only support moving the temporary into the holder object -member_access_proxy_holder(T&& t) -: _t(std::move(t)) -{} + // only support moving the temporary into the holder object + member_access_proxy_holder(T&& t) + : _t(std::move(t)) + {} -// The object is fundamentally a temporary, i.e. an rvalue, -// -const T* operator->() const -{ -return &_t; -} + // The object is fundamentally a temporary, i.e. an rvalue, + // + const T* operator->() const + { + return &_t; + } -T _t; + T _t; -}; + }; -} // end Impl namespace + } // end Impl namespace #ifdef DOXYGEN -//! Transparent support for providing member access to both lvalues and rvalues (temporary proxies). -/** -* If an iterator facade (like entity iterators) wants to allow the embedded implementation to -* return either an (internally stored) reference or a temporary object and expose these two -* behaviors to enable performance optimizations, operator->() needs special handling: If the -* implementation returns a reference, operator->() in the facade can simply return the address -* of the referenced object, but if the returned object is a temporary, we need to capture and -* store it in a helper object to make sure it outlives the member access. This function transparently -* supports both variants. It should be used like this: -* -* \code -* class iterator -* { -* ... -* -* decltype(handle_proxy_member_access(implementation.dereference())) -* operator->() const -* { -* return handle_proxy_member_access(implementation.dereference()); -* } -* -* ... -* }; -* \endcode -* -* \note This function exploits the special type deduction rules for unqualified rvalue references -* to distinguish between lvalues and rvalues and thus needs to be passed the object returned -* by the implementation. -* -* \ingroup CxxUtilities -*/ -template<typename T> -pointer_or_proxy_holder -handle_proxy_member_access(T&& t); + //! Transparent support for providing member access to both lvalues and rvalues (temporary proxies). + /** + * If an iterator facade (like entity iterators) wants to allow the embedded implementation to + * return either an (internally stored) reference or a temporary object and expose these two + * behaviors to enable performance optimizations, operator->() needs special handling: If the + * implementation returns a reference, operator->() in the facade can simply return the address + * of the referenced object, but if the returned object is a temporary, we need to capture and + * store it in a helper object to make sure it outlives the member access. This function transparently + * supports both variants. It should be used like this: + * + * \code + * class iterator + * { + * ... + * + * decltype(handle_proxy_member_access(implementation.dereference())) + * operator->() const + * { + * return handle_proxy_member_access(implementation.dereference()); + * } + * + * ... + * }; + * \endcode + * + * \note This function exploits the special type deduction rules for unqualified rvalue references + * to distinguish between lvalues and rvalues and thus needs to be passed the object returned + * by the implementation. + * + * \ingroup CxxUtilities + */ + template<typename T> + pointer_or_proxy_holder + handle_proxy_member_access(T&& t); #else // DOXYGEN -// This version matches lvalues (the C++ type deduction rules state that -// the T&& signature deduces to a reference iff the argument is an lvalue). -// As the argument is an lvalue, we do not have to worry about its lifetime -// and can just return its address. -template<typename T> -inline typename std::enable_if< -std::is_lvalue_reference<T>::value, -typename std::add_pointer< -typename std::remove_reference< -T ->::type ->::type ->::type -handle_proxy_member_access(T&& target) -{ -return ⌖ -} - -// This version matches rvalues (the C++ type deduction rules state that -// the T&& signature deduces to a non-reference iff the argument is an rvalue). -// In this case, we have to capture the rvalue in a new object to make sure it -// is kept alive for the duration of the member access. For this purpose, we move -// it into a member_access_proxy_holder instance. -template<typename T> -inline typename std::enable_if< -!std::is_lvalue_reference<T>::value, -Impl::member_access_proxy_holder<T> ->::type -handle_proxy_member_access(T&& target) -{ -return {std::forward<T>(target)}; -} + // This version matches lvalues (the C++ type deduction rules state that + // the T&& signature deduces to a reference iff the argument is an lvalue). + // As the argument is an lvalue, we do not have to worry about its lifetime + // and can just return its address. + template<typename T> + inline typename std::enable_if< + std::is_lvalue_reference<T>::value, + typename std::add_pointer< + typename std::remove_reference< + T + >::type + >::type + >::type + handle_proxy_member_access(T&& target) + { + return ⌖ + } + + // This version matches rvalues (the C++ type deduction rules state that + // the T&& signature deduces to a non-reference iff the argument is an rvalue). + // In this case, we have to capture the rvalue in a new object to make sure it + // is kept alive for the duration of the member access. For this purpose, we move + // it into a member_access_proxy_holder instance. + template<typename T> + inline typename std::enable_if< + !std::is_lvalue_reference<T>::value, + Impl::member_access_proxy_holder<T> + >::type + handle_proxy_member_access(T&& target) + { + return {std::forward<T>(target)}; + } #endif // DOXYGEN diff --git a/dune/common/quadmath.hh b/dune/common/quadmath.hh index 5b611291c39f0eef6fe5b55d3e3bb97d37c7a071..e0083ba52c6b7a02f89429d65ca39f7483db4f1a 100644 --- a/dune/common/quadmath.hh +++ b/dune/common/quadmath.hh @@ -21,435 +21,435 @@ namespace Dune { -namespace Impl -{ -// forward declaration -class Float128; - -} // end namespace Impl - -using Impl::Float128; - -// The purpose of this namespace is to move the `<cmath>` function overloads -// out of namespace `Dune`, see AlignedNumber in debugalign.hh. -namespace Impl -{ -using float128_t = __float128; - -/// Wrapper for quad-precision type __float128 -class Float128 -{ -float128_t value_ = 0.0q; - -public: -constexpr Float128() = default; -constexpr Float128(const float128_t& value) noexcept -: value_(value) -{} - -// constructor from any floating-point or integer type -template <class T, -std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> -constexpr Float128(const T& value) noexcept -: value_(value) -{} - -// constructor from pointer to null-terminated byte string -explicit Float128(const char* str) noexcept -: value_(strtoflt128(str, NULL)) -{} - -// accessors -constexpr operator float128_t() const noexcept { return value_; } - -constexpr float128_t const& value() const noexcept { return value_; } -constexpr float128_t& value() noexcept { return value_; } - -// I/O -template<class CharT, class Traits> -friend std::basic_istream<CharT, Traits>& -operator>>(std::basic_istream<CharT, Traits>& in, Float128& x) -{ -std::string buf; -buf.reserve(128); -in >> buf; -x.value() = strtoflt128(buf.c_str(), NULL); -return in; -} - -template<class CharT, class Traits> -friend std::basic_ostream<CharT, Traits>& -operator<<(std::basic_ostream<CharT, Traits>& out, const Float128& x) -{ -const std::size_t bufSize = 128; -CharT buf[128]; - -std::string format = "%." + std::to_string(out.precision()) + "Q" + -((out.flags() | std::ios_base::scientific) ? "e" : "f"); -const int numChars = quadmath_snprintf(buf, bufSize, format.c_str(), x.value()); -if (std::size_t(numChars) >= bufSize) { -DUNE_THROW(Dune::RangeError, "Failed to print Float128 value: buffer overflow"); -} -out << buf; -return out; -} - -// Increment, decrement -constexpr Float128& operator++() noexcept { ++value_; return *this; } -constexpr Float128& operator--() noexcept { --value_; return *this; } - -constexpr Float128 operator++(int) noexcept { Float128 tmp{*this}; ++value_; return tmp; } -constexpr Float128 operator--(int) noexcept { Float128 tmp{*this}; --value_; return tmp; } - -// unary operators -constexpr Float128 operator+() const noexcept { return Float128{+value_}; } -constexpr Float128 operator-() const noexcept { return Float128{-value_}; } - -// assignment operators + namespace Impl + { + // forward declaration + class Float128; + + } // end namespace Impl + + using Impl::Float128; + + // The purpose of this namespace is to move the `<cmath>` function overloads + // out of namespace `Dune`, see AlignedNumber in debugalign.hh. + namespace Impl + { + using float128_t = __float128; + + /// Wrapper for quad-precision type __float128 + class Float128 + { + float128_t value_ = 0.0q; + + public: + constexpr Float128() = default; + constexpr Float128(const float128_t& value) noexcept + : value_(value) + {} + + // constructor from any floating-point or integer type + template <class T, + std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> + constexpr Float128(const T& value) noexcept + : value_(value) + {} + + // constructor from pointer to null-terminated byte string + explicit Float128(const char* str) noexcept + : value_(strtoflt128(str, NULL)) + {} + + // accessors + constexpr operator float128_t() const noexcept { return value_; } + + constexpr float128_t const& value() const noexcept { return value_; } + constexpr float128_t& value() noexcept { return value_; } + + // I/O + template<class CharT, class Traits> + friend std::basic_istream<CharT, Traits>& + operator>>(std::basic_istream<CharT, Traits>& in, Float128& x) + { + std::string buf; + buf.reserve(128); + in >> buf; + x.value() = strtoflt128(buf.c_str(), NULL); + return in; + } + + template<class CharT, class Traits> + friend std::basic_ostream<CharT, Traits>& + operator<<(std::basic_ostream<CharT, Traits>& out, const Float128& x) + { + const std::size_t bufSize = 128; + CharT buf[128]; + + std::string format = "%." + std::to_string(out.precision()) + "Q" + + ((out.flags() | std::ios_base::scientific) ? "e" : "f"); + const int numChars = quadmath_snprintf(buf, bufSize, format.c_str(), x.value()); + if (std::size_t(numChars) >= bufSize) { + DUNE_THROW(Dune::RangeError, "Failed to print Float128 value: buffer overflow"); + } + out << buf; + return out; + } + + // Increment, decrement + constexpr Float128& operator++() noexcept { ++value_; return *this; } + constexpr Float128& operator--() noexcept { --value_; return *this; } + + constexpr Float128 operator++(int) noexcept { Float128 tmp{*this}; ++value_; return tmp; } + constexpr Float128 operator--(int) noexcept { Float128 tmp{*this}; --value_; return tmp; } + + // unary operators + constexpr Float128 operator+() const noexcept { return Float128{+value_}; } + constexpr Float128 operator-() const noexcept { return Float128{-value_}; } + + // assignment operators #define DUNE_ASSIGN_OP(OP) \ -constexpr Float128& operator OP(const Float128& u) noexcept \ -{ \ -value_ OP float128_t(u); \ -return *this; \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") + constexpr Float128& operator OP(const Float128& u) noexcept \ + { \ + value_ OP float128_t(u); \ + return *this; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") -DUNE_ASSIGN_OP(+=); -DUNE_ASSIGN_OP(-=); + DUNE_ASSIGN_OP(+=); + DUNE_ASSIGN_OP(-=); -DUNE_ASSIGN_OP(*=); -DUNE_ASSIGN_OP(/=); + DUNE_ASSIGN_OP(*=); + DUNE_ASSIGN_OP(/=); #undef DUNE_ASSIGN_OP -}; // end class Float128 + }; // end class Float128 -// binary operators: -// For symmetry provide overloads with arithmetic types -// in the first or second argument. + // binary operators: + // For symmetry provide overloads with arithmetic types + // in the first or second argument. #define DUNE_BINARY_OP(OP) \ -constexpr Float128 operator OP(const Float128& t, \ -const Float128& u) noexcept \ -{ \ -return Float128{float128_t(t) OP float128_t(u)}; \ -} \ -constexpr Float128 operator OP(const float128_t& t, \ -const Float128& u) noexcept \ -{ \ -return Float128{t OP float128_t(u)}; \ -} \ -constexpr Float128 operator OP(const Float128& t, \ -const float128_t& u) noexcept \ -{ \ -return Float128{float128_t(t) OP u}; \ -} \ -template <class T, \ -std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \ -constexpr Float128 operator OP(const T& t, \ -const Float128& u) noexcept \ -{ \ -return Float128{float128_t(t) OP float128_t(u)}; \ -} \ -template <class U, \ -std::enable_if_t<std::is_arithmetic<U>::value, int> = 0> \ -constexpr Float128 operator OP(const Float128& t, \ -const U& u) noexcept \ -{ \ -return Float128{float128_t(t) OP float128_t(u)}; \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_BINARY_OP(+); -DUNE_BINARY_OP(-); -DUNE_BINARY_OP(*); -DUNE_BINARY_OP(/); + constexpr Float128 operator OP(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + constexpr Float128 operator OP(const float128_t& t, \ + const Float128& u) noexcept \ + { \ + return Float128{t OP float128_t(u)}; \ + } \ + constexpr Float128 operator OP(const Float128& t, \ + const float128_t& u) noexcept \ + { \ + return Float128{float128_t(t) OP u}; \ + } \ + template <class T, \ + std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \ + constexpr Float128 operator OP(const T& t, \ + const Float128& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + template <class U, \ + std::enable_if_t<std::is_arithmetic<U>::value, int> = 0> \ + constexpr Float128 operator OP(const Float128& t, \ + const U& u) noexcept \ + { \ + return Float128{float128_t(t) OP float128_t(u)}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_OP(+); + DUNE_BINARY_OP(-); + DUNE_BINARY_OP(*); + DUNE_BINARY_OP(/); #undef DUNE_BINARY_OP -// logical operators: -// For symmetry provide overloads with arithmetic types -// in the first or second argument. + // logical operators: + // For symmetry provide overloads with arithmetic types + // in the first or second argument. #define DUNE_BINARY_BOOL_OP(OP) \ -constexpr bool operator OP(const Float128& t, \ -const Float128& u) noexcept \ -{ \ -return float128_t(t) OP float128_t(u); \ -} \ -template <class T, \ -std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \ -constexpr bool operator OP(const T& t, \ -const Float128& u) noexcept \ -{ \ -return float128_t(t) OP float128_t(u); \ -} \ -template <class U, \ -std::enable_if_t<std::is_arithmetic<U>::value, int> = 0> \ -constexpr bool operator OP(const Float128& t, \ -const U& u) noexcept \ -{ \ -return float128_t(t) OP float128_t(u); \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_BINARY_BOOL_OP(==); -DUNE_BINARY_BOOL_OP(!=); -DUNE_BINARY_BOOL_OP(<); -DUNE_BINARY_BOOL_OP(>); -DUNE_BINARY_BOOL_OP(<=); -DUNE_BINARY_BOOL_OP(>=); + constexpr bool operator OP(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + template <class T, \ + std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \ + constexpr bool operator OP(const T& t, \ + const Float128& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + template <class U, \ + std::enable_if_t<std::is_arithmetic<U>::value, int> = 0> \ + constexpr bool operator OP(const Float128& t, \ + const U& u) noexcept \ + { \ + return float128_t(t) OP float128_t(u); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_BOOL_OP(==); + DUNE_BINARY_BOOL_OP(!=); + DUNE_BINARY_BOOL_OP(<); + DUNE_BINARY_BOOL_OP(>); + DUNE_BINARY_BOOL_OP(<=); + DUNE_BINARY_BOOL_OP(>=); #undef DUNE_BINARY_BOOL_OP -// Overloads for the cmath functions + // Overloads for the cmath functions -// function with name `name` redirects to quadmath function `func` + // function with name `name` redirects to quadmath function `func` #define DUNE_UNARY_FUNC(name,func) \ -inline Float128 name(const Float128& u) noexcept \ -{ \ -return Float128{func (float128_t(u))}; \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") + inline Float128 name(const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") -// like DUNE_UNARY_FUNC but with cutom return type + // like DUNE_UNARY_FUNC but with cutom return type #define DUNE_CUSTOM_UNARY_FUNC(type,name,func) \ -inline type name(const Float128& u) noexcept \ -{ \ -return (type)(func (float128_t(u))); \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") + inline type name(const Float128& u) noexcept \ + { \ + return (type)(func (float128_t(u))); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") -// redirects to quadmath function with two arguments + // redirects to quadmath function with two arguments #define DUNE_BINARY_FUNC(name,func) \ -inline Float128 name(const Float128& t, \ -const Float128& u) noexcept \ -{ \ -return Float128{func (float128_t(t), float128_t(u))}; \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_UNARY_FUNC(abs, fabsq); -DUNE_UNARY_FUNC(acos, acosq); -DUNE_UNARY_FUNC(acosh, acoshq); -DUNE_UNARY_FUNC(asin, asinq); -DUNE_UNARY_FUNC(asinh, asinhq); -DUNE_UNARY_FUNC(atan, atanq); -DUNE_UNARY_FUNC(atanh, atanhq); -DUNE_UNARY_FUNC(cbrt, cbrtq); -DUNE_UNARY_FUNC(ceil, ceilq); -DUNE_UNARY_FUNC(cos, cosq); -DUNE_UNARY_FUNC(cosh, coshq); -DUNE_UNARY_FUNC(erf, erfq); -DUNE_UNARY_FUNC(erfc, erfcq); -DUNE_UNARY_FUNC(exp, expq); -DUNE_UNARY_FUNC(expm1, expm1q); -DUNE_UNARY_FUNC(fabs, fabsq); -DUNE_UNARY_FUNC(floor, floorq); -DUNE_CUSTOM_UNARY_FUNC(int, ilogb, ilogbq); -DUNE_UNARY_FUNC(lgamma, lgammaq); -DUNE_CUSTOM_UNARY_FUNC(long long int, llrint, llrintq); -DUNE_CUSTOM_UNARY_FUNC(long long int, llround, llroundq); -DUNE_UNARY_FUNC(log, logq); -DUNE_UNARY_FUNC(log10, log10q); -DUNE_UNARY_FUNC(log1p, log1pq); -DUNE_UNARY_FUNC(log2, log2q); -// DUNE_UNARY_FUNC(logb, logbq); // not available in gcc5 -DUNE_CUSTOM_UNARY_FUNC(long int, lrint, lrintq); -DUNE_CUSTOM_UNARY_FUNC(long int, lround, lroundq); -DUNE_UNARY_FUNC(nearbyint, nearbyintq); -DUNE_BINARY_FUNC(nextafter, nextafterq); -DUNE_BINARY_FUNC(pow, powq); // overload for integer argument see below -DUNE_UNARY_FUNC(rint, rintq); -DUNE_UNARY_FUNC(round, roundq); -DUNE_UNARY_FUNC(sin, sinq); -DUNE_UNARY_FUNC(sinh, sinhq); -DUNE_UNARY_FUNC(sqrt, sqrtq); -DUNE_UNARY_FUNC(tan, tanq); -DUNE_UNARY_FUNC(tanh, tanhq); -DUNE_UNARY_FUNC(tgamma, tgammaq); -DUNE_UNARY_FUNC(trunc, truncq); - -DUNE_CUSTOM_UNARY_FUNC(bool, isfinite, finiteq); -DUNE_CUSTOM_UNARY_FUNC(bool, isinf, isinfq); -DUNE_CUSTOM_UNARY_FUNC(bool, isnan, isnanq); -DUNE_CUSTOM_UNARY_FUNC(bool, signbit, signbitq); + inline Float128 name(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_UNARY_FUNC(abs, fabsq); + DUNE_UNARY_FUNC(acos, acosq); + DUNE_UNARY_FUNC(acosh, acoshq); + DUNE_UNARY_FUNC(asin, asinq); + DUNE_UNARY_FUNC(asinh, asinhq); + DUNE_UNARY_FUNC(atan, atanq); + DUNE_UNARY_FUNC(atanh, atanhq); + DUNE_UNARY_FUNC(cbrt, cbrtq); + DUNE_UNARY_FUNC(ceil, ceilq); + DUNE_UNARY_FUNC(cos, cosq); + DUNE_UNARY_FUNC(cosh, coshq); + DUNE_UNARY_FUNC(erf, erfq); + DUNE_UNARY_FUNC(erfc, erfcq); + DUNE_UNARY_FUNC(exp, expq); + DUNE_UNARY_FUNC(expm1, expm1q); + DUNE_UNARY_FUNC(fabs, fabsq); + DUNE_UNARY_FUNC(floor, floorq); + DUNE_CUSTOM_UNARY_FUNC(int, ilogb, ilogbq); + DUNE_UNARY_FUNC(lgamma, lgammaq); + DUNE_CUSTOM_UNARY_FUNC(long long int, llrint, llrintq); + DUNE_CUSTOM_UNARY_FUNC(long long int, llround, llroundq); + DUNE_UNARY_FUNC(log, logq); + DUNE_UNARY_FUNC(log10, log10q); + DUNE_UNARY_FUNC(log1p, log1pq); + DUNE_UNARY_FUNC(log2, log2q); + // DUNE_UNARY_FUNC(logb, logbq); // not available in gcc5 + DUNE_CUSTOM_UNARY_FUNC(long int, lrint, lrintq); + DUNE_CUSTOM_UNARY_FUNC(long int, lround, lroundq); + DUNE_UNARY_FUNC(nearbyint, nearbyintq); + DUNE_BINARY_FUNC(nextafter, nextafterq); + DUNE_BINARY_FUNC(pow, powq); // overload for integer argument see below + DUNE_UNARY_FUNC(rint, rintq); + DUNE_UNARY_FUNC(round, roundq); + DUNE_UNARY_FUNC(sin, sinq); + DUNE_UNARY_FUNC(sinh, sinhq); + DUNE_UNARY_FUNC(sqrt, sqrtq); + DUNE_UNARY_FUNC(tan, tanq); + DUNE_UNARY_FUNC(tanh, tanhq); + DUNE_UNARY_FUNC(tgamma, tgammaq); + DUNE_UNARY_FUNC(trunc, truncq); + + DUNE_CUSTOM_UNARY_FUNC(bool, isfinite, finiteq); + DUNE_CUSTOM_UNARY_FUNC(bool, isinf, isinfq); + DUNE_CUSTOM_UNARY_FUNC(bool, isnan, isnanq); + DUNE_CUSTOM_UNARY_FUNC(bool, signbit, signbitq); #undef DUNE_UNARY_FUNC #undef DUNE_CUSTOM_UNARY_FUNC #undef DUNE_BINARY_FUNC -// like DUNE_BINARY_FUNC but provide overloads with arithmetic -// types in the first or second argument. + // like DUNE_BINARY_FUNC but provide overloads with arithmetic + // types in the first or second argument. #define DUNE_BINARY_ARITHMETIC_FUNC(name,func) \ -inline Float128 name(const Float128& t, \ -const Float128& u) noexcept \ -{ \ -return Float128{func (float128_t(t), float128_t(u))}; \ -} \ -template <class T, \ -std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \ -inline Float128 name(const T& t, \ -const Float128& u) noexcept \ -{ \ -return Float128{func (float128_t(t), float128_t(u))}; \ -} \ -template <class U, \ -std::enable_if_t<std::is_arithmetic<U>::value, int> = 0> \ -inline Float128 name(const Float128& t, \ -const U& u) noexcept \ -{ \ -return Float128{func (float128_t(t), float128_t(u))}; \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_BINARY_ARITHMETIC_FUNC(atan2,atan2q); -DUNE_BINARY_ARITHMETIC_FUNC(copysign,copysignq); -DUNE_BINARY_ARITHMETIC_FUNC(fdim,fdimq); -DUNE_BINARY_ARITHMETIC_FUNC(fmax,fmaxq); -DUNE_BINARY_ARITHMETIC_FUNC(fmin,fminq); -DUNE_BINARY_ARITHMETIC_FUNC(fmod,fmodq); -DUNE_BINARY_ARITHMETIC_FUNC(hypot,hypotq); -DUNE_BINARY_ARITHMETIC_FUNC(remainder,remainderq); + inline Float128 name(const Float128& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + template <class T, \ + std::enable_if_t<std::is_arithmetic<T>::value, int> = 0> \ + inline Float128 name(const T& t, \ + const Float128& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + template <class U, \ + std::enable_if_t<std::is_arithmetic<U>::value, int> = 0> \ + inline Float128 name(const Float128& t, \ + const U& u) noexcept \ + { \ + return Float128{func (float128_t(t), float128_t(u))}; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_BINARY_ARITHMETIC_FUNC(atan2,atan2q); + DUNE_BINARY_ARITHMETIC_FUNC(copysign,copysignq); + DUNE_BINARY_ARITHMETIC_FUNC(fdim,fdimq); + DUNE_BINARY_ARITHMETIC_FUNC(fmax,fmaxq); + DUNE_BINARY_ARITHMETIC_FUNC(fmin,fminq); + DUNE_BINARY_ARITHMETIC_FUNC(fmod,fmodq); + DUNE_BINARY_ARITHMETIC_FUNC(hypot,hypotq); + DUNE_BINARY_ARITHMETIC_FUNC(remainder,remainderq); #undef DUNE_BINARY_ARITHMETIC_FUNC -// some more cmath functions with special signature - -inline Float128 fma(const Float128& t, const Float128& u, const Float128& v) -{ -return Float128{fmaq(float128_t(t),float128_t(u),float128_t(v))}; -} - -inline Float128 frexp(const Float128& u, int* p) -{ -return Float128{frexpq(float128_t(u), p)}; -} - -inline Float128 ldexp(const Float128& u, int p) -{ -return Float128{ldexpq(float128_t(u), p)}; -} - -inline Float128 remquo(const Float128& t, const Float128& u, int* quo) -{ -return Float128{remquoq(float128_t(t), float128_t(u), quo)}; -} - -inline Float128 scalbln(const Float128& u, long int e) -{ -return Float128{scalblnq(float128_t(u), e)}; -} - -inline Float128 scalbn(const Float128& u, int e) -{ -return Float128{scalbnq(float128_t(u), e)}; -} - -/// \brief Overload of `pow` function for integer exponents. -// NOTE: This is much faster than a pow(x, Float128(p)) call -// NOTE: This is a modified version of boost::math::cstdfloat::detail::pown -// (adapted to the type Float128) that is part of the Boost 1.65 Math toolkit 2.8.0 -// and is implemented by Christopher Kormanyos, John Maddock, and Paul A. Bristow, -// distributed under the Boost Software License, Version 1.0 -// (See http://www.boost.org/LICENSE_1_0.txt) -template <class Int, -std::enable_if_t<std::is_integral<Int>::value, int> = 0> -inline Float128 pow(const Float128& x, const Int p) -{ -static const Float128 max_value = FLT128_MAX; -static const Float128 min_value = FLT128_MIN; -static const Float128 inf_value = float128_t{1} / float128_t{0}; - -const bool isneg = (x < 0); -const bool isnan = (x != x); -const bool isinf = (isneg ? bool(-x > max_value) : bool(+x > max_value)); - -if (isnan) { return x; } -if (isinf) { return Float128{nanq("")}; } - -const Float128 abs_x = (isneg ? -x : x); -if (p < Int(0)) { -if (abs_x < min_value) -return (isneg ? -inf_value : +inf_value); -else -return Float128(1) / pow(x, Int(-p)); -} - -if (p == Int(0)) { return Float128(1); } -if (p == Int(1)) { return x; } -if (abs_x > max_value) -return (isneg ? -inf_value : +inf_value); - -if (p == Int(2)) { return (x * x); } -if (p == Int(3)) { return ((x * x) * x); } -if (p == Int(4)) { const Float128 x2 = (x * x); return (x2 * x2); } - -Float128 result = ((p % Int(2)) != Int(0)) ? x : Float128(1); -Float128 xn = x; // binary powers of x - -Int p2 = p; -while (Int(p2 /= 2) != Int(0)) { -xn *= xn; // Square xn for each binary power - -const bool has_binary_power = (Int(p2 % Int(2)) != Int(0)); -if (has_binary_power) -result *= xn; -} - -return result; -} - - -} // end namespace Impl - -template <> -struct IsNumber<Impl::Float128> -: public std::true_type {}; + // some more cmath functions with special signature + + inline Float128 fma(const Float128& t, const Float128& u, const Float128& v) + { + return Float128{fmaq(float128_t(t),float128_t(u),float128_t(v))}; + } + + inline Float128 frexp(const Float128& u, int* p) + { + return Float128{frexpq(float128_t(u), p)}; + } + + inline Float128 ldexp(const Float128& u, int p) + { + return Float128{ldexpq(float128_t(u), p)}; + } + + inline Float128 remquo(const Float128& t, const Float128& u, int* quo) + { + return Float128{remquoq(float128_t(t), float128_t(u), quo)}; + } + + inline Float128 scalbln(const Float128& u, long int e) + { + return Float128{scalblnq(float128_t(u), e)}; + } + + inline Float128 scalbn(const Float128& u, int e) + { + return Float128{scalbnq(float128_t(u), e)}; + } + + /// \brief Overload of `pow` function for integer exponents. + // NOTE: This is much faster than a pow(x, Float128(p)) call + // NOTE: This is a modified version of boost::math::cstdfloat::detail::pown + // (adapted to the type Float128) that is part of the Boost 1.65 Math toolkit 2.8.0 + // and is implemented by Christopher Kormanyos, John Maddock, and Paul A. Bristow, + // distributed under the Boost Software License, Version 1.0 + // (See http://www.boost.org/LICENSE_1_0.txt) + template <class Int, + std::enable_if_t<std::is_integral<Int>::value, int> = 0> + inline Float128 pow(const Float128& x, const Int p) + { + static const Float128 max_value = FLT128_MAX; + static const Float128 min_value = FLT128_MIN; + static const Float128 inf_value = float128_t{1} / float128_t{0}; + + const bool isneg = (x < 0); + const bool isnan = (x != x); + const bool isinf = (isneg ? bool(-x > max_value) : bool(+x > max_value)); + + if (isnan) { return x; } + if (isinf) { return Float128{nanq("")}; } + + const Float128 abs_x = (isneg ? -x : x); + if (p < Int(0)) { + if (abs_x < min_value) + return (isneg ? -inf_value : +inf_value); + else + return Float128(1) / pow(x, Int(-p)); + } + + if (p == Int(0)) { return Float128(1); } + if (p == Int(1)) { return x; } + if (abs_x > max_value) + return (isneg ? -inf_value : +inf_value); + + if (p == Int(2)) { return (x * x); } + if (p == Int(3)) { return ((x * x) * x); } + if (p == Int(4)) { const Float128 x2 = (x * x); return (x2 * x2); } + + Float128 result = ((p % Int(2)) != Int(0)) ? x : Float128(1); + Float128 xn = x; // binary powers of x + + Int p2 = p; + while (Int(p2 /= 2) != Int(0)) { + xn *= xn; // Square xn for each binary power + + const bool has_binary_power = (Int(p2 % Int(2)) != Int(0)); + if (has_binary_power) + result *= xn; + } + + return result; + } + + + } // end namespace Impl + + template <> + struct IsNumber<Impl::Float128> + : public std::true_type {}; } // end namespace Dune namespace std { #ifndef NO_STD_NUMERIC_LIMITS_SPECIALIZATION -template <> -class numeric_limits<Dune::Impl::Float128> -{ -using Float128 = Dune::Impl::Float128; -using float128_t = Dune::Impl::float128_t; - -public: -static constexpr bool is_specialized = true; -static constexpr Float128 min() noexcept { return FLT128_MIN; } -static constexpr Float128 max() noexcept { return FLT128_MAX; } -static constexpr Float128 lowest() noexcept { return -FLT128_MAX; } -static constexpr int digits = FLT128_MANT_DIG; -static constexpr int digits10 = 34; -static constexpr int max_digits10 = 36; -static constexpr bool is_signed = true; -static constexpr bool is_integer = false; -static constexpr bool is_exact = false; -static constexpr int radix = 2; -static constexpr Float128 epsilon() noexcept { return FLT128_EPSILON; } -static constexpr Float128 round_error() noexcept { return float128_t{0.5}; } -static constexpr int min_exponent = FLT128_MIN_EXP; -static constexpr int min_exponent10 = FLT128_MIN_10_EXP; -static constexpr int max_exponent = FLT128_MAX_EXP; -static constexpr int max_exponent10 = FLT128_MAX_10_EXP; -static constexpr bool has_infinity = true; -static constexpr bool has_quiet_NaN = true; -static constexpr bool has_signaling_NaN = false; -static constexpr float_denorm_style has_denorm = denorm_present; -static constexpr bool has_denorm_loss = false; -static constexpr Float128 infinity() noexcept { return float128_t{1}/float128_t{0}; } -static Float128 quiet_NaN() noexcept { return nanq(""); } -static constexpr Float128 signaling_NaN() noexcept { return float128_t{}; } -static constexpr Float128 denorm_min() noexcept { return FLT128_DENORM_MIN; } -static constexpr bool is_iec559 = true; -static constexpr bool is_bounded = false; -static constexpr bool is_modulo = false; -static constexpr bool traps = false; -static constexpr bool tinyness_before = false; -static constexpr float_round_style round_style = round_to_nearest; -}; + template <> + class numeric_limits<Dune::Impl::Float128> + { + using Float128 = Dune::Impl::Float128; + using float128_t = Dune::Impl::float128_t; + + public: + static constexpr bool is_specialized = true; + static constexpr Float128 min() noexcept { return FLT128_MIN; } + static constexpr Float128 max() noexcept { return FLT128_MAX; } + static constexpr Float128 lowest() noexcept { return -FLT128_MAX; } + static constexpr int digits = FLT128_MANT_DIG; + static constexpr int digits10 = 34; + static constexpr int max_digits10 = 36; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr int radix = 2; + static constexpr Float128 epsilon() noexcept { return FLT128_EPSILON; } + static constexpr Float128 round_error() noexcept { return float128_t{0.5}; } + static constexpr int min_exponent = FLT128_MIN_EXP; + static constexpr int min_exponent10 = FLT128_MIN_10_EXP; + static constexpr int max_exponent = FLT128_MAX_EXP; + static constexpr int max_exponent10 = FLT128_MAX_10_EXP; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = false; + static constexpr float_denorm_style has_denorm = denorm_present; + static constexpr bool has_denorm_loss = false; + static constexpr Float128 infinity() noexcept { return float128_t{1}/float128_t{0}; } + static Float128 quiet_NaN() noexcept { return nanq(""); } + static constexpr Float128 signaling_NaN() noexcept { return float128_t{}; } + static constexpr Float128 denorm_min() noexcept { return FLT128_DENORM_MIN; } + static constexpr bool is_iec559 = true; + static constexpr bool is_bounded = false; + static constexpr bool is_modulo = false; + static constexpr bool traps = false; + static constexpr bool tinyness_before = false; + static constexpr float_round_style round_style = round_to_nearest; + }; #endif } // end namespace std diff --git a/dune/common/rangeutilities.hh b/dune/common/rangeutilities.hh index f0086c159b578ac4bd9a99b8064a6c6921c6471a..183309a0f27335d5c29f84377648fa3e0e5bba7c 100644 --- a/dune/common/rangeutilities.hh +++ b/dune/common/rangeutilities.hh @@ -11,821 +11,821 @@ #include <bitset> /** -* \file -* -* \brief Utilities for reduction like operations on ranges -* \author Christian Engwer -*/ + * \file + * + * \brief Utilities for reduction like operations on ranges + * \author Christian Engwer + */ namespace Dune { -/** -* @addtogroup RangeUtilities -* @{ -*/ - -/** -\brief compute the maximum value over a range - -overloads for scalar values, and ranges exist -*/ -template <typename T, -typename std::enable_if<IsIterable<T>::value, int>::type = 0> -typename T::value_type -max_value(const T & v) { -using std::max_element; -return *max_element(v.begin(), v.end()); -} - -template <typename T, -typename std::enable_if<!IsIterable<T>::value, int>::type = 0> -const T & max_value(const T & v) { return v; } - -/** -\brief compute the minimum value over a range - -overloads for scalar values, and ranges exist -*/ -template <typename T, -typename std::enable_if<IsIterable<T>::value, int>::type = 0> -typename T::value_type -min_value(const T & v) { -using std::min_element; -return *min_element(v.begin(), v.end()); -} - -template <typename T, -typename std::enable_if<!IsIterable<T>::value, int>::type = 0> -const T & min_value(const T & v) { return v; } - -/** -\brief similar to std::bitset<N>::any() return true, if any entries is true - -overloads for scalar values, ranges, and std::bitset<N> exist -*/ -template <typename T, -typename std::enable_if<IsIterable<T>::value, int>::type = 0> -bool any_true(const T & v) { -bool b = false; -for (const auto & e : v) -b = b or bool(e); -return b; -} - -template <typename T, -typename std::enable_if<!IsIterable<T>::value, int>::type = 0> -bool any_true(const T & v) { return v; } - -template<std::size_t N> -bool any_true(const std::bitset<N> & b) -{ -return b.any(); -} - -/** -\brief similar to std::bitset<N>::all() return true, if any entries is true - -overloads for scalar values, ranges, and std::bitset<N> exist -*/ -template <typename T, -typename std::enable_if<IsIterable<T>::value, int>::type = 0> -bool all_true(const T & v) { -bool b = true; -for (const auto & e : v) -b = b and bool(e); -return b; -} - -template <typename T, -typename std::enable_if<!IsIterable<T>::value, int>::type = 0> -bool all_true(const T & v) { return v; } - -template<std::size_t N> -bool all_true(const std::bitset<N> & b) -{ -return b.all(); -} - - - -namespace Impl -{ - -template <class T> -class IntegralRangeIterator -{ -public: -typedef std::random_access_iterator_tag iterator_category; -typedef T value_type; -typedef std::make_signed_t<T> difference_type; -typedef const T *pointer; -typedef T reference; - -constexpr IntegralRangeIterator() noexcept : value_(0) {} -constexpr explicit IntegralRangeIterator(value_type value) noexcept : value_(value) {} - -pointer operator->() const noexcept { return &value_; } -constexpr reference operator*() const noexcept { return value_; } - -constexpr reference operator[]( difference_type n ) const noexcept { return (value_ + n); } - -constexpr bool operator==(const IntegralRangeIterator & other) const noexcept { return (value_ == other.value_); } -constexpr bool operator!=(const IntegralRangeIterator & other) const noexcept { return (value_ != other.value_); } - -constexpr bool operator<(const IntegralRangeIterator & other) const noexcept { return (value_ <= other.value_); } -constexpr bool operator<=(const IntegralRangeIterator & other) const noexcept { return (value_ <= other.value_); } -constexpr bool operator>(const IntegralRangeIterator & other) const noexcept { return (value_ >= other.value_); } -constexpr bool operator>=(const IntegralRangeIterator & other) const noexcept { return (value_ >= other.value_); } - -IntegralRangeIterator& operator++() noexcept { ++value_; return *this; } -IntegralRangeIterator operator++(int) noexcept { IntegralRangeIterator copy( *this ); ++(*this); return copy; } - -IntegralRangeIterator& operator--() noexcept { --value_; return *this; } -IntegralRangeIterator operator--(int) noexcept { IntegralRangeIterator copy( *this ); --(*this); return copy; } - -IntegralRangeIterator& operator+=(difference_type n) noexcept { value_ += n; return *this; } -IntegralRangeIterator& operator-=(difference_type n) noexcept { value_ -= n; return *this; } - -friend constexpr IntegralRangeIterator operator+(const IntegralRangeIterator &a, difference_type n) noexcept { return IntegralRangeIterator(a.value_ + n); } -friend constexpr IntegralRangeIterator operator+(difference_type n, const IntegralRangeIterator &a) noexcept { return IntegralRangeIterator(a.value_ + n); } -friend constexpr IntegralRangeIterator operator-(const IntegralRangeIterator &a, difference_type n) noexcept { return IntegralRangeIterator(a.value_ - n); } - -constexpr difference_type operator-(const IntegralRangeIterator &other) const noexcept { return (static_cast<difference_type>(value_) - static_cast<difference_type>(other.value_)); } - -private: -value_type value_; -}; - -} // namespace Impl - - - -/** -* \brief dynamic integer range for use in range-based for loops -* -* \note This range can also be used in Hybrid::forEach, resulting in a dynamic -* for loop over the contained integers. -* -* \tparam T type of integers contained in the range -**/ -template <class T> -class IntegralRange -{ -public: -/** \brief type of integers contained in the range **/ -typedef T value_type; -/** \brief type of iterator **/ -typedef Impl::IntegralRangeIterator<T> iterator; -/** \brief unsigned integer type corresponding to value_type **/ -typedef std::make_unsigned_t<T> size_type; - -/** \brief construct integer range [from, to) **/ -constexpr IntegralRange(value_type from, value_type to) noexcept : from_(from), to_(to) {} -/** \brief construct integer range [0, to) **/ -constexpr explicit IntegralRange(value_type to) noexcept : from_(0), to_(to) {} -/** \brief construct integer range std::pair **/ -constexpr IntegralRange(std::pair<value_type, value_type> range) noexcept : from_(range.first), to_(range.second) {} - -/** \brief obtain a random-access iterator to the first element **/ -constexpr iterator begin() const noexcept { return iterator(from_); } -/** \brief obtain a random-access iterator past the last element **/ -constexpr iterator end() const noexcept { return iterator(to_); } - -/** \brief access specified element **/ -constexpr value_type operator[](const value_type &i) const noexcept { return (from_ + i); } - -/** \brief check whether the range is empty **/ -constexpr bool empty() const noexcept { return (from_ == to_); } -/** \brief obtain number of elements in the range **/ -constexpr size_type size() const noexcept { return (static_cast<size_type>(to_) - static_cast<size_type>(from_)); } - -private: -value_type from_, to_; -}; - - -/** -* \brief static integer range for use in range-based for loops -* -* This is a compile-time static variant of the IntegralRange. Apart from -* returning all range information statically, it casts into the corresponding -* std::integer_sequence. -* -* \note This range can also be used in Hybrid::forEach, resulting in a static -* for loop over the contained integers like a std::integer_sequence. -* -* \tparam T type of integers contained in the range -* \tparam to first element not contained in the range -* \tparam from first element contained in the range, defaults to 0 -**/ -template <class T, T to, T from = 0> -class StaticIntegralRange -{ -template <T ofs, T... i> -static std::integer_sequence<T, (i+ofs)...> shift_integer_sequence(std::integer_sequence<T, i...>); - -public: -/** \brief type of integers contained in the range **/ -typedef T value_type; -/** \brief type of iterator **/ -typedef Impl::IntegralRangeIterator<T> iterator; -/** \brief unsigned integer type corresponding to value_type **/ -typedef std::make_unsigned_t<T> size_type; - -/** \brief type of corresponding std::integer_sequence **/ -typedef decltype(shift_integer_sequence<from>(std::make_integer_sequence<T, to-from>())) integer_sequence; - -/** \brief default constructor **/ -constexpr StaticIntegralRange() noexcept = default; - -/** \brief cast into dynamic IntegralRange **/ -constexpr operator IntegralRange<T>() const noexcept { return {from, to}; } -/** \brief cast into corresponding std::integer_sequence **/ -constexpr operator integer_sequence() const noexcept { return {}; } - -/** \brief obtain a random-access iterator to the first element **/ -static constexpr iterator begin() noexcept { return iterator(from); } -/** \brief obtain a random-access iterator past the last element **/ -static constexpr iterator end() noexcept { return iterator(to); } - -/** \brief access specified element (static version) **/ -template <class U, U i> -constexpr auto operator[](const std::integral_constant<U, i> &) const noexcept --> std::integral_constant<value_type, from + static_cast<value_type>(i)> -{ -return {}; -} - -/** \brief access specified element (dynamic version) **/ -constexpr value_type operator[](const size_type &i) const noexcept { return (from + static_cast<value_type>(i)); } - -/** \brief check whether the range is empty **/ -static constexpr std::integral_constant<bool, from == to> empty() noexcept { return {}; } -/** \brief obtain number of elements in the range **/ -static constexpr std::integral_constant<size_type, static_cast<size_type>(to) - static_cast<size_type>(from) > size() noexcept { return {}; } -}; - -/** -\brief free standing function for setting up a range based for loop -over an integer range -for (auto i: range(0,10)) // 0,1,2,3,4,5,6,7,8,9 -or -for (auto i: range(-10,10)) // -10,-9,..,8,9 -or -for (auto i: range(10)) // 0,1,2,3,4,5,6,7,8,9 -*/ -template<class T, class U, -std::enable_if_t<std::is_same<std::decay_t<T>, std::decay_t<U>>::value, int> = 0, -std::enable_if_t<std::is_integral<std::decay_t<T>>::value, int> = 0> -inline static IntegralRange<std::decay_t<T>> range(T &&from, U &&to) noexcept -{ -return IntegralRange<std::decay_t<T>>(std::forward<T>(from), std::forward<U>(to)); -} - -template<class T, std::enable_if_t<std::is_integral<std::decay_t<T>>::value, int> = 0> -inline static IntegralRange<std::decay_t<T>> range(T &&to) noexcept -{ -return IntegralRange<std::decay_t<T>>(std::forward<T>(to)); -} - -template<class T, std::enable_if_t<std::is_enum<std::decay_t<T>>::value, int> = 0> -inline static IntegralRange<std::underlying_type_t<std::decay_t<T>>> range(T &&to) noexcept -{ -return IntegralRange<std::underlying_type_t<std::decay_t<T>>>(std::forward<T>(to)); -} - -template<class T, T from, T to> -inline static StaticIntegralRange<T, to, from> range(std::integral_constant<T, from>, std::integral_constant<T, to>) noexcept -{ -return {}; -} - -template<class T, T to> -inline static StaticIntegralRange<T, to> range(std::integral_constant<T, to>) noexcept -{ -return {}; -} - - - -/** -* \brief Tag to enable value based transformations in TransformedRangeView -*/ -struct ValueTransformationTag {}; - -/** -* \brief Tag to enable iterator based transformations in TransformedRangeView -*/ -struct IteratorTransformationTag {}; - -namespace Impl -{ - -// Helper class to mimic a pointer for proxy objects. -// This is needed to implement operator-> on an iterator -// using proxy-values. It stores the proxy value but -// provides operator-> like a pointer. -template<class ProxyType> -class PointerProxy -{ -public: -PointerProxy(ProxyType&& p) : p_(p) -{} - -ProxyType* operator->() -{ -return &p_; -} - -ProxyType p_; -}; - -// An iterator transforming a wrapped iterator using -// an unary function. It inherits the iterator-category -// of the underlying iterator. -template <class I, class F, class TransformationType, class C = typename std::iterator_traits<I>::iterator_category> -class TransformedRangeIterator; - -template <class I, class F, class TransformationType> -class TransformedRangeIterator<I,F,TransformationType,std::forward_iterator_tag> -{ -protected: - -static decltype(auto) transform(const F& f, const I& it) { -if constexpr (std::is_same_v<TransformationType,IteratorTransformationTag>) -return f(it); -else -return f(*it); -} - -public: -using iterator_category = std::forward_iterator_tag; -using reference = decltype(transform(std::declval<F>(), std::declval<I>())); -using value_type = std::decay_t<reference>; -using pointer = PointerProxy<value_type>; - -// If we later want to allow standalone TransformedRangeIterators, -// we could customize the FunctionPointer to be a default-constructible, -// copy-assignable type storing a function but acting like a pointer -// to function. -using FunctionPointer = const F*; - -constexpr TransformedRangeIterator(const I& it, FunctionPointer f) noexcept : -it_(it), -f_(f) -{} - -// Explicitly initialize members. Using a plain -// -// constexpr TransformedRangeIterator() noexcept {} -// -// would default-initialize the members while -// -// constexpr TransformedRangeIterator() noexcept : it_(), f_() {} -// -// leads to value-initialization. This is a case where -// both are really different. If it_ is a raw pointer (i.e. POD) -// then default-initialization leaves it uninitialized while -// value-initialization zero-initializes it. -constexpr TransformedRangeIterator() noexcept : -it_(), -f_() -{} - -// Dereferencing returns a value created by the function -constexpr reference operator*() const noexcept { -return transform(*f_, it_); -} - -// Dereferencing returns a value created by the function -pointer operator->() const noexcept { -return transform(*f_, it_); -} - -constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; - -constexpr bool operator==(const TransformedRangeIterator& other) const noexcept { -return (it_ == other.it_); -} - -constexpr bool operator!=(const TransformedRangeIterator& other) const noexcept { -return (it_ != other.it_); -} - -TransformedRangeIterator& operator++() noexcept { -++it_; -return *this; -} - -TransformedRangeIterator operator++(int) noexcept { -TransformedRangeIterator copy(*this); -++(*this); -return copy; -} - -protected: -I it_; -FunctionPointer f_; -}; - - - -template <class I, class F, class T> -class TransformedRangeIterator<I,F,T,std::bidirectional_iterator_tag> : -public TransformedRangeIterator<I,F,T,std::forward_iterator_tag> -{ -protected: -using Base = TransformedRangeIterator<I,F,T,std::forward_iterator_tag>; -using Base::it_; -using Base::f_; -public: -using iterator_category = std::bidirectional_iterator_tag; -using reference = typename Base::reference; -using value_type = typename Base::value_type; -using pointer = typename Base::pointer; - -using FunctionPointer = typename Base::FunctionPointer; - -using Base::Base; - -// Member functions of the forward_iterator that need -// to be redefined because the base class methods return a -// forward_iterator. -constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; - -TransformedRangeIterator& operator++() noexcept { -++it_; -return *this; -} - -TransformedRangeIterator operator++(int) noexcept { -TransformedRangeIterator copy(*this); -++(*this); -return copy; -} - -// Additional member functions of bidirectional_iterator -TransformedRangeIterator& operator--() noexcept { ---(this->it_); -return *this; -} - -TransformedRangeIterator operator--(int) noexcept { -TransformedRangeIterator copy(*this); ---(*this); -return copy; -} -}; - - - -template <class I, class F, class T> -class TransformedRangeIterator<I,F,T,std::random_access_iterator_tag> : -public TransformedRangeIterator<I,F,T,std::bidirectional_iterator_tag> -{ -protected: -using Base = TransformedRangeIterator<I,F,T,std::bidirectional_iterator_tag>; -using Base::it_; -using Base::f_; -public: -using iterator_category = std::random_access_iterator_tag; -using reference = typename Base::reference; -using value_type = typename Base::value_type; -using pointer = typename Base::pointer; -using difference_type = typename std::iterator_traits<I>::difference_type; - -using FunctionPointer = typename Base::FunctionPointer; - -using Base::Base; - -// Member functions of the forward_iterator that need -// to be redefined because the base class methods return a -// forward_iterator. -constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; - -TransformedRangeIterator& operator++() noexcept { -++it_; -return *this; -} - -TransformedRangeIterator operator++(int) noexcept { -TransformedRangeIterator copy(*this); -++(*this); -return copy; -} - -// Member functions of the bidirectional_iterator that need -// to be redefined because the base class methods return a -// bidirectional_iterator. -TransformedRangeIterator& operator--() noexcept { ---(this->it_); -return *this; -} - -TransformedRangeIterator operator--(int) noexcept { -TransformedRangeIterator copy(*this); ---(*this); -return copy; -} - -// Additional member functions of random_access_iterator -TransformedRangeIterator& operator+=(difference_type n) noexcept { -it_ += n; -return *this; -} - -TransformedRangeIterator& operator-=(difference_type n) noexcept { -it_ -= n; -return *this; -} - -bool operator<(const TransformedRangeIterator& other) noexcept { -return it_<other.it_; -} - -bool operator<=(const TransformedRangeIterator& other) noexcept { -return it_<=other.it_; -} - -bool operator>(const TransformedRangeIterator& other) noexcept { -return it_>other.it_; -} - -bool operator>=(const TransformedRangeIterator& other) noexcept { -return it_>=other.it_; -} - -reference operator[](difference_type n) noexcept { -return Base::transform(*f_, it_+n); -} - -friend -TransformedRangeIterator operator+(const TransformedRangeIterator& it, difference_type n) noexcept { -return TransformedRangeIterator(it.it_+n, it.f_); -} - -friend -TransformedRangeIterator operator+(difference_type n, const TransformedRangeIterator& it) noexcept { -return TransformedRangeIterator(n+it.it_, it.f_); -} - -friend -TransformedRangeIterator operator-(const TransformedRangeIterator& it, difference_type n) noexcept { -return TransformedRangeIterator(it.it_-n, it.f_); -} - -friend -difference_type operator-(const TransformedRangeIterator& first, const TransformedRangeIterator& second) noexcept { -return first.it_-second.it_; -} -}; - - -} // namespace Impl - - - -/** -* \brief A range transforming the values of another range on-the-fly -* -* This behaves like a range providing `begin()` and `end()`. -* The iterators over this range internally iterate over -* the wrapped range. When dereferencing the iterator, -* the value is transformed on-the-fly using a given -* transformation function leaving the underlying range -* unchanged. -* -* The transformation may either return temporary values -* or l-value references. In the former case the range behaves -* like a proxy-container. In the latter case it forwards these -* references allowing, e.g., to sort a subset of some container -* by applying a transformation to an index-range for those values. -* -* The iterators of the TransformedRangeView have the same -* iterator_category as the ones of the wrapped container. -* -* If range is given as r-value, then the returned TransformedRangeView -* stores it by value, if range is given as (const) l-value, then the -* TransformedRangeView stores it by (const) reference. -* -* If R is a value type, then the TransformedRangeView stores the wrapped range by value, -* if R is a reference type, then the TransformedRangeView stores the wrapped range by reference. -* -* \tparam R Underlying range. -* \tparam F Unary function used to transform the values in the underlying range. -* \tparam T Class for describing how to apply the transformation -* -* T has to be either ValueTransformationTag (default) or IteratorTransformationTag. -* In the former case, the transformation is applied to the values -* obtained by dereferencing the wrapped iterator. In the latter case -* it is applied to the iterator directly, allowing to access non-standard -* functions of the iterator. -**/ -template <class R, class F, class T=ValueTransformationTag> -class TransformedRangeView -{ -using RawConstIterator = std::decay_t<decltype(std::declval<const R>().begin())>; -using RawIterator = std::decay_t<decltype(std::declval<R>().begin())>; - -public: - -/** -* \brief Const iterator type -* -* This inherits the iterator_category of the iterators -* of the underlying range. -*/ -using const_iterator = Impl::TransformedRangeIterator<RawConstIterator, F, T>; - -/** -* \brief Iterator type -* -* This inherits the iterator_category of the iterators -* of the underlying range. -*/ -using iterator = Impl::TransformedRangeIterator<RawIterator, F, T>; - -/** -* \brief Export type of the wrapped untransformed range. -* -* Notice that this will always be the raw type with references -* removed, even if a reference is stored. -*/ -using RawRange = std::remove_reference_t<R>; - -/** -* \brief Construct from range and function -*/ -template<class RR> -constexpr TransformedRangeView(RR&& rawRange, const F& f) noexcept : -rawRange_(std::forward<RR>(rawRange)), -f_(f) -{ -static_assert(std::is_same_v<T, ValueTransformationTag> or std::is_same_v<T, IteratorTransformationTag>, -"The TransformationType passed to TransformedRangeView has to be either ValueTransformationTag or IteratorTransformationTag."); -} - -/** -* \brief Obtain a iterator to the first element -* -* The life time of the returned iterator is bound to -* the life time of the range since it only contains a -* pointer to the transformation function stored -* in the range. -*/ -constexpr const_iterator begin() const noexcept { -return const_iterator(rawRange_.begin(), &f_); -} - -constexpr iterator begin() noexcept { -return iterator(rawRange_.begin(), &f_); -} - -/** -* \brief Obtain a iterator past the last element -* -* The life time of the returned iterator is bound to -* the life time of the range since it only contains a -* pointer to the transformation function stored -* in the range. -*/ -constexpr const_iterator end() const noexcept { -return const_iterator(rawRange_.end(), &f_); -} - -constexpr iterator end() noexcept { -return iterator(rawRange_.end(), &f_); -} - -/** -* \brief Obtain the size of the range -* -* This is only available if the underlying range -* provides a size() method. In this case size() -* just forwards to the underlying range's size() method. -* -* Attention: Don't select the template parameters explicitly. -* They are only used to implement SFINAE. -*/ -template<class Dummy=R, -class = void_t<decltype(std::declval<Dummy>().size())>> -auto size() const -{ -return rawRange_.size(); -} - -/** -* \brief Export the wrapped untransformed range. -*/ -const RawRange& rawRange() const -{ -return rawRange_; -} - -/** -* \brief Export the wrapped untransformed range. -*/ -RawRange& rawRange() -{ -return rawRange_; -} - -private: -R rawRange_; -F f_; -}; - -/** -* \brief Create a TransformedRangeView -* -* \param range The range to transform -* \param f Unary function that should the applied to the entries of the range. -* -* This behaves like a range providing `begin()` and `end()`. -* The iterators over this range internally iterate over -* the wrapped range. When dereferencing the iterator, -* the wrapped iterator is dereferenced, -* the given transformation function is applied on-the-fly, -* and the result is returned. -* I.e, if \code it \endcode is the wrapped iterator -* and \code f \endcode is the transformation function, -* then the result of \code f(*it) \endcode is returned -* -* The transformation may either return temporary values -* or l-value references. In the former case the range behaves -* like a proxy-container. In the latter case it forwards these -* references allowing, e.g., to sort a subset of some container -* by applying a transformation to an index-range for those values. -* -* The iterators of the TransformedRangeView have the same -* iterator_category as the ones of the wrapped container. -* -* If range is an r-value, then the TransformedRangeView stores it by value, -* if range is an l-value, then the TransformedRangeView stores it by reference. -**/ -template <class R, class F> -auto transformedRangeView(R&& range, const F& f) -{ -return TransformedRangeView<R, F, ValueTransformationTag>(std::forward<R>(range), f); -} - -/** -* \brief Create a TransformedRangeView using an iterator transformation -* -* \param range The range to transform -* \param f Unary function that should the applied to the entries of the range. -* -* This behaves like a range providing `begin()` and `end()`. -* The iterators over this range internally iterate over -* the wrapped range. When dereferencing the iterator, -* the given transformation function is applied to the wrapped -* iterator on-the-fly and the result is returned. -* I.e, if \code it \endcode is the wrapped iterator -* and \code f \endcode is the transformation function, -* then the result of \code f(it) \endcode is returned. -* -* The transformation may either return temorary values -* or l-value references. In the former case the range behaves -* like a proxy-container. In the latter case it forwards these -* references allowing, e.g., to sort a subset of some container -* by applying a transformation to an index-range for those values. -* -* The iterators of the TransformedRangeView have the same -* iterator_category as the ones of the wrapped container. -* -* If range is an r-value, then the TransformedRangeView stores it by value, -* if range is an l-value, then the TransformedRangeView stores it by reference. -**/ -template <class R, class F> -auto iteratorTransformedRangeView(R&& range, const F& f) -{ -return TransformedRangeView<R, F, IteratorTransformationTag>(std::forward<R>(range), f); -} - - -/** -* \brief Allow structured-binding for-loops for sparse iterators -* -* Given a sparse range `R` whose iterators `it` -* provide (additionally to dereferencing) a method -* `it->index()` for accessing the index of the current entry in the -* sparse range, this allows to write code like -* \code -* for(auto&& [A_i, i] : sparseRange(R)) -* doSomethingWithValueAndIndex(A_i, i); -* \endcode -*/ -template<class Range> -auto sparseRange(Range&& range) { -return Dune::iteratorTransformedRangeView(std::forward<Range>(range), [](auto&& it) { -return std::tuple<decltype(*it), decltype(it.index())>(*it, it.index()); -}); -} - -/** -* @} -*/ + /** + * @addtogroup RangeUtilities + * @{ + */ + + /** + \brief compute the maximum value over a range + + overloads for scalar values, and ranges exist + */ + template <typename T, + typename std::enable_if<IsIterable<T>::value, int>::type = 0> + typename T::value_type + max_value(const T & v) { + using std::max_element; + return *max_element(v.begin(), v.end()); + } + + template <typename T, + typename std::enable_if<!IsIterable<T>::value, int>::type = 0> + const T & max_value(const T & v) { return v; } + + /** + \brief compute the minimum value over a range + + overloads for scalar values, and ranges exist + */ + template <typename T, + typename std::enable_if<IsIterable<T>::value, int>::type = 0> + typename T::value_type + min_value(const T & v) { + using std::min_element; + return *min_element(v.begin(), v.end()); + } + + template <typename T, + typename std::enable_if<!IsIterable<T>::value, int>::type = 0> + const T & min_value(const T & v) { return v; } + + /** + \brief similar to std::bitset<N>::any() return true, if any entries is true + + overloads for scalar values, ranges, and std::bitset<N> exist + */ + template <typename T, + typename std::enable_if<IsIterable<T>::value, int>::type = 0> + bool any_true(const T & v) { + bool b = false; + for (const auto & e : v) + b = b or bool(e); + return b; + } + + template <typename T, + typename std::enable_if<!IsIterable<T>::value, int>::type = 0> + bool any_true(const T & v) { return v; } + + template<std::size_t N> + bool any_true(const std::bitset<N> & b) + { + return b.any(); + } + + /** + \brief similar to std::bitset<N>::all() return true, if any entries is true + + overloads for scalar values, ranges, and std::bitset<N> exist + */ + template <typename T, + typename std::enable_if<IsIterable<T>::value, int>::type = 0> + bool all_true(const T & v) { + bool b = true; + for (const auto & e : v) + b = b and bool(e); + return b; + } + + template <typename T, + typename std::enable_if<!IsIterable<T>::value, int>::type = 0> + bool all_true(const T & v) { return v; } + + template<std::size_t N> + bool all_true(const std::bitset<N> & b) + { + return b.all(); + } + + + + namespace Impl + { + + template <class T> + class IntegralRangeIterator + { + public: + typedef std::random_access_iterator_tag iterator_category; + typedef T value_type; + typedef std::make_signed_t<T> difference_type; + typedef const T *pointer; + typedef T reference; + + constexpr IntegralRangeIterator() noexcept : value_(0) {} + constexpr explicit IntegralRangeIterator(value_type value) noexcept : value_(value) {} + + pointer operator->() const noexcept { return &value_; } + constexpr reference operator*() const noexcept { return value_; } + + constexpr reference operator[]( difference_type n ) const noexcept { return (value_ + n); } + + constexpr bool operator==(const IntegralRangeIterator & other) const noexcept { return (value_ == other.value_); } + constexpr bool operator!=(const IntegralRangeIterator & other) const noexcept { return (value_ != other.value_); } + + constexpr bool operator<(const IntegralRangeIterator & other) const noexcept { return (value_ <= other.value_); } + constexpr bool operator<=(const IntegralRangeIterator & other) const noexcept { return (value_ <= other.value_); } + constexpr bool operator>(const IntegralRangeIterator & other) const noexcept { return (value_ >= other.value_); } + constexpr bool operator>=(const IntegralRangeIterator & other) const noexcept { return (value_ >= other.value_); } + + IntegralRangeIterator& operator++() noexcept { ++value_; return *this; } + IntegralRangeIterator operator++(int) noexcept { IntegralRangeIterator copy( *this ); ++(*this); return copy; } + + IntegralRangeIterator& operator--() noexcept { --value_; return *this; } + IntegralRangeIterator operator--(int) noexcept { IntegralRangeIterator copy( *this ); --(*this); return copy; } + + IntegralRangeIterator& operator+=(difference_type n) noexcept { value_ += n; return *this; } + IntegralRangeIterator& operator-=(difference_type n) noexcept { value_ -= n; return *this; } + + friend constexpr IntegralRangeIterator operator+(const IntegralRangeIterator &a, difference_type n) noexcept { return IntegralRangeIterator(a.value_ + n); } + friend constexpr IntegralRangeIterator operator+(difference_type n, const IntegralRangeIterator &a) noexcept { return IntegralRangeIterator(a.value_ + n); } + friend constexpr IntegralRangeIterator operator-(const IntegralRangeIterator &a, difference_type n) noexcept { return IntegralRangeIterator(a.value_ - n); } + + constexpr difference_type operator-(const IntegralRangeIterator &other) const noexcept { return (static_cast<difference_type>(value_) - static_cast<difference_type>(other.value_)); } + + private: + value_type value_; + }; + + } // namespace Impl + + + + /** + * \brief dynamic integer range for use in range-based for loops + * + * \note This range can also be used in Hybrid::forEach, resulting in a dynamic + * for loop over the contained integers. + * + * \tparam T type of integers contained in the range + **/ + template <class T> + class IntegralRange + { + public: + /** \brief type of integers contained in the range **/ + typedef T value_type; + /** \brief type of iterator **/ + typedef Impl::IntegralRangeIterator<T> iterator; + /** \brief unsigned integer type corresponding to value_type **/ + typedef std::make_unsigned_t<T> size_type; + + /** \brief construct integer range [from, to) **/ + constexpr IntegralRange(value_type from, value_type to) noexcept : from_(from), to_(to) {} + /** \brief construct integer range [0, to) **/ + constexpr explicit IntegralRange(value_type to) noexcept : from_(0), to_(to) {} + /** \brief construct integer range std::pair **/ + constexpr IntegralRange(std::pair<value_type, value_type> range) noexcept : from_(range.first), to_(range.second) {} + + /** \brief obtain a random-access iterator to the first element **/ + constexpr iterator begin() const noexcept { return iterator(from_); } + /** \brief obtain a random-access iterator past the last element **/ + constexpr iterator end() const noexcept { return iterator(to_); } + + /** \brief access specified element **/ + constexpr value_type operator[](const value_type &i) const noexcept { return (from_ + i); } + + /** \brief check whether the range is empty **/ + constexpr bool empty() const noexcept { return (from_ == to_); } + /** \brief obtain number of elements in the range **/ + constexpr size_type size() const noexcept { return (static_cast<size_type>(to_) - static_cast<size_type>(from_)); } + + private: + value_type from_, to_; + }; + + + /** + * \brief static integer range for use in range-based for loops + * + * This is a compile-time static variant of the IntegralRange. Apart from + * returning all range information statically, it casts into the corresponding + * std::integer_sequence. + * + * \note This range can also be used in Hybrid::forEach, resulting in a static + * for loop over the contained integers like a std::integer_sequence. + * + * \tparam T type of integers contained in the range + * \tparam to first element not contained in the range + * \tparam from first element contained in the range, defaults to 0 + **/ + template <class T, T to, T from = 0> + class StaticIntegralRange + { + template <T ofs, T... i> + static std::integer_sequence<T, (i+ofs)...> shift_integer_sequence(std::integer_sequence<T, i...>); + + public: + /** \brief type of integers contained in the range **/ + typedef T value_type; + /** \brief type of iterator **/ + typedef Impl::IntegralRangeIterator<T> iterator; + /** \brief unsigned integer type corresponding to value_type **/ + typedef std::make_unsigned_t<T> size_type; + + /** \brief type of corresponding std::integer_sequence **/ + typedef decltype(shift_integer_sequence<from>(std::make_integer_sequence<T, to-from>())) integer_sequence; + + /** \brief default constructor **/ + constexpr StaticIntegralRange() noexcept = default; + + /** \brief cast into dynamic IntegralRange **/ + constexpr operator IntegralRange<T>() const noexcept { return {from, to}; } + /** \brief cast into corresponding std::integer_sequence **/ + constexpr operator integer_sequence() const noexcept { return {}; } + + /** \brief obtain a random-access iterator to the first element **/ + static constexpr iterator begin() noexcept { return iterator(from); } + /** \brief obtain a random-access iterator past the last element **/ + static constexpr iterator end() noexcept { return iterator(to); } + + /** \brief access specified element (static version) **/ + template <class U, U i> + constexpr auto operator[](const std::integral_constant<U, i> &) const noexcept + -> std::integral_constant<value_type, from + static_cast<value_type>(i)> + { + return {}; + } + + /** \brief access specified element (dynamic version) **/ + constexpr value_type operator[](const size_type &i) const noexcept { return (from + static_cast<value_type>(i)); } + + /** \brief check whether the range is empty **/ + static constexpr std::integral_constant<bool, from == to> empty() noexcept { return {}; } + /** \brief obtain number of elements in the range **/ + static constexpr std::integral_constant<size_type, static_cast<size_type>(to) - static_cast<size_type>(from) > size() noexcept { return {}; } + }; + + /** + \brief free standing function for setting up a range based for loop + over an integer range + for (auto i: range(0,10)) // 0,1,2,3,4,5,6,7,8,9 + or + for (auto i: range(-10,10)) // -10,-9,..,8,9 + or + for (auto i: range(10)) // 0,1,2,3,4,5,6,7,8,9 + */ + template<class T, class U, + std::enable_if_t<std::is_same<std::decay_t<T>, std::decay_t<U>>::value, int> = 0, + std::enable_if_t<std::is_integral<std::decay_t<T>>::value, int> = 0> + inline static IntegralRange<std::decay_t<T>> range(T &&from, U &&to) noexcept + { + return IntegralRange<std::decay_t<T>>(std::forward<T>(from), std::forward<U>(to)); + } + + template<class T, std::enable_if_t<std::is_integral<std::decay_t<T>>::value, int> = 0> + inline static IntegralRange<std::decay_t<T>> range(T &&to) noexcept + { + return IntegralRange<std::decay_t<T>>(std::forward<T>(to)); + } + + template<class T, std::enable_if_t<std::is_enum<std::decay_t<T>>::value, int> = 0> + inline static IntegralRange<std::underlying_type_t<std::decay_t<T>>> range(T &&to) noexcept + { + return IntegralRange<std::underlying_type_t<std::decay_t<T>>>(std::forward<T>(to)); + } + + template<class T, T from, T to> + inline static StaticIntegralRange<T, to, from> range(std::integral_constant<T, from>, std::integral_constant<T, to>) noexcept + { + return {}; + } + + template<class T, T to> + inline static StaticIntegralRange<T, to> range(std::integral_constant<T, to>) noexcept + { + return {}; + } + + + + /** + * \brief Tag to enable value based transformations in TransformedRangeView + */ + struct ValueTransformationTag {}; + + /** + * \brief Tag to enable iterator based transformations in TransformedRangeView + */ + struct IteratorTransformationTag {}; + + namespace Impl + { + + // Helper class to mimic a pointer for proxy objects. + // This is needed to implement operator-> on an iterator + // using proxy-values. It stores the proxy value but + // provides operator-> like a pointer. + template<class ProxyType> + class PointerProxy + { + public: + PointerProxy(ProxyType&& p) : p_(p) + {} + + ProxyType* operator->() + { + return &p_; + } + + ProxyType p_; + }; + + // An iterator transforming a wrapped iterator using + // an unary function. It inherits the iterator-category + // of the underlying iterator. + template <class I, class F, class TransformationType, class C = typename std::iterator_traits<I>::iterator_category> + class TransformedRangeIterator; + + template <class I, class F, class TransformationType> + class TransformedRangeIterator<I,F,TransformationType,std::forward_iterator_tag> + { + protected: + + static decltype(auto) transform(const F& f, const I& it) { + if constexpr (std::is_same_v<TransformationType,IteratorTransformationTag>) + return f(it); + else + return f(*it); + } + + public: + using iterator_category = std::forward_iterator_tag; + using reference = decltype(transform(std::declval<F>(), std::declval<I>())); + using value_type = std::decay_t<reference>; + using pointer = PointerProxy<value_type>; + + // If we later want to allow standalone TransformedRangeIterators, + // we could customize the FunctionPointer to be a default-constructible, + // copy-assignable type storing a function but acting like a pointer + // to function. + using FunctionPointer = const F*; + + constexpr TransformedRangeIterator(const I& it, FunctionPointer f) noexcept : + it_(it), + f_(f) + {} + + // Explicitly initialize members. Using a plain + // + // constexpr TransformedRangeIterator() noexcept {} + // + // would default-initialize the members while + // + // constexpr TransformedRangeIterator() noexcept : it_(), f_() {} + // + // leads to value-initialization. This is a case where + // both are really different. If it_ is a raw pointer (i.e. POD) + // then default-initialization leaves it uninitialized while + // value-initialization zero-initializes it. + constexpr TransformedRangeIterator() noexcept : + it_(), + f_() + {} + + // Dereferencing returns a value created by the function + constexpr reference operator*() const noexcept { + return transform(*f_, it_); + } + + // Dereferencing returns a value created by the function + pointer operator->() const noexcept { + return transform(*f_, it_); + } + + constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; + + constexpr bool operator==(const TransformedRangeIterator& other) const noexcept { + return (it_ == other.it_); + } + + constexpr bool operator!=(const TransformedRangeIterator& other) const noexcept { + return (it_ != other.it_); + } + + TransformedRangeIterator& operator++() noexcept { + ++it_; + return *this; + } + + TransformedRangeIterator operator++(int) noexcept { + TransformedRangeIterator copy(*this); + ++(*this); + return copy; + } + + protected: + I it_; + FunctionPointer f_; + }; + + + + template <class I, class F, class T> + class TransformedRangeIterator<I,F,T,std::bidirectional_iterator_tag> : + public TransformedRangeIterator<I,F,T,std::forward_iterator_tag> + { + protected: + using Base = TransformedRangeIterator<I,F,T,std::forward_iterator_tag>; + using Base::it_; + using Base::f_; + public: + using iterator_category = std::bidirectional_iterator_tag; + using reference = typename Base::reference; + using value_type = typename Base::value_type; + using pointer = typename Base::pointer; + + using FunctionPointer = typename Base::FunctionPointer; + + using Base::Base; + + // Member functions of the forward_iterator that need + // to be redefined because the base class methods return a + // forward_iterator. + constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; + + TransformedRangeIterator& operator++() noexcept { + ++it_; + return *this; + } + + TransformedRangeIterator operator++(int) noexcept { + TransformedRangeIterator copy(*this); + ++(*this); + return copy; + } + + // Additional member functions of bidirectional_iterator + TransformedRangeIterator& operator--() noexcept { + --(this->it_); + return *this; + } + + TransformedRangeIterator operator--(int) noexcept { + TransformedRangeIterator copy(*this); + --(*this); + return copy; + } + }; + + + + template <class I, class F, class T> + class TransformedRangeIterator<I,F,T,std::random_access_iterator_tag> : + public TransformedRangeIterator<I,F,T,std::bidirectional_iterator_tag> + { + protected: + using Base = TransformedRangeIterator<I,F,T,std::bidirectional_iterator_tag>; + using Base::it_; + using Base::f_; + public: + using iterator_category = std::random_access_iterator_tag; + using reference = typename Base::reference; + using value_type = typename Base::value_type; + using pointer = typename Base::pointer; + using difference_type = typename std::iterator_traits<I>::difference_type; + + using FunctionPointer = typename Base::FunctionPointer; + + using Base::Base; + + // Member functions of the forward_iterator that need + // to be redefined because the base class methods return a + // forward_iterator. + constexpr TransformedRangeIterator& operator=(const TransformedRangeIterator& other) = default; + + TransformedRangeIterator& operator++() noexcept { + ++it_; + return *this; + } + + TransformedRangeIterator operator++(int) noexcept { + TransformedRangeIterator copy(*this); + ++(*this); + return copy; + } + + // Member functions of the bidirectional_iterator that need + // to be redefined because the base class methods return a + // bidirectional_iterator. + TransformedRangeIterator& operator--() noexcept { + --(this->it_); + return *this; + } + + TransformedRangeIterator operator--(int) noexcept { + TransformedRangeIterator copy(*this); + --(*this); + return copy; + } + + // Additional member functions of random_access_iterator + TransformedRangeIterator& operator+=(difference_type n) noexcept { + it_ += n; + return *this; + } + + TransformedRangeIterator& operator-=(difference_type n) noexcept { + it_ -= n; + return *this; + } + + bool operator<(const TransformedRangeIterator& other) noexcept { + return it_<other.it_; + } + + bool operator<=(const TransformedRangeIterator& other) noexcept { + return it_<=other.it_; + } + + bool operator>(const TransformedRangeIterator& other) noexcept { + return it_>other.it_; + } + + bool operator>=(const TransformedRangeIterator& other) noexcept { + return it_>=other.it_; + } + + reference operator[](difference_type n) noexcept { + return Base::transform(*f_, it_+n); + } + + friend + TransformedRangeIterator operator+(const TransformedRangeIterator& it, difference_type n) noexcept { + return TransformedRangeIterator(it.it_+n, it.f_); + } + + friend + TransformedRangeIterator operator+(difference_type n, const TransformedRangeIterator& it) noexcept { + return TransformedRangeIterator(n+it.it_, it.f_); + } + + friend + TransformedRangeIterator operator-(const TransformedRangeIterator& it, difference_type n) noexcept { + return TransformedRangeIterator(it.it_-n, it.f_); + } + + friend + difference_type operator-(const TransformedRangeIterator& first, const TransformedRangeIterator& second) noexcept { + return first.it_-second.it_; + } + }; + + + } // namespace Impl + + + + /** + * \brief A range transforming the values of another range on-the-fly + * + * This behaves like a range providing `begin()` and `end()`. + * The iterators over this range internally iterate over + * the wrapped range. When dereferencing the iterator, + * the value is transformed on-the-fly using a given + * transformation function leaving the underlying range + * unchanged. + * + * The transformation may either return temporary values + * or l-value references. In the former case the range behaves + * like a proxy-container. In the latter case it forwards these + * references allowing, e.g., to sort a subset of some container + * by applying a transformation to an index-range for those values. + * + * The iterators of the TransformedRangeView have the same + * iterator_category as the ones of the wrapped container. + * + * If range is given as r-value, then the returned TransformedRangeView + * stores it by value, if range is given as (const) l-value, then the + * TransformedRangeView stores it by (const) reference. + * + * If R is a value type, then the TransformedRangeView stores the wrapped range by value, + * if R is a reference type, then the TransformedRangeView stores the wrapped range by reference. + * + * \tparam R Underlying range. + * \tparam F Unary function used to transform the values in the underlying range. + * \tparam T Class for describing how to apply the transformation + * + * T has to be either ValueTransformationTag (default) or IteratorTransformationTag. + * In the former case, the transformation is applied to the values + * obtained by dereferencing the wrapped iterator. In the latter case + * it is applied to the iterator directly, allowing to access non-standard + * functions of the iterator. + **/ + template <class R, class F, class T=ValueTransformationTag> + class TransformedRangeView + { + using RawConstIterator = std::decay_t<decltype(std::declval<const R>().begin())>; + using RawIterator = std::decay_t<decltype(std::declval<R>().begin())>; + + public: + + /** + * \brief Const iterator type + * + * This inherits the iterator_category of the iterators + * of the underlying range. + */ + using const_iterator = Impl::TransformedRangeIterator<RawConstIterator, F, T>; + + /** + * \brief Iterator type + * + * This inherits the iterator_category of the iterators + * of the underlying range. + */ + using iterator = Impl::TransformedRangeIterator<RawIterator, F, T>; + + /** + * \brief Export type of the wrapped untransformed range. + * + * Notice that this will always be the raw type with references + * removed, even if a reference is stored. + */ + using RawRange = std::remove_reference_t<R>; + + /** + * \brief Construct from range and function + */ + template<class RR> + constexpr TransformedRangeView(RR&& rawRange, const F& f) noexcept : + rawRange_(std::forward<RR>(rawRange)), + f_(f) + { + static_assert(std::is_same_v<T, ValueTransformationTag> or std::is_same_v<T, IteratorTransformationTag>, + "The TransformationType passed to TransformedRangeView has to be either ValueTransformationTag or IteratorTransformationTag."); + } + + /** + * \brief Obtain a iterator to the first element + * + * The life time of the returned iterator is bound to + * the life time of the range since it only contains a + * pointer to the transformation function stored + * in the range. + */ + constexpr const_iterator begin() const noexcept { + return const_iterator(rawRange_.begin(), &f_); + } + + constexpr iterator begin() noexcept { + return iterator(rawRange_.begin(), &f_); + } + + /** + * \brief Obtain a iterator past the last element + * + * The life time of the returned iterator is bound to + * the life time of the range since it only contains a + * pointer to the transformation function stored + * in the range. + */ + constexpr const_iterator end() const noexcept { + return const_iterator(rawRange_.end(), &f_); + } + + constexpr iterator end() noexcept { + return iterator(rawRange_.end(), &f_); + } + + /** + * \brief Obtain the size of the range + * + * This is only available if the underlying range + * provides a size() method. In this case size() + * just forwards to the underlying range's size() method. + * + * Attention: Don't select the template parameters explicitly. + * They are only used to implement SFINAE. + */ + template<class Dummy=R, + class = void_t<decltype(std::declval<Dummy>().size())>> + auto size() const + { + return rawRange_.size(); + } + + /** + * \brief Export the wrapped untransformed range. + */ + const RawRange& rawRange() const + { + return rawRange_; + } + + /** + * \brief Export the wrapped untransformed range. + */ + RawRange& rawRange() + { + return rawRange_; + } + + private: + R rawRange_; + F f_; + }; + + /** + * \brief Create a TransformedRangeView + * + * \param range The range to transform + * \param f Unary function that should the applied to the entries of the range. + * + * This behaves like a range providing `begin()` and `end()`. + * The iterators over this range internally iterate over + * the wrapped range. When dereferencing the iterator, + * the wrapped iterator is dereferenced, + * the given transformation function is applied on-the-fly, + * and the result is returned. + * I.e, if \code it \endcode is the wrapped iterator + * and \code f \endcode is the transformation function, + * then the result of \code f(*it) \endcode is returned + * + * The transformation may either return temporary values + * or l-value references. In the former case the range behaves + * like a proxy-container. In the latter case it forwards these + * references allowing, e.g., to sort a subset of some container + * by applying a transformation to an index-range for those values. + * + * The iterators of the TransformedRangeView have the same + * iterator_category as the ones of the wrapped container. + * + * If range is an r-value, then the TransformedRangeView stores it by value, + * if range is an l-value, then the TransformedRangeView stores it by reference. + **/ + template <class R, class F> + auto transformedRangeView(R&& range, const F& f) + { + return TransformedRangeView<R, F, ValueTransformationTag>(std::forward<R>(range), f); + } + + /** + * \brief Create a TransformedRangeView using an iterator transformation + * + * \param range The range to transform + * \param f Unary function that should the applied to the entries of the range. + * + * This behaves like a range providing `begin()` and `end()`. + * The iterators over this range internally iterate over + * the wrapped range. When dereferencing the iterator, + * the given transformation function is applied to the wrapped + * iterator on-the-fly and the result is returned. + * I.e, if \code it \endcode is the wrapped iterator + * and \code f \endcode is the transformation function, + * then the result of \code f(it) \endcode is returned. + * + * The transformation may either return temorary values + * or l-value references. In the former case the range behaves + * like a proxy-container. In the latter case it forwards these + * references allowing, e.g., to sort a subset of some container + * by applying a transformation to an index-range for those values. + * + * The iterators of the TransformedRangeView have the same + * iterator_category as the ones of the wrapped container. + * + * If range is an r-value, then the TransformedRangeView stores it by value, + * if range is an l-value, then the TransformedRangeView stores it by reference. + **/ + template <class R, class F> + auto iteratorTransformedRangeView(R&& range, const F& f) + { + return TransformedRangeView<R, F, IteratorTransformationTag>(std::forward<R>(range), f); + } + + + /** + * \brief Allow structured-binding for-loops for sparse iterators + * + * Given a sparse range `R` whose iterators `it` + * provide (additionally to dereferencing) a method + * `it->index()` for accessing the index of the current entry in the + * sparse range, this allows to write code like + * \code + * for(auto&& [A_i, i] : sparseRange(R)) + * doSomethingWithValueAndIndex(A_i, i); + * \endcode + */ + template<class Range> + auto sparseRange(Range&& range) { + return Dune::iteratorTransformedRangeView(std::forward<Range>(range), [](auto&& it) { + return std::tuple<decltype(*it), decltype(it.index())>(*it, it.index()); + }); + } + + /** + * @} + */ } diff --git a/dune/common/reservedvector.hh b/dune/common/reservedvector.hh index b9d53a76339ef1e585a3b7b17359d36f2e49e05e..969fc747148f1b6627ddf99521a76e886fd778fe 100644 --- a/dune/common/reservedvector.hh +++ b/dune/common/reservedvector.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief An stl-compliant random-access container which stores everything on the stack -*/ + * \brief An stl-compliant random-access container which stores everything on the stack + */ #include <algorithm> #include <iostream> @@ -24,206 +24,206 @@ namespace Dune { -/** -\brief A Vector class with statically reserved memory. - -ReservedVector is something between std::array and std::vector. -It is a dynamically sized vector which can be extended and shrunk -using methods like push_back and pop_back, but reserved memory is -statically predefined. - -This implies that the vector cannot grow bigger than the predefined -maximum size. - -\tparam T The data type ReservedVector stores. -\tparam n The maximum number of objects the ReservedVector can store. - -*/ -template<class T, int n> -class ReservedVector -{ -public: - -/** @{ Typedefs */ - -//! The type of object, T, stored in the vector. -typedef T value_type; -//! Pointer to T. -typedef T* pointer; -//! Reference to T -typedef T& reference; -//! Const reference to T -typedef const T& const_reference; -//! An unsigned integral type. -typedef size_t size_type; -//! A signed integral type. -typedef std::ptrdiff_t difference_type; -//! Iterator used to iterate through a vector. -typedef Dune::GenericIterator<ReservedVector, value_type> iterator; -//! Const iterator used to iterate through a vector. -typedef Dune::GenericIterator<const ReservedVector, const value_type> const_iterator; - -/** @} */ - -/** @{ Constructors */ - -//! Constructor -ReservedVector() = default; - -ReservedVector(std::initializer_list<T> const &l) -{ -assert(l.size() <= n);// Actually, this is not needed any more! -sz = l.size(); -std::copy_n(l.begin(), sz, data); -} - -/** @} */ - -bool operator == (const ReservedVector & other) const -{ -bool eq = (sz == other.sz); -for (size_type i=0; i<sz && eq; ++i) -eq = eq && (data[i] == other.data[i]); -return eq; -} - -/** @{ Data access operations */ - -//! Erases all elements. -void clear() -{ -sz = 0; -} - -//! Specifies a new size for the vector. -void resize(size_t s) -{ -CHECKSIZE(s<=n); -sz = s; -} - -//! Appends an element to the end of a vector, up to the maximum size n, O(1) time. -void push_back(const T& t) -{ -CHECKSIZE(sz<n); -data[sz++] = t; -} - -//! Erases the last element of the vector, O(1) time. -void pop_back() -{ -if (! empty()) sz--; -} - -//! Returns a iterator pointing to the beginning of the vector. -iterator begin(){ -return iterator(*this, 0); -} - -//! Returns a const_iterator pointing to the beginning of the vector. -const_iterator begin() const { -return const_iterator(*this, 0); -} - -//! Returns an iterator pointing to the end of the vector. -iterator end(){ -return iterator(*this, sz); -} - -//! Returns a const_iterator pointing to the end of the vector. -const_iterator end() const { -return const_iterator(*this, sz); -} - -//! Returns reference to the i'th element. -reference operator[] (size_type i) -{ -CHECKSIZE(sz>i); -return data[i]; -} - -//! Returns a const reference to the i'th element. -const_reference operator[] (size_type i) const -{ -CHECKSIZE(sz>i); -return data[i]; -} - -//! Returns reference to first element of vector. -reference front() -{ -CHECKSIZE(sz>0); -return data[0]; -} - -//! Returns const reference to first element of vector. -const_reference front() const -{ -CHECKSIZE(sz>0); -return data[0]; -} - -//! Returns reference to last element of vector. -reference back() -{ -CHECKSIZE(sz>0); -return data[sz-1]; -} - -//! Returns const reference to last element of vector. -const_reference back() const -{ -CHECKSIZE(sz>0); -return data[sz-1]; -} - -/** @} */ - -/** @{ Informative Methods */ - -//! Returns number of elements in the vector. -size_type size () const -{ -return sz; -} - -//! Returns true if vector has no elements. -bool empty() const -{ -return sz==0; -} - -//! Returns current capacity (allocated memory) of the vector. -static constexpr size_type capacity() -{ -return n; -} - -//! Returns the maximum length of the vector. -static constexpr size_type max_size() -{ -return n; -} - -/** @} */ - -//! Send ReservedVector to an output stream -friend std::ostream& operator<< (std::ostream& s, const ReservedVector& v) -{ -for (size_t i=0; i<v.size(); i++) -s << v[i] << " "; -return s; -} - -inline friend std::size_t hash_value(const ReservedVector& v) noexcept -{ -return hash_range(v.data,v.data+v.sz); -} - -private: -T data[n] = {}; -size_type sz = 0; -}; + /** + \brief A Vector class with statically reserved memory. + + ReservedVector is something between std::array and std::vector. + It is a dynamically sized vector which can be extended and shrunk + using methods like push_back and pop_back, but reserved memory is + statically predefined. + + This implies that the vector cannot grow bigger than the predefined + maximum size. + + \tparam T The data type ReservedVector stores. + \tparam n The maximum number of objects the ReservedVector can store. + + */ + template<class T, int n> + class ReservedVector + { + public: + + /** @{ Typedefs */ + + //! The type of object, T, stored in the vector. + typedef T value_type; + //! Pointer to T. + typedef T* pointer; + //! Reference to T + typedef T& reference; + //! Const reference to T + typedef const T& const_reference; + //! An unsigned integral type. + typedef size_t size_type; + //! A signed integral type. + typedef std::ptrdiff_t difference_type; + //! Iterator used to iterate through a vector. + typedef Dune::GenericIterator<ReservedVector, value_type> iterator; + //! Const iterator used to iterate through a vector. + typedef Dune::GenericIterator<const ReservedVector, const value_type> const_iterator; + + /** @} */ + + /** @{ Constructors */ + + //! Constructor + ReservedVector() = default; + + ReservedVector(std::initializer_list<T> const &l) + { + assert(l.size() <= n);// Actually, this is not needed any more! + sz = l.size(); + std::copy_n(l.begin(), sz, data); + } + + /** @} */ + + bool operator == (const ReservedVector & other) const + { + bool eq = (sz == other.sz); + for (size_type i=0; i<sz && eq; ++i) + eq = eq && (data[i] == other.data[i]); + return eq; + } + + /** @{ Data access operations */ + + //! Erases all elements. + void clear() + { + sz = 0; + } + + //! Specifies a new size for the vector. + void resize(size_t s) + { + CHECKSIZE(s<=n); + sz = s; + } + + //! Appends an element to the end of a vector, up to the maximum size n, O(1) time. + void push_back(const T& t) + { + CHECKSIZE(sz<n); + data[sz++] = t; + } + + //! Erases the last element of the vector, O(1) time. + void pop_back() + { + if (! empty()) sz--; + } + + //! Returns a iterator pointing to the beginning of the vector. + iterator begin(){ + return iterator(*this, 0); + } + + //! Returns a const_iterator pointing to the beginning of the vector. + const_iterator begin() const { + return const_iterator(*this, 0); + } + + //! Returns an iterator pointing to the end of the vector. + iterator end(){ + return iterator(*this, sz); + } + + //! Returns a const_iterator pointing to the end of the vector. + const_iterator end() const { + return const_iterator(*this, sz); + } + + //! Returns reference to the i'th element. + reference operator[] (size_type i) + { + CHECKSIZE(sz>i); + return data[i]; + } + + //! Returns a const reference to the i'th element. + const_reference operator[] (size_type i) const + { + CHECKSIZE(sz>i); + return data[i]; + } + + //! Returns reference to first element of vector. + reference front() + { + CHECKSIZE(sz>0); + return data[0]; + } + + //! Returns const reference to first element of vector. + const_reference front() const + { + CHECKSIZE(sz>0); + return data[0]; + } + + //! Returns reference to last element of vector. + reference back() + { + CHECKSIZE(sz>0); + return data[sz-1]; + } + + //! Returns const reference to last element of vector. + const_reference back() const + { + CHECKSIZE(sz>0); + return data[sz-1]; + } + + /** @} */ + + /** @{ Informative Methods */ + + //! Returns number of elements in the vector. + size_type size () const + { + return sz; + } + + //! Returns true if vector has no elements. + bool empty() const + { + return sz==0; + } + + //! Returns current capacity (allocated memory) of the vector. + static constexpr size_type capacity() + { + return n; + } + + //! Returns the maximum length of the vector. + static constexpr size_type max_size() + { + return n; + } + + /** @} */ + + //! Send ReservedVector to an output stream + friend std::ostream& operator<< (std::ostream& s, const ReservedVector& v) + { + for (size_t i=0; i<v.size(); i++) + s << v[i] << " "; + return s; + } + + inline friend std::size_t hash_value(const ReservedVector& v) noexcept + { + return hash_range(v.data,v.data+v.sz); + } + + private: + T data[n] = {}; + size_type sz = 0; + }; } diff --git a/dune/common/scalarmatrixview.hh b/dune/common/scalarmatrixview.hh index c514660a6965e4192bbf1773a48bcde81dd52ddc..3ecda7f8b015fdde808e0198c21aa4d5f7ef923f 100644 --- a/dune/common/scalarmatrixview.hh +++ b/dune/common/scalarmatrixview.hh @@ -20,186 +20,186 @@ namespace Dune { namespace Impl { -/** -@addtogroup DenseMatVec -@{ -*/ - -/*! \file -* \brief Implements a scalar matrix view wrapper around an existing scalar. -*/ - -/** \brief A wrapper making a scalar look like a matrix -* -* This stores a pointer to a scalar of type K and -* provides the interface of a matrix with a single row -* and column represented by the data behind the pointer. -*/ -template<class K> -class ScalarMatrixView : -public DenseMatrix<ScalarMatrixView<K>> -{ -ScalarVectorView<K> data_; -using Base = DenseMatrix<ScalarMatrixView<K>>; - -template <class> -friend class ScalarMatrixView; -public: - -//===== type definitions and constants - -//! We are at the leaf of the block recursion -enum { -//! The number of block levels we contain. -//! This is always one for this type. -blocklevel = 1 -}; - -using size_type = typename Base::size_type; -using row_type = typename Base::row_type; -using row_reference = typename Base::row_reference; -using const_row_reference = typename Base::const_row_reference; - -//! export size -enum { -//! \brief The number of rows. -//! This is always one for this type. -rows = 1, -//! \brief The number of columns. -//! This is always one for this type. -cols = 1 -}; - -//===== constructors -/** \brief Default constructor -*/ -constexpr ScalarMatrixView () -: data_() -{} - -/** \brief Construct from a pointer to a scalar */ -ScalarMatrixView (K* p) : -data_(p) -{} - -//! Copy constructor -ScalarMatrixView (const ScalarMatrixView &other) : -Base(), -data_(other.data_) -{} - -//! Move constructor -ScalarMatrixView (ScalarMatrixView &&other) : -Base(), -data_( other.data_ ) -{} - -//! Copy assignment operator -ScalarMatrixView& operator= (const ScalarMatrixView& other) -{ -data_ = other.data_; -return *this; -} - -template<class KK> -ScalarMatrixView& operator= (const ScalarMatrixView<KK>& other) -{ -data_ = other.data_; -return *this; -} - -//! Assignment operator from a scalar -template<typename T, -std::enable_if_t<std::is_convertible<T, K>::value, int> = 0> -inline ScalarMatrixView& operator= (const T& k) -{ -data_ = k; -return *this; -} - -// make this thing a matrix -static constexpr size_type mat_rows() { return 1; } -static constexpr size_type mat_cols() { return 1; } - -row_reference mat_access ( size_type i ) -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return data_; -} - -const_row_reference mat_access ( size_type i ) const -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return data_; -} -}; // class ScalarMatrixView - -/** \brief Sends the matrix to an output stream */ -template<typename K> -std::ostream& operator<< (std::ostream& s, const ScalarMatrixView<K>& a) -{ -s << a[0][0]; -return s; -} - -/** \brief Wrap a scalar as a 1-1-matrix */ -template<class T, -std::enable_if_t<IsNumber<T>::value, int> = 0> -auto asMatrix(T& t) -{ -return ScalarMatrixView<T>{&t}; -} - -/** \brief Wrap a const scalar as a const 1-1-matrix */ -template<class T, -std::enable_if_t<IsNumber<T>::value, int> = 0> -auto asMatrix(const T& t) -{ -return ScalarMatrixView<const T>{&t}; -} - -/** \brief Non-scalar types are assumed to be matrices, and simply forwarded */ -template<class T, -std::enable_if_t<not IsNumber<T>::value, int> = 0> -T& asMatrix(T& t) -{ -return t; -} - -/** \brief Non-scalar types are assumed to be matrices, and simply forwarded */ -template<class T, -std::enable_if_t<not IsNumber<T>::value, int> = 0> -const T& asMatrix(const T& t) -{ -return t; -} - -/** @} end documentation */ + /** + @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief Implements a scalar matrix view wrapper around an existing scalar. + */ + + /** \brief A wrapper making a scalar look like a matrix + * + * This stores a pointer to a scalar of type K and + * provides the interface of a matrix with a single row + * and column represented by the data behind the pointer. + */ + template<class K> + class ScalarMatrixView : + public DenseMatrix<ScalarMatrixView<K>> + { + ScalarVectorView<K> data_; + using Base = DenseMatrix<ScalarMatrixView<K>>; + + template <class> + friend class ScalarMatrixView; + public: + + //===== type definitions and constants + + //! We are at the leaf of the block recursion + enum { + //! The number of block levels we contain. + //! This is always one for this type. + blocklevel = 1 + }; + + using size_type = typename Base::size_type; + using row_type = typename Base::row_type; + using row_reference = typename Base::row_reference; + using const_row_reference = typename Base::const_row_reference; + + //! export size + enum { + //! \brief The number of rows. + //! This is always one for this type. + rows = 1, + //! \brief The number of columns. + //! This is always one for this type. + cols = 1 + }; + + //===== constructors + /** \brief Default constructor + */ + constexpr ScalarMatrixView () + : data_() + {} + + /** \brief Construct from a pointer to a scalar */ + ScalarMatrixView (K* p) : + data_(p) + {} + + //! Copy constructor + ScalarMatrixView (const ScalarMatrixView &other) : + Base(), + data_(other.data_) + {} + + //! Move constructor + ScalarMatrixView (ScalarMatrixView &&other) : + Base(), + data_( other.data_ ) + {} + + //! Copy assignment operator + ScalarMatrixView& operator= (const ScalarMatrixView& other) + { + data_ = other.data_; + return *this; + } + + template<class KK> + ScalarMatrixView& operator= (const ScalarMatrixView<KK>& other) + { + data_ = other.data_; + return *this; + } + + //! Assignment operator from a scalar + template<typename T, + std::enable_if_t<std::is_convertible<T, K>::value, int> = 0> + inline ScalarMatrixView& operator= (const T& k) + { + data_ = k; + return *this; + } + + // make this thing a matrix + static constexpr size_type mat_rows() { return 1; } + static constexpr size_type mat_cols() { return 1; } + + row_reference mat_access ( size_type i ) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return data_; + } + + const_row_reference mat_access ( size_type i ) const + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return data_; + } + }; // class ScalarMatrixView + + /** \brief Sends the matrix to an output stream */ + template<typename K> + std::ostream& operator<< (std::ostream& s, const ScalarMatrixView<K>& a) + { + s << a[0][0]; + return s; + } + + /** \brief Wrap a scalar as a 1-1-matrix */ + template<class T, + std::enable_if_t<IsNumber<T>::value, int> = 0> + auto asMatrix(T& t) + { + return ScalarMatrixView<T>{&t}; + } + + /** \brief Wrap a const scalar as a const 1-1-matrix */ + template<class T, + std::enable_if_t<IsNumber<T>::value, int> = 0> + auto asMatrix(const T& t) + { + return ScalarMatrixView<const T>{&t}; + } + + /** \brief Non-scalar types are assumed to be matrices, and simply forwarded */ + template<class T, + std::enable_if_t<not IsNumber<T>::value, int> = 0> + T& asMatrix(T& t) + { + return t; + } + + /** \brief Non-scalar types are assumed to be matrices, and simply forwarded */ + template<class T, + std::enable_if_t<not IsNumber<T>::value, int> = 0> + const T& asMatrix(const T& t) + { + return t; + } + + /** @} end documentation */ } // end namespace Impl -template<class K> -struct FieldTraits<Impl::ScalarMatrixView<K>> : public FieldTraits<std::remove_const_t<K>> {}; - -template<class K> -struct DenseMatVecTraits<Impl::ScalarMatrixView<K>> -{ -using derived_type = Impl::ScalarMatrixView<K>; -using row_type = Impl::ScalarVectorView<K>; -using row_reference = row_type&; -using const_row_reference = const row_type&; -using value_type = std::remove_const_t<K>; -using size_type = std::size_t; -}; - - -template<class K> -struct AutonomousValueType<Impl::ScalarMatrixView<K>> -{ -using type = FieldMatrix<std::remove_const_t<K>,1,1>; -}; + template<class K> + struct FieldTraits<Impl::ScalarMatrixView<K>> : public FieldTraits<std::remove_const_t<K>> {}; + + template<class K> + struct DenseMatVecTraits<Impl::ScalarMatrixView<K>> + { + using derived_type = Impl::ScalarMatrixView<K>; + using row_type = Impl::ScalarVectorView<K>; + using row_reference = row_type&; + using const_row_reference = const row_type&; + using value_type = std::remove_const_t<K>; + using size_type = std::size_t; + }; + + + template<class K> + struct AutonomousValueType<Impl::ScalarMatrixView<K>> + { + using type = FieldMatrix<std::remove_const_t<K>,1,1>; + }; } // end namespace Dune diff --git a/dune/common/scalarvectorview.hh b/dune/common/scalarvectorview.hh index a5614f9a7a7240dbced20694233362ea24eec4d5..28fd30ba007331d44c4aab3d352c8260977994cf 100644 --- a/dune/common/scalarvectorview.hh +++ b/dune/common/scalarvectorview.hh @@ -17,194 +17,194 @@ namespace Dune { namespace Impl { -/** @addtogroup DenseMatVec -@{ -*/ - -/*! \file -* \brief Implements a scalar vector view wrapper around an existing scalar. -*/ - -/** \brief A wrapper making a scalar look like a vector -* -* This stores a pointer to a scalar of type K and -* provides the interface of a vector with a single -* entry represented by the data behind the pointer. -*/ -template<class K> -class ScalarVectorView : -public DenseVector<ScalarVectorView<K>> -{ -K* dataP_; -using Base = DenseVector<ScalarVectorView<K>>; - -template <class> -friend class ScalarVectorView; -public: - -//! export size -enum { -//! The size of this vector. -dimension = 1 -}; - -/** \brief The type used for array indices and sizes */ -using size_type = typename Base::size_type; - -/** \brief The type used for references to the vector entry */ -using reference = std::decay_t<K>&; - -/** \brief The type used for const references to the vector entry */ -using const_reference = const K&; - -//===== construction - -/** \brief Default constructor */ -constexpr ScalarVectorView () -: dataP_(nullptr) -{} - -/** \brief Construct from a pointer to a scalar */ -ScalarVectorView (K* p) : -dataP_(p) -{} - -//! Copy constructor -ScalarVectorView (const ScalarVectorView &other) : -Base(), -dataP_(other.dataP_) -{} - -//! Move constructor -ScalarVectorView (ScalarVectorView &&other) : -Base(), -dataP_( other.dataP_ ) -{} - -//! Copy assignment operator -ScalarVectorView& operator= (const ScalarVectorView& other) -{ -assert(dataP_); -assert(other.dataP_); -*dataP_ = *(other.dataP_); -return *this; -} - -template<class KK> -ScalarVectorView& operator= (const ScalarVectorView<KK>& other) -{ -assert(dataP_); -assert(other.dataP_); -*dataP_ = *(other.dataP_); -return *this; -} - -//! Assignment operator from a scalar -template<typename T, -std::enable_if_t<std::is_convertible<T, K>::value, int> = 0> -inline ScalarVectorView& operator= (const T& k) -{ -*dataP_ = k; -return *this; -} - -/** \brief Container size -- this is always 1 */ -static constexpr size_type size () -{ -return 1; -} - -/** \brief Random access operator, actually disregards its argument */ -K& operator[] (size_type i) -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return *dataP_; -} - -/** \brief Const random access operator, actually disregards its argument */ -const K& operator[] (size_type i) const -{ -DUNE_UNUSED_PARAMETER(i); -DUNE_ASSERT_BOUNDS(i == 0); -return *dataP_; -} -}; // class ScalarVectorView + /** @addtogroup DenseMatVec + @{ + */ + + /*! \file + * \brief Implements a scalar vector view wrapper around an existing scalar. + */ + + /** \brief A wrapper making a scalar look like a vector + * + * This stores a pointer to a scalar of type K and + * provides the interface of a vector with a single + * entry represented by the data behind the pointer. + */ + template<class K> + class ScalarVectorView : + public DenseVector<ScalarVectorView<K>> + { + K* dataP_; + using Base = DenseVector<ScalarVectorView<K>>; + + template <class> + friend class ScalarVectorView; + public: + + //! export size + enum { + //! The size of this vector. + dimension = 1 + }; + + /** \brief The type used for array indices and sizes */ + using size_type = typename Base::size_type; + + /** \brief The type used for references to the vector entry */ + using reference = std::decay_t<K>&; + + /** \brief The type used for const references to the vector entry */ + using const_reference = const K&; + + //===== construction + + /** \brief Default constructor */ + constexpr ScalarVectorView () + : dataP_(nullptr) + {} + + /** \brief Construct from a pointer to a scalar */ + ScalarVectorView (K* p) : + dataP_(p) + {} + + //! Copy constructor + ScalarVectorView (const ScalarVectorView &other) : + Base(), + dataP_(other.dataP_) + {} + + //! Move constructor + ScalarVectorView (ScalarVectorView &&other) : + Base(), + dataP_( other.dataP_ ) + {} + + //! Copy assignment operator + ScalarVectorView& operator= (const ScalarVectorView& other) + { + assert(dataP_); + assert(other.dataP_); + *dataP_ = *(other.dataP_); + return *this; + } + + template<class KK> + ScalarVectorView& operator= (const ScalarVectorView<KK>& other) + { + assert(dataP_); + assert(other.dataP_); + *dataP_ = *(other.dataP_); + return *this; + } + + //! Assignment operator from a scalar + template<typename T, + std::enable_if_t<std::is_convertible<T, K>::value, int> = 0> + inline ScalarVectorView& operator= (const T& k) + { + *dataP_ = k; + return *this; + } + + /** \brief Container size -- this is always 1 */ + static constexpr size_type size () + { + return 1; + } + + /** \brief Random access operator, actually disregards its argument */ + K& operator[] (size_type i) + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return *dataP_; + } + + /** \brief Const random access operator, actually disregards its argument */ + const K& operator[] (size_type i) const + { + DUNE_UNUSED_PARAMETER(i); + DUNE_ASSERT_BOUNDS(i == 0); + return *dataP_; + } + }; // class ScalarVectorView } // namespace Impl -template< class K> -struct DenseMatVecTraits< Impl::ScalarVectorView<K> > -{ -using derived_type = Impl::ScalarVectorView<K>; -using value_type = std::remove_const_t<K>; -using size_type = std::size_t; -}; + template< class K> + struct DenseMatVecTraits< Impl::ScalarVectorView<K> > + { + using derived_type = Impl::ScalarVectorView<K>; + using value_type = std::remove_const_t<K>; + using size_type = std::size_t; + }; -template< class K > -struct FieldTraits< Impl::ScalarVectorView<K> > : public FieldTraits<std::remove_const_t<K>> {}; + template< class K > + struct FieldTraits< Impl::ScalarVectorView<K> > : public FieldTraits<std::remove_const_t<K>> {}; -template<class K> -struct AutonomousValueType<Impl::ScalarVectorView<K>> -{ -using type = FieldVector<std::remove_const_t<K>,1>; -}; + template<class K> + struct AutonomousValueType<Impl::ScalarVectorView<K>> + { + using type = FieldVector<std::remove_const_t<K>,1>; + }; namespace Impl { -/** \brief Read a ScalarVectorView from an input stream -* \relates ScalarVectorView -* -* \note This operator is STL compliant, i.e., the content of v is only -* changed if the read operation is successful. -* -* \param[in] in std :: istream to read from -* \param[out] v ScalarVectorView to be read -* -* \returns the input stream (in) -*/ -template<class K> -inline std::istream &operator>> ( std::istream &in, ScalarVectorView<K> &v ) -{ -K w; -if(in >> w) -v = w; -return in; -} - - -/** \brief Wrap a scalar as a 1-vector */ -template<class T, -std::enable_if_t<IsNumber<T>::value, int> = 0> -auto asVector(T& t) -{ -return ScalarVectorView<T>{&t}; -} - -/** \brief Wrap a const scalar as a const 1-vector */ -template<class T, -std::enable_if_t<IsNumber<T>::value, int> = 0> -auto asVector(const T& t) -{ -return ScalarVectorView<const T>{&t}; -} - -/** \brief Non-scalar types are assumed to be arrays, and simply forwarded */ -template<class T, -std::enable_if_t<not IsNumber<T>::value, int> = 0> -T& asVector(T& t) -{ -return t; -} - -/** \brief Non-scalar types are assumed to be arrays, and simply forwarded */ -template<class T, -std::enable_if_t<not IsNumber<T>::value, int> = 0> -const T& asVector(const T& t) -{ -return t; -} + /** \brief Read a ScalarVectorView from an input stream + * \relates ScalarVectorView + * + * \note This operator is STL compliant, i.e., the content of v is only + * changed if the read operation is successful. + * + * \param[in] in std :: istream to read from + * \param[out] v ScalarVectorView to be read + * + * \returns the input stream (in) + */ + template<class K> + inline std::istream &operator>> ( std::istream &in, ScalarVectorView<K> &v ) + { + K w; + if(in >> w) + v = w; + return in; + } + + + /** \brief Wrap a scalar as a 1-vector */ + template<class T, + std::enable_if_t<IsNumber<T>::value, int> = 0> + auto asVector(T& t) + { + return ScalarVectorView<T>{&t}; + } + + /** \brief Wrap a const scalar as a const 1-vector */ + template<class T, + std::enable_if_t<IsNumber<T>::value, int> = 0> + auto asVector(const T& t) + { + return ScalarVectorView<const T>{&t}; + } + + /** \brief Non-scalar types are assumed to be arrays, and simply forwarded */ + template<class T, + std::enable_if_t<not IsNumber<T>::value, int> = 0> + T& asVector(T& t) + { + return t; + } + + /** \brief Non-scalar types are assumed to be arrays, and simply forwarded */ + template<class T, + std::enable_if_t<not IsNumber<T>::value, int> = 0> + const T& asVector(const T& t) + { + return t; + } } // end namespace Impl diff --git a/dune/common/shared_ptr.hh b/dune/common/shared_ptr.hh index 38050b521648a7c6eb7c747620b0df1454a7320a..7616a6e3b247eb0966952543d6ba9efbadb25015 100644 --- a/dune/common/shared_ptr.hh +++ b/dune/common/shared_ptr.hh @@ -9,116 +9,116 @@ #include <dune/common/typetraits.hh> /** -* @file -* @brief This file implements several utilities related to std::shared_ptr -* @author Markus Blatt -*/ + * @file + * @brief This file implements several utilities related to std::shared_ptr + * @author Markus Blatt + */ namespace Dune { -/** -@brief implements the Deleter concept of shared_ptr without deleting anything -@relates shared_ptr - -If you allocate an object on the stack, but want to pass it to a class or function as a shared_ptr, -you can use this deleter to avoid accidental deletion of the stack-allocated object. - -For convenience we provide two free functions to create a shared_ptr from a stack-allocated object -(\see stackobject_to_shared_ptr): - -1) Convert a stack-allocated object to a shared_ptr: -@code -int i = 10; -std::shared_ptr<int> pi = stackobject_to_shared_ptr(i); -@endcode -2) Convert a stack-allocated object to a std::shared_ptr of a base class -@code -class A {}; -class B : public A {}; - -... - -B b; -std::shared_ptr<A> pa = stackobject_to_shared_ptr<A>(b); -@endcode - -@tparam T type of the stack-allocated object -*/ -template<class T> -struct null_deleter -{ -void operator() (T*) const {} -}; - -/** -@brief Create a shared_ptr for a stack-allocated object -@relatesalso null_deleter -@code -#include <dune/common/shared_ptr.hh> -@endcode - -Usage: -@code -int i = 10; -std::shared_ptr<int> pi = stackobject_to_shared_ptr(i); -@endcode -The @c std::shared_ptr points to the object on the stack, but its deleter is -set to an instance of @c null_deleter so that nothing happens when the @c -shared_ptr is destroyed. - -@sa null_deleter -*/ -template<typename T> -inline std::shared_ptr<T> stackobject_to_shared_ptr(T & t) -{ -return std::shared_ptr<T>(&t, null_deleter<T>()); -} - - -/** -* \brief Capture R-value reference to shared_ptr -* -* This will store a copy of the passed object in -* a shared_ptr. -* -* The two overloads of wrap_or_move are intended -* to capture references and temporaries in a unique -* way without creating copies and only moving if -* necessary. -* -* Be careful: Only use this function if you are -* aware of it's implications. You can e.g. easily -* end up storing a reference to a temporary if -* you use this inside of another function without -* perfect forwarding. -*/ -template<class T> -auto wrap_or_move(T&& t) -{ -return std::make_shared<std::decay_t<T>>(std::forward<T>(t)); -} - -/** -* \brief Capture L-value reference to std::shared_ptr -* -* This will store a pointer for the passed reference -* in a non-owning std::shared_ptr. -* -* The two overloads of wrap_or_move are intended -* to capture references and temporaries in a unique -* way without creating copies and only moving if -* necessary. -* -* Be careful: Only use this function if you are -* aware of it's implications. You can e.g. easily -* end up storing a reference to a temporary if -* you use this inside of another function without -* perfect forwarding. -*/ -template<class T> -auto wrap_or_move(T& t) -{ -return stackobject_to_shared_ptr(t); -} + /** + @brief implements the Deleter concept of shared_ptr without deleting anything + @relates shared_ptr + + If you allocate an object on the stack, but want to pass it to a class or function as a shared_ptr, + you can use this deleter to avoid accidental deletion of the stack-allocated object. + + For convenience we provide two free functions to create a shared_ptr from a stack-allocated object + (\see stackobject_to_shared_ptr): + + 1) Convert a stack-allocated object to a shared_ptr: + @code + int i = 10; + std::shared_ptr<int> pi = stackobject_to_shared_ptr(i); + @endcode + 2) Convert a stack-allocated object to a std::shared_ptr of a base class + @code + class A {}; + class B : public A {}; + + ... + + B b; + std::shared_ptr<A> pa = stackobject_to_shared_ptr<A>(b); + @endcode + + @tparam T type of the stack-allocated object + */ + template<class T> + struct null_deleter + { + void operator() (T*) const {} + }; + + /** + @brief Create a shared_ptr for a stack-allocated object + @relatesalso null_deleter + @code + #include <dune/common/shared_ptr.hh> + @endcode + + Usage: + @code + int i = 10; + std::shared_ptr<int> pi = stackobject_to_shared_ptr(i); + @endcode + The @c std::shared_ptr points to the object on the stack, but its deleter is + set to an instance of @c null_deleter so that nothing happens when the @c + shared_ptr is destroyed. + + @sa null_deleter + */ + template<typename T> + inline std::shared_ptr<T> stackobject_to_shared_ptr(T & t) + { + return std::shared_ptr<T>(&t, null_deleter<T>()); + } + + + /** + * \brief Capture R-value reference to shared_ptr + * + * This will store a copy of the passed object in + * a shared_ptr. + * + * The two overloads of wrap_or_move are intended + * to capture references and temporaries in a unique + * way without creating copies and only moving if + * necessary. + * + * Be careful: Only use this function if you are + * aware of it's implications. You can e.g. easily + * end up storing a reference to a temporary if + * you use this inside of another function without + * perfect forwarding. + */ + template<class T> + auto wrap_or_move(T&& t) + { + return std::make_shared<std::decay_t<T>>(std::forward<T>(t)); + } + + /** + * \brief Capture L-value reference to std::shared_ptr + * + * This will store a pointer for the passed reference + * in a non-owning std::shared_ptr. + * + * The two overloads of wrap_or_move are intended + * to capture references and temporaries in a unique + * way without creating copies and only moving if + * necessary. + * + * Be careful: Only use this function if you are + * aware of it's implications. You can e.g. easily + * end up storing a reference to a temporary if + * you use this inside of another function without + * perfect forwarding. + */ + template<class T> + auto wrap_or_move(T& t) + { + return stackobject_to_shared_ptr(t); + } } #endif diff --git a/dune/common/simd.hh b/dune/common/simd.hh index 56c8cc2c42f1d29853940f26eb09de4c7bba0f4d..61db3af8a74595d6b2cc6f6b778e2413953058b3 100644 --- a/dune/common/simd.hh +++ b/dune/common/simd.hh @@ -6,25 +6,25 @@ #warning Use the new infrastructure from dune/common/simd/simd.h instead. /** -\file + \file -\brief Abstractions for support of dedicated SIMD data types + \brief Abstractions for support of dedicated SIMD data types -Libraries like Vc (https://github.com/VcDevel/Vc) add high-level -data types for SIMD (or vectorization) support in C++. Most of -these operations mimic the behavior of a numerical data type. Some -boolean operations can not be implemented in a compatible way to -trivial data types. + Libraries like Vc (https://github.com/VcDevel/Vc) add high-level + data types for SIMD (or vectorization) support in C++. Most of + these operations mimic the behavior of a numerical data type. Some + boolean operations can not be implemented in a compatible way to + trivial data types. -This header contains additional abstractions to help writing code -that works with trivial numerical data types (like double) and Vc -vectorization data types. + This header contains additional abstractions to help writing code + that works with trivial numerical data types (like double) and Vc + vectorization data types. -See also the conditional.hh and range_utils.hh headers. + See also the conditional.hh and range_utils.hh headers. -\deprecated Use the newer simd architecture from dune/common/simd/simd.hh -instead. -*/ + \deprecated Use the newer simd architecture from dune/common/simd/simd.hh + instead. + */ #include <cassert> #include <cstddef> @@ -46,455 +46,455 @@ namespace Dune { #if HAVE_VC -namespace VcImpl { -//! A reference-like proxy for elements of random-access vectors. -/** -* This is necessary because Vc's lane-access operation return a proxy -* that cannot constructed by non-Vc code (i.e. code that isn't -* explicitly declared `friend`). This means in particular that there -* is no copy/move constructor, meaning we cannot return such proxies -* from our own functions, such as `lane()`. To work around this, we -* define our own proxy class which internally holds a reference to the -* vector and a lane index. -*/ -template<class V> -class Proxy -{ -static_assert(std::is_same<V, std::decay_t<V> >::value, "Class Proxy " -"may only be instantiated with unqualified types"); -public: -using value_type = typename V::value_type; - -private: -static_assert(std::is_arithmetic<value_type>::value, -"Only artihmetic types are supported"); -V &vec_; -std::size_t idx_; - -public: -Proxy(std::size_t idx, V &vec) -: vec_(vec), idx_(idx) -{ } - -operator value_type() const { return vec_[idx_]; } - -// postfix operators - -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -value_type operator++(int) { return vec_[idx_]++; } -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -value_type operator--(int) { return vec_[idx_]--; } - -// unary (prefix) operators -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -Proxy &operator++() { ++(vec_[idx_]); return *this; } -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -Proxy &operator--() { --(vec_[idx_]); return *this; } -decltype(auto) operator!() const { return !(vec_[idx_]); } -decltype(auto) operator+() const { return +(vec_[idx_]); } -decltype(auto) operator-() const { return -(vec_[idx_]); } -template<class T = value_type, -class = std::enable_if_t<std::is_integral<T>::value> > -decltype(auto) operator~() const { return ~(vec_[idx_]); } - -// binary operators + namespace VcImpl { + //! A reference-like proxy for elements of random-access vectors. + /** + * This is necessary because Vc's lane-access operation return a proxy + * that cannot constructed by non-Vc code (i.e. code that isn't + * explicitly declared `friend`). This means in particular that there + * is no copy/move constructor, meaning we cannot return such proxies + * from our own functions, such as `lane()`. To work around this, we + * define our own proxy class which internally holds a reference to the + * vector and a lane index. + */ + template<class V> + class Proxy + { + static_assert(std::is_same<V, std::decay_t<V> >::value, "Class Proxy " + "may only be instantiated with unqualified types"); + public: + using value_type = typename V::value_type; + + private: + static_assert(std::is_arithmetic<value_type>::value, + "Only artihmetic types are supported"); + V &vec_; + std::size_t idx_; + + public: + Proxy(std::size_t idx, V &vec) + : vec_(vec), idx_(idx) + { } + + operator value_type() const { return vec_[idx_]; } + + // postfix operators + + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + value_type operator++(int) { return vec_[idx_]++; } + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + value_type operator--(int) { return vec_[idx_]--; } + + // unary (prefix) operators + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + Proxy &operator++() { ++(vec_[idx_]); return *this; } + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + Proxy &operator--() { --(vec_[idx_]); return *this; } + decltype(auto) operator!() const { return !(vec_[idx_]); } + decltype(auto) operator+() const { return +(vec_[idx_]); } + decltype(auto) operator-() const { return -(vec_[idx_]); } + template<class T = value_type, + class = std::enable_if_t<std::is_integral<T>::value> > + decltype(auto) operator~() const { return ~(vec_[idx_]); } + + // binary operators #define DUNE_SIMD_VC_BINARY_OP(OP) \ -template<class T> \ -auto operator OP(T &&o) const \ --> decltype(vec_[idx_] OP valueCast(std::forward<T>(o))) \ -{ \ -return vec_[idx_] OP valueCast(std::forward<T>(o)); \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_SIMD_VC_BINARY_OP(*); -DUNE_SIMD_VC_BINARY_OP(/); -DUNE_SIMD_VC_BINARY_OP(%); - -DUNE_SIMD_VC_BINARY_OP(+); -DUNE_SIMD_VC_BINARY_OP(-); - -DUNE_SIMD_VC_BINARY_OP(<<); -DUNE_SIMD_VC_BINARY_OP(>>); - -DUNE_SIMD_VC_BINARY_OP(<); -DUNE_SIMD_VC_BINARY_OP(>); -DUNE_SIMD_VC_BINARY_OP(<=); -DUNE_SIMD_VC_BINARY_OP(>=); - -DUNE_SIMD_VC_BINARY_OP(==); -DUNE_SIMD_VC_BINARY_OP(!=); - -DUNE_SIMD_VC_BINARY_OP(&); -DUNE_SIMD_VC_BINARY_OP(^); -DUNE_SIMD_VC_BINARY_OP(|); - -DUNE_SIMD_VC_BINARY_OP(&&); -DUNE_SIMD_VC_BINARY_OP(||); + template<class T> \ + auto operator OP(T &&o) const \ + -> decltype(vec_[idx_] OP valueCast(std::forward<T>(o))) \ + { \ + return vec_[idx_] OP valueCast(std::forward<T>(o)); \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_SIMD_VC_BINARY_OP(*); + DUNE_SIMD_VC_BINARY_OP(/); + DUNE_SIMD_VC_BINARY_OP(%); + + DUNE_SIMD_VC_BINARY_OP(+); + DUNE_SIMD_VC_BINARY_OP(-); + + DUNE_SIMD_VC_BINARY_OP(<<); + DUNE_SIMD_VC_BINARY_OP(>>); + + DUNE_SIMD_VC_BINARY_OP(<); + DUNE_SIMD_VC_BINARY_OP(>); + DUNE_SIMD_VC_BINARY_OP(<=); + DUNE_SIMD_VC_BINARY_OP(>=); + + DUNE_SIMD_VC_BINARY_OP(==); + DUNE_SIMD_VC_BINARY_OP(!=); + + DUNE_SIMD_VC_BINARY_OP(&); + DUNE_SIMD_VC_BINARY_OP(^); + DUNE_SIMD_VC_BINARY_OP(|); + + DUNE_SIMD_VC_BINARY_OP(&&); + DUNE_SIMD_VC_BINARY_OP(||); #undef DUNE_SIMD_VC_BINARY_OP #define DUNE_SIMD_VC_ASSIGNMENT(OP) \ -template<class T> \ -auto operator OP(T &&o) \ --> std::enable_if_t<AlwaysTrue<decltype( \ -vec_[idx_] OP valueCast(std::forward<T>(o)) \ -)>::value, Proxy&> \ -{ \ -vec_[idx_] OP valueCast(std::forward<T>(o)); \ -return *this; \ -} \ -static_assert(true, "Require semicolon to unconfuse editors") - -DUNE_SIMD_VC_ASSIGNMENT(=); -DUNE_SIMD_VC_ASSIGNMENT(*=); -DUNE_SIMD_VC_ASSIGNMENT(/=); -DUNE_SIMD_VC_ASSIGNMENT(%=); -DUNE_SIMD_VC_ASSIGNMENT(+=); -DUNE_SIMD_VC_ASSIGNMENT(-=); -DUNE_SIMD_VC_ASSIGNMENT(<<=); -DUNE_SIMD_VC_ASSIGNMENT(>>=); -DUNE_SIMD_VC_ASSIGNMENT(&=); -DUNE_SIMD_VC_ASSIGNMENT(^=); -DUNE_SIMD_VC_ASSIGNMENT(|=); + template<class T> \ + auto operator OP(T &&o) \ + -> std::enable_if_t<AlwaysTrue<decltype( \ + vec_[idx_] OP valueCast(std::forward<T>(o)) \ + )>::value, Proxy&> \ + { \ + vec_[idx_] OP valueCast(std::forward<T>(o)); \ + return *this; \ + } \ + static_assert(true, "Require semicolon to unconfuse editors") + + DUNE_SIMD_VC_ASSIGNMENT(=); + DUNE_SIMD_VC_ASSIGNMENT(*=); + DUNE_SIMD_VC_ASSIGNMENT(/=); + DUNE_SIMD_VC_ASSIGNMENT(%=); + DUNE_SIMD_VC_ASSIGNMENT(+=); + DUNE_SIMD_VC_ASSIGNMENT(-=); + DUNE_SIMD_VC_ASSIGNMENT(<<=); + DUNE_SIMD_VC_ASSIGNMENT(>>=); + DUNE_SIMD_VC_ASSIGNMENT(&=); + DUNE_SIMD_VC_ASSIGNMENT(^=); + DUNE_SIMD_VC_ASSIGNMENT(|=); #undef DUNE_SIMD_VC_ASSIGNMENT -// swap on proxies swaps the proxied vector entries. As such, it -// applies to rvalues of proxies too, not just lvalues -template<class V1, class V2> -friend void swap(Proxy<V1>, Proxy<V2>); - -template<class T> -friend void swap(Proxy p1, T& s2) -{ -// don't use swap() ourselves -- not supported by Vc 1.3.0 (but is -// supported by Vc 1.3.2) -T tmp = p1.vec_[p1.idx_]; -p1.vec_[p1.idx_] = s2; -s2 = tmp; -} - -template<class T> -friend void swap(T& s1, Proxy p2) -{ -T tmp = s1; -s1 = p2.vec_[p2.idx_]; -p2.vec_[p2.idx_] = tmp; -} -}; - -template<class V1, class V2> -void swap(Proxy<V1> p1, Proxy<V2> p2) -{ -typename V1::value_type tmp = p1.vec_[p1.idx_]; -p1.vec_[p1.idx_] = p2.vec_[p2.idx_]; -p2.vec_[p2.idx_] = tmp; -} -} // namespace VcImpl + // swap on proxies swaps the proxied vector entries. As such, it + // applies to rvalues of proxies too, not just lvalues + template<class V1, class V2> + friend void swap(Proxy<V1>, Proxy<V2>); + + template<class T> + friend void swap(Proxy p1, T& s2) + { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + T tmp = p1.vec_[p1.idx_]; + p1.vec_[p1.idx_] = s2; + s2 = tmp; + } + + template<class T> + friend void swap(T& s1, Proxy p2) + { + T tmp = s1; + s1 = p2.vec_[p2.idx_]; + p2.vec_[p2.idx_] = tmp; + } + }; + + template<class V1, class V2> + void swap(Proxy<V1> p1, Proxy<V2> p2) + { + typename V1::value_type tmp = p1.vec_[p1.idx_]; + p1.vec_[p1.idx_] = p2.vec_[p2.idx_]; + p2.vec_[p2.idx_] = tmp; + } + } // namespace VcImpl #endif // HAVE_VC -template<typename T> -struct SimdScalarTypeTraits -{ -using type = T; -}; + template<typename T> + struct SimdScalarTypeTraits + { + using type = T; + }; -template<typename T> -using SimdScalar = typename SimdScalarTypeTraits<T>::type; + template<typename T> + using SimdScalar = typename SimdScalarTypeTraits<T>::type; #if HAVE_VC -/* -Add Vc specializations for the SimdScalarTypeTraits trais class -*/ -template<typename T, typename A> -struct SimdScalarTypeTraits< Vc::Vector<T,A> > -{ -using type = T; -}; - -template<typename T, std::size_t N, typename V, std::size_t M> -struct SimdScalarTypeTraits< Vc::SimdArray<T,N,V,M> > -{ -using type = T; -}; + /* + Add Vc specializations for the SimdScalarTypeTraits trais class + */ + template<typename T, typename A> + struct SimdScalarTypeTraits< Vc::Vector<T,A> > + { + using type = T; + }; + + template<typename T, std::size_t N, typename V, std::size_t M> + struct SimdScalarTypeTraits< Vc::SimdArray<T,N,V,M> > + { + using type = T; + }; #endif // HAVE_VC -//! deduce the underlying scalar data type of an AlignedNumber -template<typename T, std::size_t align> -struct SimdScalarTypeTraits< AlignedNumber<T,align> > -{ -using type = T; -}; - -template<typename V, typename = void> -struct SimdIndexTypeTraits { -using type = std::size_t; -}; - -//! An simd vector of indices corresponding to a simd vector V -/** -* lanes(T()) == lanes(SimdIndex<T>()) holds. -* -* \note The size of the elements of a SimdIndex isn't very well-defined. -* Be careful. -*/ -template<typename V> -using SimdIndex = typename SimdIndexTypeTraits<V>::type; + //! deduce the underlying scalar data type of an AlignedNumber + template<typename T, std::size_t align> + struct SimdScalarTypeTraits< AlignedNumber<T,align> > + { + using type = T; + }; + + template<typename V, typename = void> + struct SimdIndexTypeTraits { + using type = std::size_t; + }; + + //! An simd vector of indices corresponding to a simd vector V + /** + * lanes(T()) == lanes(SimdIndex<T>()) holds. + * + * \note The size of the elements of a SimdIndex isn't very well-defined. + * Be careful. + */ + template<typename V> + using SimdIndex = typename SimdIndexTypeTraits<V>::type; #if HAVE_VC -template<typename T, typename A> -struct SimdIndexTypeTraits<Vc::Vector<T, A> > { -using type = typename Vc::Vector<T, A>::index_type; -}; - -template<typename T, std::size_t n, typename V> -struct SimdIndexTypeTraits<Vc::SimdArray<T, n, V> > { -using type = typename Vc::SimdArray<T, n, V>::index_type; -}; + template<typename T, typename A> + struct SimdIndexTypeTraits<Vc::Vector<T, A> > { + using type = typename Vc::Vector<T, A>::index_type; + }; + + template<typename T, std::size_t n, typename V> + struct SimdIndexTypeTraits<Vc::SimdArray<T, n, V> > { + using type = typename Vc::SimdArray<T, n, V>::index_type; + }; #endif // HAVE_VC -template<typename V, typename = void> -struct SimdMaskTypeTraits { -using type = bool; -}; + template<typename V, typename = void> + struct SimdMaskTypeTraits { + using type = bool; + }; -//! A simd vector of truth values corresponding to a simd vector V -/** -* lanes(T()) == lanes(SimdMask<T>()) holds. -*/ -template<typename V> -using SimdMask = typename SimdMaskTypeTraits<V>::type; + //! A simd vector of truth values corresponding to a simd vector V + /** + * lanes(T()) == lanes(SimdMask<T>()) holds. + */ + template<typename V> + using SimdMask = typename SimdMaskTypeTraits<V>::type; #if HAVE_VC -template<typename T, typename A> -struct SimdMaskTypeTraits<Vc::Vector<T, A> > { -using type = typename Vc::Vector<T, A>::mask_type; -}; - -template<typename T, std::size_t n, typename V> -struct SimdMaskTypeTraits<Vc::SimdArray<T, n, V> > { -using type = typename Vc::SimdArray<T, n, V>::mask_type; -}; + template<typename T, typename A> + struct SimdMaskTypeTraits<Vc::Vector<T, A> > { + using type = typename Vc::Vector<T, A>::mask_type; + }; + + template<typename T, std::size_t n, typename V> + struct SimdMaskTypeTraits<Vc::SimdArray<T, n, V> > { + using type = typename Vc::SimdArray<T, n, V>::mask_type; + }; #endif // HAVE_VC #if HAVE_VC -/* -Add Vc specializations for cond(), see conditional.hh -*/ -template<typename T, typename A> -Vc::Vector<T,A> cond(const Vc::Mask<T,A> & b, -const Vc::Vector<T,A> & v1, -const Vc::Vector<T,A> & v2) -{ -return std::move(Vc::iif(b, v1, v2)); -} - -template<typename T, std::size_t N, typename V, std::size_t M> -Vc::SimdArray<T,N,V,M> cond(const typename Vc::SimdArray<T,N,V,M>::mask_type & b, -const Vc::SimdArray<T,N,V,M> & v1, -const Vc::SimdArray<T,N,V,M> & v2) -{ -return std::move(Vc::iif(b, v1, v2)); -} + /* + Add Vc specializations for cond(), see conditional.hh + */ + template<typename T, typename A> + Vc::Vector<T,A> cond(const Vc::Mask<T,A> & b, + const Vc::Vector<T,A> & v1, + const Vc::Vector<T,A> & v2) + { + return std::move(Vc::iif(b, v1, v2)); + } + + template<typename T, std::size_t N, typename V, std::size_t M> + Vc::SimdArray<T,N,V,M> cond(const typename Vc::SimdArray<T,N,V,M>::mask_type & b, + const Vc::SimdArray<T,N,V,M> & v1, + const Vc::SimdArray<T,N,V,M> & v2) + { + return std::move(Vc::iif(b, v1, v2)); + } #endif // HAVE_VC #if HAVE_VC -/* -Add Vc specializations for several boolean operations, see rangeutitlities.hh: - -max_value, min_value, any_true, all_true -*/ -template<typename T, typename A> -T max_value(const Vc::Vector<T,A> & v) -{ -return v.max(); -} - -template<typename T, std::size_t N, typename V, std::size_t M> -double max_value(const Vc::SimdArray<T,N,V,M> & v) -{ -return v.max(); -} - -template<typename T, typename A> -T min_value(const Vc::Vector<T,A> & v) -{ -return v.min(); -} - -template<typename T, std::size_t N, typename V, std::size_t M> -double min_value(const Vc::SimdArray<T,N,V,M> & v) -{ -return v.min(); -} - -template<typename T, typename A> -bool any_true(const Vc::Mask<T,A> & v) -{ -return Vc::any_of(v); -} - -template<typename T, std::size_t N, typename V, std::size_t M> -bool any_true(const Vc::SimdMaskArray<T,N,V,M> & v) -{ -return Vc::any_of(v); -} - -template<typename T, typename A> -bool all_true(const Vc::Mask<T,A> & v) -{ -return Vc::all_of(v); -} - -template<typename T, std::size_t N, typename V, std::size_t M> -bool all_true(const Vc::SimdMaskArray<T,N,V,M> & v) -{ -return Vc::all_of(v); -} + /* + Add Vc specializations for several boolean operations, see rangeutitlities.hh: + + max_value, min_value, any_true, all_true + */ + template<typename T, typename A> + T max_value(const Vc::Vector<T,A> & v) + { + return v.max(); + } + + template<typename T, std::size_t N, typename V, std::size_t M> + double max_value(const Vc::SimdArray<T,N,V,M> & v) + { + return v.max(); + } + + template<typename T, typename A> + T min_value(const Vc::Vector<T,A> & v) + { + return v.min(); + } + + template<typename T, std::size_t N, typename V, std::size_t M> + double min_value(const Vc::SimdArray<T,N,V,M> & v) + { + return v.min(); + } + + template<typename T, typename A> + bool any_true(const Vc::Mask<T,A> & v) + { + return Vc::any_of(v); + } + + template<typename T, std::size_t N, typename V, std::size_t M> + bool any_true(const Vc::SimdMaskArray<T,N,V,M> & v) + { + return Vc::any_of(v); + } + + template<typename T, typename A> + bool all_true(const Vc::Mask<T,A> & v) + { + return Vc::all_of(v); + } + + template<typename T, std::size_t N, typename V, std::size_t M> + bool all_true(const Vc::SimdMaskArray<T,N,V,M> & v) + { + return Vc::all_of(v); + } #endif // HAVE_VC -//! get the number of lanes of a simd vector (scalar version) -template<class T> -std::size_t lanes(const T &) { return 1; } - -//! access a lane of a simd vector (scalar version) -template<class T> -T lane(std::size_t l, const T &v) -{ -assert(l == 0); -return v; -} - -//! access a lane of a simd vector (scalar version) -template<class T> -T &lane(std::size_t l, T &v) -{ -assert(l == 0); -return v; -} + //! get the number of lanes of a simd vector (scalar version) + template<class T> + std::size_t lanes(const T &) { return 1; } + + //! access a lane of a simd vector (scalar version) + template<class T> + T lane(std::size_t l, const T &v) + { + assert(l == 0); + return v; + } + + //! access a lane of a simd vector (scalar version) + template<class T> + T &lane(std::size_t l, T &v) + { + assert(l == 0); + return v; + } #if HAVE_VC -template<class T, class A> -std::size_t lanes(const Vc::Vector<T, A> &) -{ -return Vc::Vector<T, A>::size(); -} - -template<class T, class A> -T lane(std::size_t l, const Vc::Vector<T, A> &v) -{ -assert(l < lanes(v)); -return v[l]; -} - -template<class T, class A> -auto lane(std::size_t l, Vc::Vector<T, A> &v) -{ -assert(l < lanes(v)); -return VcImpl::Proxy<Vc::Vector<T, A> >{l, v}; -} - -template<class T, std::size_t n, class V> -std::size_t lanes(const Vc::SimdArray<T, n, V> &) -{ -return n; -} - -template<class T, std::size_t n, class V> -T lane(std::size_t l, const Vc::SimdArray<T, n, V> &v) -{ -assert(l < n); -return v[l]; -} - -template<class T, std::size_t n, class V> -auto lane(std::size_t l, Vc::SimdArray<T, n, V> &v) -{ -assert(l < n); -return VcImpl::Proxy<Vc::SimdArray<T, n, V> >{l, v}; -} - -template<class T, std::size_t n, class V> -std::size_t lanes(const Vc::SimdMaskArray<T, n, V> &) -{ -return n; -} - -template<class T, std::size_t n, class V> -bool lane(std::size_t l, const Vc::SimdMaskArray<T, n, V> &v) -{ -assert(l < n); -return v[l]; -} - -template<class T, std::size_t n, class V> -auto lane(std::size_t l, Vc::SimdMaskArray<T, n, V> &v) -{ -assert(l < n); -return VcImpl::Proxy<Vc::SimdMaskArray<T, n, V> >{l, v}; -} + template<class T, class A> + std::size_t lanes(const Vc::Vector<T, A> &) + { + return Vc::Vector<T, A>::size(); + } + + template<class T, class A> + T lane(std::size_t l, const Vc::Vector<T, A> &v) + { + assert(l < lanes(v)); + return v[l]; + } + + template<class T, class A> + auto lane(std::size_t l, Vc::Vector<T, A> &v) + { + assert(l < lanes(v)); + return VcImpl::Proxy<Vc::Vector<T, A> >{l, v}; + } + + template<class T, std::size_t n, class V> + std::size_t lanes(const Vc::SimdArray<T, n, V> &) + { + return n; + } + + template<class T, std::size_t n, class V> + T lane(std::size_t l, const Vc::SimdArray<T, n, V> &v) + { + assert(l < n); + return v[l]; + } + + template<class T, std::size_t n, class V> + auto lane(std::size_t l, Vc::SimdArray<T, n, V> &v) + { + assert(l < n); + return VcImpl::Proxy<Vc::SimdArray<T, n, V> >{l, v}; + } + + template<class T, std::size_t n, class V> + std::size_t lanes(const Vc::SimdMaskArray<T, n, V> &) + { + return n; + } + + template<class T, std::size_t n, class V> + bool lane(std::size_t l, const Vc::SimdMaskArray<T, n, V> &v) + { + assert(l < n); + return v[l]; + } + + template<class T, std::size_t n, class V> + auto lane(std::size_t l, Vc::SimdMaskArray<T, n, V> &v) + { + assert(l < n); + return VcImpl::Proxy<Vc::SimdMaskArray<T, n, V> >{l, v}; + } #endif // HAVE_VC -//! masked Simd assignment (scalar version) -/** -* Assign \c src to \c dest for those lanes where \c mask is true. -*/ -template<class T> -void assign(T &dst, const T &src, bool mask) -{ -if(mask) dst = src; -} + //! masked Simd assignment (scalar version) + /** + * Assign \c src to \c dest for those lanes where \c mask is true. + */ + template<class T> + void assign(T &dst, const T &src, bool mask) + { + if(mask) dst = src; + } #if HAVE_VC -/* -Add Vc specializations for masked assignment -*/ -template<class T, class A> -void assign(Vc::Vector<T, A> &dst, const Vc::Vector<T, A> &src, -typename Vc::Vector<T, A>::mask_type mask) -{ -dst(mask) = src; -} - -template<class T, std::size_t n, class V> -void assign(Vc::SimdArray<T, n, V> &dst, const Vc::SimdArray<T, n, V> &src, -typename Vc::SimdArray<T, n, V>::mask_type mask) -{ -dst(mask) = src; -} + /* + Add Vc specializations for masked assignment + */ + template<class T, class A> + void assign(Vc::Vector<T, A> &dst, const Vc::Vector<T, A> &src, + typename Vc::Vector<T, A>::mask_type mask) + { + dst(mask) = src; + } + + template<class T, std::size_t n, class V> + void assign(Vc::SimdArray<T, n, V> &dst, const Vc::SimdArray<T, n, V> &src, + typename Vc::SimdArray<T, n, V>::mask_type mask) + { + dst(mask) = src; + } #endif // HAVE_VC -template<class T> -void swap(T &v1, T &v2, bool mask) -{ -using std::swap; -if(mask) swap(v1, v2); -} + template<class T> + void swap(T &v1, T &v2, bool mask) + { + using std::swap; + if(mask) swap(v1, v2); + } #if HAVE_VC -/* -Add Vc specializations for masked swap -*/ -template<class T, class A> -void swap(Vc::Vector<T, A> &v1, Vc::Vector<T, A> &v2, -typename Vc::Vector<T, A>::mask_type mask) -{ -auto tmp = v1; -v1(mask) = v2; -v2(mask) = tmp; -} - -template<class T, std::size_t n, class V> -void swap(Vc::SimdArray<T, n, V> &v1, Vc::SimdArray<T, n, V> &v2, -typename Vc::SimdArray<T, n, V>::mask_type mask) -{ -auto tmp = v1; -v1(mask) = v2; -v2(mask) = tmp; -} + /* + Add Vc specializations for masked swap + */ + template<class T, class A> + void swap(Vc::Vector<T, A> &v1, Vc::Vector<T, A> &v2, + typename Vc::Vector<T, A>::mask_type mask) + { + auto tmp = v1; + v1(mask) = v2; + v2(mask) = tmp; + } + + template<class T, std::size_t n, class V> + void swap(Vc::SimdArray<T, n, V> &v1, Vc::SimdArray<T, n, V> &v2, + typename Vc::SimdArray<T, n, V>::mask_type mask) + { + auto tmp = v1; + v1(mask) = v2; + v2(mask) = tmp; + } #endif // HAVE_VC } // end namespace Dune diff --git a/dune/common/simd/base.hh b/dune/common/simd/base.hh index 6d0f87041700cf29c2f59b61b81bbfebeb170537..6aabb60196d8abf5996f732788f221b2c9dc97b5 100644 --- a/dune/common/simd/base.hh +++ b/dune/common/simd/base.hh @@ -3,217 +3,217 @@ #include <dune/internal/dune-common.hh> /** @file -* @brief Basic definitions for SIMD Implementations -* @ingroup SIMDAbstract -* -* This file provides basic definitions and template declarations that are -* used to write SIMD abtraction layers. -* -* This file should never be included by users of the SIMD -* abstraction. Include <dune/common/simd/simd.hh> instead. -*/ + * @brief Basic definitions for SIMD Implementations + * @ingroup SIMDAbstract + * + * This file provides basic definitions and template declarations that are + * used to write SIMD abtraction layers. + * + * This file should never be included by users of the SIMD + * abstraction. Include <dune/common/simd/simd.hh> instead. + */ /** @defgroup SIMD Vectorization -* @ingroup Common -* @brief Abstractions for using vectorization libraries -* -* This vectorization abstraction targets three kinds of developers: -* -* - Application developers create SIMD types (usually with the help of some -* vectorization library) and pass them to the Dune library. They are -* responsible for a compilation unit, typically a .cc file that is compiled -* into a program or part of a library. Since they create the type, they -* have the knowledge which library abstraction is needed and are -* responsible for including that, as well as making sure the correct -* compiler flags are provided. -* -* - Library developers implement support in Dune for handling SIMD types, -* e.g. by extending some existing class. By using the interfaces provided -* here, they should not have to worry about the exact vectorization library -* beeing used, or whether a vectorization library is used at all. -* -* - Abstraction developers provide the necessary hooks to make a -* vectorization library known to this interface. They are also responsible -* for documenting for application developers how to meet the prerequisites -* for using the abstraction, e.g. which headers to include and how to add -* the necessary compiler flags. -*/ + * @ingroup Common + * @brief Abstractions for using vectorization libraries + * + * This vectorization abstraction targets three kinds of developers: + * + * - Application developers create SIMD types (usually with the help of some + * vectorization library) and pass them to the Dune library. They are + * responsible for a compilation unit, typically a .cc file that is compiled + * into a program or part of a library. Since they create the type, they + * have the knowledge which library abstraction is needed and are + * responsible for including that, as well as making sure the correct + * compiler flags are provided. + * + * - Library developers implement support in Dune for handling SIMD types, + * e.g. by extending some existing class. By using the interfaces provided + * here, they should not have to worry about the exact vectorization library + * beeing used, or whether a vectorization library is used at all. + * + * - Abstraction developers provide the necessary hooks to make a + * vectorization library known to this interface. They are also responsible + * for documenting for application developers how to meet the prerequisites + * for using the abstraction, e.g. which headers to include and how to add + * the necessary compiler flags. + */ /** @defgroup SIMDApp Application Developer's Interface -* @ingroup SIMD -* @brief How to request vectorization from Dune -* -* This module describes how to pass vectorized types to Dune classes. It -* lists the supported vectorization libraries and how to include each -* (although it cannot list those libraries where support is not part of the -* dune core). -*/ + * @ingroup SIMD + * @brief How to request vectorization from Dune + * + * This module describes how to pass vectorized types to Dune classes. It + * lists the supported vectorization libraries and how to include each + * (although it cannot list those libraries where support is not part of the + * dune core). + */ /** @defgroup SIMDLib Library Developer's Interface -* @ingroup SIMD -* @brief How to support vectorization in Dune classes -* -* This module describes how a Dune library developer can add support for -* vectorization to library facilities. -*/ + * @ingroup SIMD + * @brief How to support vectorization in Dune classes + * + * This module describes how a Dune library developer can add support for + * vectorization to library facilities. + */ /** @defgroup SIMDAbstract Abstraction Developer's Interface -* @ingroup SIMD -* @brief How to add support for a new vectorization library -* -* This module describes the interface that you must implement if you want to -* provide an abstraction layer for some vectorization library. To understand -* some of the design choices, have a look at dune/common/simd/DESIGN.md in -* dune-common's source. -* -* Everything an abstraction implementation needs to provide is in namespace -* `Dune::Simd::Overloads`. -* -* An implementation must specialize all the template classes in namespace -* `Overloads` (with the exception of `Overloads::ADLTag`, see below). To -* make it possible for certain specializations not to participate in overload -* resolution, each template class provides a dummy template parameter \c -* SFINAETag that defaults to \c void. -* -* An implementation must overload all functions within namespace `Overloads` -* that are defined deleted. It may overload other functions if the default -* behaviour is not suitable. All functions take a value of type -* `Overloads::ADLTag<priority, true>` as their first argument to enable -* argument-dependent lookup, to be able to prioritize different overloads -* with respect to each other, and to be able to inhibit certain overloads -* from taking part in overload resolution. See the documentation for -* `Overloads::ADLTag` for a detailed explanation. -* -* An abstraction implementation may not specialize `Overloads::ADLTag`, and -* may not introduce new names into namespace `Overloads`. -*/ + * @ingroup SIMD + * @brief How to add support for a new vectorization library + * + * This module describes the interface that you must implement if you want to + * provide an abstraction layer for some vectorization library. To understand + * some of the design choices, have a look at dune/common/simd/DESIGN.md in + * dune-common's source. + * + * Everything an abstraction implementation needs to provide is in namespace + * `Dune::Simd::Overloads`. + * + * An implementation must specialize all the template classes in namespace + * `Overloads` (with the exception of `Overloads::ADLTag`, see below). To + * make it possible for certain specializations not to participate in overload + * resolution, each template class provides a dummy template parameter \c + * SFINAETag that defaults to \c void. + * + * An implementation must overload all functions within namespace `Overloads` + * that are defined deleted. It may overload other functions if the default + * behaviour is not suitable. All functions take a value of type + * `Overloads::ADLTag<priority, true>` as their first argument to enable + * argument-dependent lookup, to be able to prioritize different overloads + * with respect to each other, and to be able to inhibit certain overloads + * from taking part in overload resolution. See the documentation for + * `Overloads::ADLTag` for a detailed explanation. + * + * An abstraction implementation may not specialize `Overloads::ADLTag`, and + * may not introduce new names into namespace `Overloads`. + */ namespace Dune { -namespace Simd { - -//! @brief Namespace for the overloads and specializations that make up a -//! SIMD implementation -/** -* @ingroup SIMDAbstract -* -* This namespace contains three sets of things: the struct ADLTag, which -* is used to look up functions in this namespace using argument-dependet -* lookup, traits classes that must be specialized by abstraction -* implementations, and functions that must/can be overloaded by -* abstraction implementations. -* -* \note Only introduce new names into this namespace to extend the -* interface. This applies in particular to people in the -* "abstraction developer" role; they may meddle in this namespace -* only by providing overloads and/or specializations for existing -* names (and for `ADLTag` even that is prohibited). -*/ -namespace Overloads { - -//! @addtogroup SIMDAbstract -//! @{ - -//! Tag used to force late-binding lookup in Dune::Simd::Overloads -/** -* This tag is used by functions in \c Dune::Simd to make -* argument-dependent lookups (ADL) for functions in \c -* Dune::Simd::Overloads. The property of ADL that is used here is that -* it binds the names of functions late, i.e. at the time of -* instantiation, while all other lookups bind early, i.e. at the time -* when the function call is parsed. Using late binding enables a -* function \c foo() to find a functions \c Overloads::foo() that has -* been declared only after \c foo() itself has been defined: -* -* \code -* template<class V> -* void foo(V v) -* { -* foo(Overloads::ADLTag<6>{}, v); -* } -* -* struct MyType {}; -* namespace Overloads { -* void foo(ADLTag<4>, MyType v); -* } -* \endcode -* -* \note It is generally an error to declare a function with an ADLTag -* argument outside of namespace Simd::Overloads. An exception -* would be an abstraction implementation that declares all its -* implementation functions in its own implementation namespace, -* and then pulls them into the namespace Overloads by way of \c -* using. -* -* `ADLTag<i>` derives from `ADLTag<i-1>`. Thus it is possible to -* prioritize overloads by choosing an appropriate \c i. The following -* values for \c i are predefined: -* - `i==0,1`: these are reserved for the defaults. -* - `i==2,3`: these are reserved for the implementation for standard -* types. -* - `i==5,6`: these should normally be used by other implementations -* -* The lower priority should be used by default. The higher priority -* can be used by an implementation to resolve ambiguities, e.g. between -* an overload with a by-value argument and an overload with an -* lvalue-reference argument. -* -* The folloing priorities should not normally be used. However, they -* may sometimes be necessary: -* - \c i==4: override standard implementation, but prefer other -* implementations -* - \c i==7: try to override other implementations -* -* \c i==7 is the highest supported priority. -* -* The second (bool) template argument is to make writing abstraction -* implementations that use SFINAE to remove (some of) their functions -* from the overload set more concise. \c ADLTag<i,false> is not -* defined, so instead of -* \code -* std::enable_if_t<cond, ADLTag<4> > -* \endcode -* you may write the equivalent -* \code -* ADLTag<4, cond> -* \endcode -*/ -template<unsigned i, bool = true> -struct ADLTag; - -template<unsigned i> -struct ADLTag<i> : ADLTag<i-1> {}; - -template<> -struct ADLTag<0> {}; - -//! should have a member type \c type -/** -* Implements `Simd::Scalar`. `V` will never have cv or reference -* qualifiers, no need to strip those. -*/ -template<class V, class SFINAETag = void> -struct ScalarType; - -//! should have a member type \c type -/** -* Implements `Simd::Rebind`. `V` and `S` will never have cv or -* reference qualifiers, no need to strip those. -*/ -template<class S, class V, class SFINAETag = void> -struct RebindType; - -//! should be derived from a `Dune::index_constant` -/** -* Implements `Simd::lanes()`. `V` will never have cv or reference -* qualifiers, no need to strip those. -*/ -template<class V, class SFINAETag = void> -struct LaneCount; - -//! @} Group SIMDAbstract - -} // namespace Overloads -} // namespace Simd + namespace Simd { + + //! @brief Namespace for the overloads and specializations that make up a + //! SIMD implementation + /** + * @ingroup SIMDAbstract + * + * This namespace contains three sets of things: the struct ADLTag, which + * is used to look up functions in this namespace using argument-dependet + * lookup, traits classes that must be specialized by abstraction + * implementations, and functions that must/can be overloaded by + * abstraction implementations. + * + * \note Only introduce new names into this namespace to extend the + * interface. This applies in particular to people in the + * "abstraction developer" role; they may meddle in this namespace + * only by providing overloads and/or specializations for existing + * names (and for `ADLTag` even that is prohibited). + */ + namespace Overloads { + + //! @addtogroup SIMDAbstract + //! @{ + + //! Tag used to force late-binding lookup in Dune::Simd::Overloads + /** + * This tag is used by functions in \c Dune::Simd to make + * argument-dependent lookups (ADL) for functions in \c + * Dune::Simd::Overloads. The property of ADL that is used here is that + * it binds the names of functions late, i.e. at the time of + * instantiation, while all other lookups bind early, i.e. at the time + * when the function call is parsed. Using late binding enables a + * function \c foo() to find a functions \c Overloads::foo() that has + * been declared only after \c foo() itself has been defined: + * + * \code + * template<class V> + * void foo(V v) + * { + * foo(Overloads::ADLTag<6>{}, v); + * } + * + * struct MyType {}; + * namespace Overloads { + * void foo(ADLTag<4>, MyType v); + * } + * \endcode + * + * \note It is generally an error to declare a function with an ADLTag + * argument outside of namespace Simd::Overloads. An exception + * would be an abstraction implementation that declares all its + * implementation functions in its own implementation namespace, + * and then pulls them into the namespace Overloads by way of \c + * using. + * + * `ADLTag<i>` derives from `ADLTag<i-1>`. Thus it is possible to + * prioritize overloads by choosing an appropriate \c i. The following + * values for \c i are predefined: + * - `i==0,1`: these are reserved for the defaults. + * - `i==2,3`: these are reserved for the implementation for standard + * types. + * - `i==5,6`: these should normally be used by other implementations + * + * The lower priority should be used by default. The higher priority + * can be used by an implementation to resolve ambiguities, e.g. between + * an overload with a by-value argument and an overload with an + * lvalue-reference argument. + * + * The folloing priorities should not normally be used. However, they + * may sometimes be necessary: + * - \c i==4: override standard implementation, but prefer other + * implementations + * - \c i==7: try to override other implementations + * + * \c i==7 is the highest supported priority. + * + * The second (bool) template argument is to make writing abstraction + * implementations that use SFINAE to remove (some of) their functions + * from the overload set more concise. \c ADLTag<i,false> is not + * defined, so instead of + * \code + * std::enable_if_t<cond, ADLTag<4> > + * \endcode + * you may write the equivalent + * \code + * ADLTag<4, cond> + * \endcode + */ + template<unsigned i, bool = true> + struct ADLTag; + + template<unsigned i> + struct ADLTag<i> : ADLTag<i-1> {}; + + template<> + struct ADLTag<0> {}; + + //! should have a member type \c type + /** + * Implements `Simd::Scalar`. `V` will never have cv or reference + * qualifiers, no need to strip those. + */ + template<class V, class SFINAETag = void> + struct ScalarType; + + //! should have a member type \c type + /** + * Implements `Simd::Rebind`. `V` and `S` will never have cv or + * reference qualifiers, no need to strip those. + */ + template<class S, class V, class SFINAETag = void> + struct RebindType; + + //! should be derived from a `Dune::index_constant` + /** + * Implements `Simd::lanes()`. `V` will never have cv or reference + * qualifiers, no need to strip those. + */ + template<class V, class SFINAETag = void> + struct LaneCount; + + //! @} Group SIMDAbstract + + } // namespace Overloads + } // namespace Simd } // namespace Dune #endif // DUNE_COMMON_SIMD_BASE_HH diff --git a/dune/common/simd/defaults.hh b/dune/common/simd/defaults.hh index 677e212e2deed4e952624ae25c2bd13c89939101..819817cd24c36c2f97379917af60a9010e1aaaad 100644 --- a/dune/common/simd/defaults.hh +++ b/dune/common/simd/defaults.hh @@ -3,15 +3,15 @@ #include <dune/internal/dune-common.hh> /** @file -* @brief Default implementations for SIMD Implementations -* @ingroup SIMDAbstract -* -* This file provides default overloads for SIMD implementation functions, and -* deleted placeholders where there are no default implementations. -* -* This file should never be included by users of the SIMD -* abstraction. Include <dune/common/simd/simd.hh> instead. -*/ + * @brief Default implementations for SIMD Implementations + * @ingroup SIMDAbstract + * + * This file provides default overloads for SIMD implementation functions, and + * deleted placeholders where there are no default implementations. + * + * This file should never be included by users of the SIMD + * abstraction. Include <dune/common/simd/simd.hh> instead. + */ #include <algorithm> #include <cstddef> @@ -24,164 +24,164 @@ #include <dune/common/typetraits.hh> namespace Dune { -namespace Simd { -namespace Overloads { - -/** -* @addtogroup SIMDAbstract -* @{ -*/ - -/** @name Overloadable and default functions -* -* This group contains functions that you, as an abstraction developer, -* must implement. All functions that are deleted must be provided, -* functions that have a default implementation may be left -* unimplemented if the default behaviour is satisfactory. -* -* @{ -*/ - -//! implements Simd::lane() -template<class V> -decltype(auto) lane(ADLTag<0>, std::size_t l, V v) = delete; - -//! implements Simd::implCast<V>(V) -template<class V> -constexpr V implCast(ADLTag<0>, MetaType<V>, const V &u) -{ -return u; -} - -//! implements Simd::implCast<V>(U) -template<class V, class U> -constexpr V implCast(ADLTag<0>, MetaType<V>, const U &u) -{ -V result(Simd::Scalar<V>(0)); -for(auto l : range(Simd::lanes(u))) -Simd::lane(l, result) = Simd::lane(l, u); -return result; -} - -//! implements Simd::broadcast<V>() -template<class V, class S> -auto broadcast(ADLTag<0>, MetaType<V>, S s) -{ -return V(Simd::Scalar<V>(s)); -} - -//! implements Simd::cond() -template<class V> -V cond(ADLTag<0>, const Mask<V> &mask, -const V &ifTrue, const V &ifFalse) = delete; - -//! implements binary Simd::max() -template<class V> -auto max(ADLTag<0>, const V &v1, const V &v2) -{ -using std::max; -return max(v1, v2); -} - -//! implements binary Simd::min() -template<class V> -auto min(ADLTag<0>, const V &v1, const V &v2) -{ -using std::min; -return min(v1, v2); -} - -//! implements Simd::anyTrue() -template<class Mask> -bool anyTrue(ADLTag<0>, const Mask &mask) = delete; - -//! implements Simd::allTrue() -/** -* Default uses Simd::anyTrue() -*/ -template<class Mask> -bool allTrue(ADLTag<0>, const Mask &mask) -{ -return !Dune::Simd::anyTrue(!mask); -} - -//! implements Simd::anyFalse() -/** -* Default uses Simd::anyTrue() -*/ -template<class Mask> -bool anyFalse(ADLTag<0>, const Mask &mask) -{ -return Dune::Simd::anyTrue(!mask); -} - -//! implements Simd::allFalse() -/** -* Default uses Simd::anyTrue() -*/ -template<class Mask> -bool allFalse(ADLTag<0>, const Mask &mask) -{ -return !Dune::Simd::anyTrue(mask); -} - -//! implements Simd::maxValue() -template<class V> -auto max(ADLTag<0>, const V &v) -{ -Scalar<V> m = Simd::lane(0, v); -for(std::size_t l = 1; l < Simd::lanes(v); ++l) -if(m < Simd::lane(l, v)) -m = Simd::lane(l, v); -return m; -} - -//! implements Simd::minValue() -template<class V> -auto min(ADLTag<0>, const V &v) -{ -Scalar<V> m = Simd::lane(0, v); -for(std::size_t l = 1; l < Simd::lanes(v); ++l) -if(Simd::lane(l, v) < m) -m = Simd::lane(l, v); -return m; -} - -//! implements Simd::mask() -template<class V> -Mask<V> mask(ADLTag<0, std::is_same<V, Mask<V> >::value>, -const V &v) -{ -return v; -} - -//! implements Simd::mask() -template<class V> -auto mask(ADLTag<0, !std::is_same<V, Mask<V> >::value>, -const V &v) -{ -using Copy = AutonomousValue<V>; // just in case we are handed a proxy -return v != Copy(Scalar<Copy>(0)); -} - -//! implements Simd::maskOr() -template<class V1, class V2> -auto maskOr(ADLTag<0>, const V1 &v1, const V2 &v2) -{ -return Simd::mask(v1) || Simd::mask(v2); -} - -//! implements Simd::maskAnd() -template<class V1, class V2> -auto maskAnd(ADLTag<0>, const V1 &v1, const V2 &v2) -{ -return Simd::mask(v1) && Simd::mask(v2); -} - -//! @} Overloadable and default functions -//! @} Group SIMDAbstract -} // namespace Overloads -} // namespace Simd + namespace Simd { + namespace Overloads { + + /** + * @addtogroup SIMDAbstract + * @{ + */ + + /** @name Overloadable and default functions + * + * This group contains functions that you, as an abstraction developer, + * must implement. All functions that are deleted must be provided, + * functions that have a default implementation may be left + * unimplemented if the default behaviour is satisfactory. + * + * @{ + */ + + //! implements Simd::lane() + template<class V> + decltype(auto) lane(ADLTag<0>, std::size_t l, V v) = delete; + + //! implements Simd::implCast<V>(V) + template<class V> + constexpr V implCast(ADLTag<0>, MetaType<V>, const V &u) + { + return u; + } + + //! implements Simd::implCast<V>(U) + template<class V, class U> + constexpr V implCast(ADLTag<0>, MetaType<V>, const U &u) + { + V result(Simd::Scalar<V>(0)); + for(auto l : range(Simd::lanes(u))) + Simd::lane(l, result) = Simd::lane(l, u); + return result; + } + + //! implements Simd::broadcast<V>() + template<class V, class S> + auto broadcast(ADLTag<0>, MetaType<V>, S s) + { + return V(Simd::Scalar<V>(s)); + } + + //! implements Simd::cond() + template<class V> + V cond(ADLTag<0>, const Mask<V> &mask, + const V &ifTrue, const V &ifFalse) = delete; + + //! implements binary Simd::max() + template<class V> + auto max(ADLTag<0>, const V &v1, const V &v2) + { + using std::max; + return max(v1, v2); + } + + //! implements binary Simd::min() + template<class V> + auto min(ADLTag<0>, const V &v1, const V &v2) + { + using std::min; + return min(v1, v2); + } + + //! implements Simd::anyTrue() + template<class Mask> + bool anyTrue(ADLTag<0>, const Mask &mask) = delete; + + //! implements Simd::allTrue() + /** + * Default uses Simd::anyTrue() + */ + template<class Mask> + bool allTrue(ADLTag<0>, const Mask &mask) + { + return !Dune::Simd::anyTrue(!mask); + } + + //! implements Simd::anyFalse() + /** + * Default uses Simd::anyTrue() + */ + template<class Mask> + bool anyFalse(ADLTag<0>, const Mask &mask) + { + return Dune::Simd::anyTrue(!mask); + } + + //! implements Simd::allFalse() + /** + * Default uses Simd::anyTrue() + */ + template<class Mask> + bool allFalse(ADLTag<0>, const Mask &mask) + { + return !Dune::Simd::anyTrue(mask); + } + + //! implements Simd::maxValue() + template<class V> + auto max(ADLTag<0>, const V &v) + { + Scalar<V> m = Simd::lane(0, v); + for(std::size_t l = 1; l < Simd::lanes(v); ++l) + if(m < Simd::lane(l, v)) + m = Simd::lane(l, v); + return m; + } + + //! implements Simd::minValue() + template<class V> + auto min(ADLTag<0>, const V &v) + { + Scalar<V> m = Simd::lane(0, v); + for(std::size_t l = 1; l < Simd::lanes(v); ++l) + if(Simd::lane(l, v) < m) + m = Simd::lane(l, v); + return m; + } + + //! implements Simd::mask() + template<class V> + Mask<V> mask(ADLTag<0, std::is_same<V, Mask<V> >::value>, + const V &v) + { + return v; + } + + //! implements Simd::mask() + template<class V> + auto mask(ADLTag<0, !std::is_same<V, Mask<V> >::value>, + const V &v) + { + using Copy = AutonomousValue<V>; // just in case we are handed a proxy + return v != Copy(Scalar<Copy>(0)); + } + + //! implements Simd::maskOr() + template<class V1, class V2> + auto maskOr(ADLTag<0>, const V1 &v1, const V2 &v2) + { + return Simd::mask(v1) || Simd::mask(v2); + } + + //! implements Simd::maskAnd() + template<class V1, class V2> + auto maskAnd(ADLTag<0>, const V1 &v1, const V2 &v2) + { + return Simd::mask(v1) && Simd::mask(v2); + } + + //! @} Overloadable and default functions + //! @} Group SIMDAbstract + } // namespace Overloads + } // namespace Simd } // namespace Dune #endif // DUNE_COMMON_SIMD_DEFAULTS_HH diff --git a/dune/common/simd/interface.hh b/dune/common/simd/interface.hh index 3c785a28f0f5282118d1d7df308886c9cd675cb9..f36dc57d84a397a65500138879630b72d7d9426b 100644 --- a/dune/common/simd/interface.hh +++ b/dune/common/simd/interface.hh @@ -3,15 +3,15 @@ #include <dune/internal/dune-common.hh> /** @file -* @brief User interface of the SIMD abstraction -* @ingroup SIMDLib -* -* This file provides the user interface functions of the SIMD abstraction -* layer. -* -* This file should never be included by users of the SIMD -* abstraction. Include <dune/common/simd/simd.hh> instead. -*/ + * @brief User interface of the SIMD abstraction + * @ingroup SIMDLib + * + * This file provides the user interface functions of the SIMD abstraction + * layer. + * + * This file should never be included by users of the SIMD + * abstraction. Include <dune/common/simd/simd.hh> instead. + */ #include <cassert> #include <cstddef> @@ -23,522 +23,522 @@ namespace Dune { -//! @brief Namespace for vectorization interface functions used by library -//! developers -/** -* @ingroup SIMDLib -*/ -namespace Simd { - -/** @addtogroup SIMDLib -* -* @{ -* -* @section understand_simd Understanding SIMD types -* -* The (idealized) model of a SIMD type `V` used in this abstraction layer -* is that they are fixed-length vectors of some scalar type `S`. -* Operations and operators that take values of type `S` as arguments, -* except for `operator,()`, should be overloaded to support values of -* type `V` too. These operations should apply element-wise. If the -* operation takes more than one argument, it should accept arbitrary -* combinations of `V` and `S`. The exception is the combination of `S` -* on the left hand side and `V` on the right hand side of one of the -* assignment operators, which does not make sense. -* -* The result of a boolean operation is a mask type `M`, which is a SIMD -* type with scalar type `bool` with the same number of elements as `V`. -* The result of all other operations is again of type `V`, or of some -* type convertible to `V`. -* -* This is very similar to `std::valarray`, with the main difference -* being that `std::valarray` is dynamic in size, while for this -* abstraction the size is static. -* -* @section SIMDLibPromoWarn Type promotion issues -* -* True SIMD types have an issue with type promotion, which means they -* cannot behave completely analogous to built-in integral types (this is -* a non-issue with floating point types). Essentially, operations on -* true SIMD types cannot promote their arguments, because the promoted -* types typically require more storage than the original types, meaning -* an argument that was passed in a single vector register would need -* multiple vector registers after promotion, which would mean greater -* register pressure. Also, there would be conversion operations -* required, which (at least on x86) is not typically the case for -* promotions of the built-in types. Lastly, with larger types the vector -* units can typically operate on fewer lanes at a time. -* -* Omitting integral promotions has in many cases no negative impact, -* because many programmers do not really expect them anyway. There are -* however cases where they matter, and for illustration I want to explain -* one that crept up during unit testing. -* -* Here is a simplified (and somewhat pseudo-code) version of the test. -* The test checks the result of unary `-` on `Vc::Vector<unsigned short>` -* by comparing the result of unary `-` when applied to the complete -* vector to the result of unary `-` when applied to each lane -* individually. -* \code -* Vc::Vector<unsigned short> varg; -* for(std::size_t l = 0; l < lanes(varg); ++l) -* lane(l, varg) = l + 1; -* auto vresult = -varg; -* for(std::size_t l = 0; l < lanes(varg); ++l) -* assert(lane(l, vresult) == -lane(l, varg)); -* \endcode -* The test fails in lane 0. On the left side of the `==`, `lane(0, -* vresult)` is `(unsigned short)65535`, which is the same as `(unsigned -* short)-1`, as it should be. On the right side, `lane(0, varg)` is -* `(unsigned short)1`. `-` promotes its argument, so that becomes -* `(int)1`, and the result of the negation is `(int)-1`. -* -* Now the comparison is `(unsigned short)65535 == (int)-1`. The -* comparison operator applies the *usual arithmetic conversions* to bring -* both operands to the same type. In this case this boils down to -* converting the left side to `int` via integral promotions and the -* comparison becomes `(int)65535 == (int)-1`. The result is of course -* `false` and the assertion triggers. -* -* The only way to thoroughly prevent this kind of problem is to convert -* the result of any operation back to the expected type. In the above -* example, the assertion would need to be written as `assert(lane(l, -* vresult) == static_cast<unsigned short>(-lane(l, varg)));`. In -* practice, this should only be a problem with operations on unsigned -* types where the result may be "negative". Most code in Dune will want -* to operate on floating point types, where this is a non-issue. -* -* (Of couse, this is also a problem for code that operates on untrusted -* input, but you should not be doing that with Dune anyway). -* -* Still, when writing code using the SIMD abstractions, you should be -* aware that in the following snippet -* \code -* auto var1 = lane(0, -vec); -* auto var2 = -lane(0, vec); -* \endcode -* the exact types of `var1` and `var2` may be somewhat surprising. -* -* @section simd_abstraction_limit Limitations of the Abstraction Layer -* -* Since the abstraction layer cannot overload operators of SIMD types -* (that would be meddling with the domain of the library that provides -* the SIMD types), nor provide it's own constructors, there are severe -* limitations in what the abstraction layer guarantees. Besides the -* standard types, the first SIMD library supported is Vc, so that is -* where most of the limitations stem from; see \ref SIMDVcRestrictions in -* \ref SIMDVc. -* -* The biggest limitations are with masks. In Vc masks support a very -* restricted set of operations compared to other SIMD types, so in what -* follows we will distinguish between masks with a very small set of -* operations and between vectors with a larger set of operations. -* -* Here is a compact table of the limitations as a quick reference, -* together with suggested workarounds for the constructs that don't work. -* `s` denotes a scalar object/expression (i.e. of type `double` or in the -* case of masks `bool`). `v` denotes a vector/mask object/expression. -* `sv` means that both scalar and vector arguments are accepted. `V` -* denotes a vector/mask type. `@` means any applicable operator that is -* not otherwise listed. -* -* <!-- The following table is in orgtbl format -- If you are using emacs, -* you may want to enable the `orgtbl` minor mode. We substitute `|` -* with `¦` when describing or-operators so as to not confuse -* orgtbl. --> -* \code -| | Vectors | workaround | Masks | workaround | -|-------------------------+---------+----------------------------+-------------+------------------| -| V v(s); | y | | y | | -| V v = s; | y | V v(s); | *N* | V v(s); | -| V v{s}; | *N* | V v(s); | y | V v(s); | -| V v = {s}; | *N* | V v(s); | y | V v(s); | -|-------------------------+---------+----------------------------+-------------+------------------| -| v = s; | y | v = V(s); | *N* | v = V(s); | -| v = {s}; | *N* | v = V(s); | *N* | v = V(s); | -|-------------------------+---------+----------------------------+-------------+------------------| -| v++; ++v; | *N* | v += Scalar<V>(1); | *N*(n/a)[2] | v = V(true); | -| v--; --v; | *N* | v -= Scalar<V>(1); | n/a | | -|-------------------------+---------+----------------------------+-------------+------------------| -| +v; -v; | y | | *N* | none | -| !v; | y | | y | | -| ~v; | y | | *N* | none | -|-------------------------+---------+----------------------------+-------------+------------------| -| sv @ sv; but see below | y | | *N* | none | -|-------------------------+---------+----------------------------+-------------+------------------| -| s << v; s >> v; | *N* | v << V(s); | *N* | none | -|-------------------------+---------+----------------------------+-------------+------------------| -| v == v; v != v; | y | | *N* [1] | !(v ^ v); v ^ v; | -|-------------------------+---------+----------------------------+-------------+------------------| -| v & v; v ^ v; v ¦ v; | y | | y | | -| v && v; v ¦¦ v; | *N* | maskAnd(v,v); maskOr(v,v); | y | | -|-------------------------+---------+----------------------------+-------------+------------------| -| v @= sv; but see below | y | | *N* | none | -| v &= v; v ^= v; v ¦= v; | y | | y | | -|-------------------------+---------+----------------------------+-------------+------------------| -| v, v;[3,4] | *N* | void(v), v; | y | | -* \endcode -* -* Notes: -* -* - [1] In Vc, mask-mask `==` and `!=` operations exist, but the result -* is of type `bool`, i.e. a scalar. -* -* - [2] `++` (either kind) on bools is deprecated by the standard. Our -* test suite does not check for it on masks, but it was supported by Vc -* masks at some point. -* -* - [3] Contrary to the other operators, the expected result for `(sv1, -* sv2)` is exactly `sv2`, no broadcasting applied. -* -* - [4] Try to avoid the use of `operator,` unless both operands are -* built-in types if possible. Libraries had a tendency to overload -* `operator,` to provide for things like container initialization -* before C++11, and these overloads may still be present in the library -* you are using and replace the default meaning of `operator,`. -* -* Support levels: -* -* - `y`: operation generally works; some instances of the operation may -* not apply -* -* - `*N*`: operation generally does not work; some instances of the -* operation may not apply -* -* - `n/a`: operation does not apply (i.e. bitwise operations to -* floating-point operands, `--` (and in the future possibly `++`) to -* boolean operands, assignment operators to scalar left hand sides) -*/ - -/** @name Basic interface -* -* Templates and functions in this group are directly implemented by -* templates and functions in namespace Overloads. -* -* @{ -*/ - -//! Element type of some SIMD type -/** -* \tparam V The SIMD (mask or vector) type. `const`, `volatile` or -* reference qualifiers are automatically ignored. -* -* Not all operations that access the element of a vector return (a -* reference to) the scalar type -- some may return proxy objects instead. -* Use `autoCopy()` to make sure you are getting a prvalue of the scalar -* type. -* -* Implemented by `Overloads::ScalarType`. -*/ -template<class V> -using Scalar = typename Overloads::ScalarType<std::decay_t<V> >::type; - -//! Construct SIMD type with different scalar type -/** -* \tparam S The new scalar type -* \tparam V The SIMD (mask or vector) type. -* -* The resulting type a SIMD vector of `S` with the same number of lanes -* as `V`. `const`, `volatile` or reference qualifiers in `S` and `V` are -* automatically ignored, and the result will have no such qualifiers. -* -* Implementations shall rebind to `LoopSIMD<S, lanes<V>()>` if they can't -* support a particular rebind natively. -* -* Implemented by `Overloads::RebindType`. -*/ -template<class S, class V> -using Rebind = -typename Overloads::RebindType<std::decay_t<S>, std::decay_t<V>>::type; - -//! @} group Basic interface - -/** @name Syntactic Sugar -* -* Templates and functions in this group provide syntactic sugar, they are -* implemented using the functionality from @ref SimdInterfaceBase, and -* are not customizable by implementations. -* -* @{ -*/ - -//! Mask type type of some SIMD type -/** -* \tparam V The SIMD (mask or vector) type. `const`, `volatile` or -* reference qualifiers are automatically ignored. -* -* The mask type is kind of a SIMD vector of `bool` with the same number -* of lanes as `V`. It results from comparison operations between values -* of type `V`. It is only "kind of" a SIMD vector, because the -* guaranteed supported operations are extremely limited. At the moment -* only the logical operators `&&`, `||` and `!` and the "bitwise" -* operators `&`, `^` and `|` between masks are supported, and even with -* those operators you cannot rely on automatic broadcasting of `bool` -* values. -* -* \note In particular, masks do not support comparison. As a workaround -* you can use `^` instead of `!=` and `!(m1 ^ m2)` instead of `m1 -* == m2`. (The reason why comparison is not supported is because -* in Vc `==` and `!=` between masks yield a single `bool` result -* and not a mask.) -* -* This is an alias for `Rebind<bool, V>`. -*/ -template<class V> -using Mask = Rebind<bool, V>; - -//! @} group Syntactic Sugar - -/** @name Basic interface -* @{ -*/ - -//! Number of lanes in a SIMD type -/** -* \tparam V The SIMD (mask or vector) type. `const`, `volatile` -* or reference qualifiers are automatically ignored. -* -* Implemented by `Overloads::LaneCount`. -*/ -template<class V> -constexpr std::size_t lanes() -{ -return Overloads::LaneCount<std::decay_t<V>>::value; -} - -//! Extract an element of a SIMD type -/** -* \param l Number of lane to extract -* \param v SIMD object to extract from -* -* \return If `v` is a non-`const` lvalue, a reference -* `Scalar<decay_t<V>>&`, or a proxy object through which the -* element of `v` may be modified. Overwise, `v` is a `const` -* lvalue or an rvalue, and the result is a prvalue (a temporary) -* of type `Scalar<decay_t<V>>`. -* -* Implemented by `Overloads::lane()`. -*/ -template<class V> -decltype(auto) lane(std::size_t l, V &&v) -{ -assert(l < lanes<V>()); -return lane(Overloads::ADLTag<7>{}, l, std::forward<V>(v)); -} - -//! Cast an expression from one implementation to another -/** -* Implemented by `Overloads::implCast()` -* -* Requires the scalar type and the number of lanes to match exactly. -* -* This is particularly useful for masks, which often know the type they -* were derived from. This can become a problem when doing a conditional -* operation e.g. on some floating point vector type, but with a mask -* derived from some index vector type. -* -* \note One of the few functions that explicitly take a template -* argument (`V` in this case). -*/ -template<class V, class U> -constexpr V implCast(U &&u) -{ -static_assert(std::is_same<Scalar<V>, Scalar<U> >::value, -"Scalar types must match exactly in implCast"); -static_assert(lanes<V>() == lanes<U>(), -"Number of lanes must match in implCast"); -return implCast(Overloads::ADLTag<7>{}, MetaType<std::decay_t<V> >{}, -std::forward<U>(u)); -} - -//! Broadcast a scalar to a vector explicitly -/** -* Implemented by `Overloads::broadcast()` -* -* This is useful because the syntax for broadcasting can vary wildly -* between implementations. -* -* \note One of the few functions that explicitly take a template -* argument (`V` in this case). -*/ -template<class V, class S> -constexpr V broadcast(S s) -{ -return broadcast(Overloads::ADLTag<7>{}, MetaType<std::decay_t<V> >{}, -std::move(s)); -} - -//! Like the ?: operator -/** -* Equivalent to -* \code -* V result; -* for(std::size_t l = 0; l < lanes(mask); ++l) -* lane(l, result) = -* ( lane(l, mask) ? lane(l, ifTrue) : lane(l ifFalse) ); -* return result; -* \endcode -* -* Implemented by `Overloads::cond()`. -*/ -template<class M, class V> -V cond(M &&mask, const V &ifTrue, const V &ifFalse) -{ -return cond(Overloads::ADLTag<7>{}, -implCast<Mask<V> >(std::forward<M>(mask)), ifTrue, ifFalse); -} - -//! Like the ?: operator -/** -* Overload for plain bool masks, accepting any simd type -* -* Implemented by `Overloads::cond()`. -*/ -template<class V> -V cond(bool mask, const V &ifTrue, const V &ifFalse) -{ -return mask ? ifTrue : ifFalse; -} - -//! The binary maximum value over two simd objects -/** -* Implemented by `Overloads::max()`. -*/ -template<class V> -auto max(const V &v1, const V &v2) -{ -return max(Overloads::ADLTag<7>{}, v1, v2); -} - -//! The binary minimum value over two simd objects -/** -* Implemented by `Overloads::min()`. -*/ -template<class V> -auto min(const V &v1, const V &v2) -{ -return min(Overloads::ADLTag<7>{}, v1, v2); -} - -//! Whether any entry is `true` -/** -* Implemented by `Overloads::anyTrue()`. -*/ -template<class Mask> -bool anyTrue(const Mask &mask) -{ -return anyTrue(Overloads::ADLTag<7>{}, mask); -} - -//! Whether all entries are `true` -/** -* Implemented by `Overloads::allTrue()`. -*/ -template<class Mask> -bool allTrue(const Mask &mask) -{ -return allTrue(Overloads::ADLTag<7>{}, mask); -} - -//! Whether any entry is `false` -/** -* Implemented by `Overloads::anyFalse()`. -*/ -template<class Mask> -bool anyFalse(const Mask &mask) -{ -return anyFalse(Overloads::ADLTag<7>{}, mask); -} - -//! Whether all entries are `false` -/** -* Implemented by `Overloads::allFalse()`. -*/ -template<class Mask> -bool allFalse(const Mask &mask) -{ -return allFalse(Overloads::ADLTag<7>{}, mask); -} - -//! The horizontal maximum value over all lanes -/** -* Implemented by `Overloads::max()`. -*/ -template<class V> -Scalar<V> max(const V &v) -{ -return max(Overloads::ADLTag<7>{}, v); -} - -//! The horizontal minimum value over all lanes -/** -* Implemented by `Overloads::min()`. -*/ -template<class V> -Scalar<V> min(const V &v) -{ -return min(Overloads::ADLTag<7>{}, v); -} - -//! Convert to mask, analogue of bool(s) for scalars -/** -* Implemented by `Overloads::mask()`. -*/ -template<class V> -auto mask(const V &v) -{ -return mask(Overloads::ADLTag<7>{}, v); -} - -//! Logic or of masks -/** -* Implemented by `Overloads::maskOr()`. -*/ -template<class V1, class V2> -auto maskOr(const V1 &v1, const V2 &v2) -{ -return maskOr(Overloads::ADLTag<7>{}, v1, v2); -} - -//! Logic and of masks -/** -* Implemented by `Overloads::maskAnd()`. -*/ -template<class V1, class V2> -auto maskAnd(const V1 &v1, const V2 &v2) -{ -return maskAnd(Overloads::ADLTag<7>{}, v1, v2); -} - -//! @} group Basic interface - -/** @name Syntactic Sugar -* -* Templates and functions in this group provide syntactic sugar, they are -* implemented using the functionality from @ref SimdInterfaceBase, and -* are not customizable by implementations. -* -* @{ -*/ - -//! Number of lanes in a SIMD type -/** -* \tparam V The SIMD (mask or vector) type. -* -* The value of the parameter is ignored; the call is simply forwarded to -* `lanes<V>()`. -*/ -template<class V> -std::size_t lanes(const V &) -{ -return lanes<V>(); -} - -//! @} group Syntactic Sugar - -//! @} Group SIMDLib - -} // namespace Simd + //! @brief Namespace for vectorization interface functions used by library + //! developers + /** + * @ingroup SIMDLib + */ + namespace Simd { + + /** @addtogroup SIMDLib + * + * @{ + * + * @section understand_simd Understanding SIMD types + * + * The (idealized) model of a SIMD type `V` used in this abstraction layer + * is that they are fixed-length vectors of some scalar type `S`. + * Operations and operators that take values of type `S` as arguments, + * except for `operator,()`, should be overloaded to support values of + * type `V` too. These operations should apply element-wise. If the + * operation takes more than one argument, it should accept arbitrary + * combinations of `V` and `S`. The exception is the combination of `S` + * on the left hand side and `V` on the right hand side of one of the + * assignment operators, which does not make sense. + * + * The result of a boolean operation is a mask type `M`, which is a SIMD + * type with scalar type `bool` with the same number of elements as `V`. + * The result of all other operations is again of type `V`, or of some + * type convertible to `V`. + * + * This is very similar to `std::valarray`, with the main difference + * being that `std::valarray` is dynamic in size, while for this + * abstraction the size is static. + * + * @section SIMDLibPromoWarn Type promotion issues + * + * True SIMD types have an issue with type promotion, which means they + * cannot behave completely analogous to built-in integral types (this is + * a non-issue with floating point types). Essentially, operations on + * true SIMD types cannot promote their arguments, because the promoted + * types typically require more storage than the original types, meaning + * an argument that was passed in a single vector register would need + * multiple vector registers after promotion, which would mean greater + * register pressure. Also, there would be conversion operations + * required, which (at least on x86) is not typically the case for + * promotions of the built-in types. Lastly, with larger types the vector + * units can typically operate on fewer lanes at a time. + * + * Omitting integral promotions has in many cases no negative impact, + * because many programmers do not really expect them anyway. There are + * however cases where they matter, and for illustration I want to explain + * one that crept up during unit testing. + * + * Here is a simplified (and somewhat pseudo-code) version of the test. + * The test checks the result of unary `-` on `Vc::Vector<unsigned short>` + * by comparing the result of unary `-` when applied to the complete + * vector to the result of unary `-` when applied to each lane + * individually. + * \code + * Vc::Vector<unsigned short> varg; + * for(std::size_t l = 0; l < lanes(varg); ++l) + * lane(l, varg) = l + 1; + * auto vresult = -varg; + * for(std::size_t l = 0; l < lanes(varg); ++l) + * assert(lane(l, vresult) == -lane(l, varg)); + * \endcode + * The test fails in lane 0. On the left side of the `==`, `lane(0, + * vresult)` is `(unsigned short)65535`, which is the same as `(unsigned + * short)-1`, as it should be. On the right side, `lane(0, varg)` is + * `(unsigned short)1`. `-` promotes its argument, so that becomes + * `(int)1`, and the result of the negation is `(int)-1`. + * + * Now the comparison is `(unsigned short)65535 == (int)-1`. The + * comparison operator applies the *usual arithmetic conversions* to bring + * both operands to the same type. In this case this boils down to + * converting the left side to `int` via integral promotions and the + * comparison becomes `(int)65535 == (int)-1`. The result is of course + * `false` and the assertion triggers. + * + * The only way to thoroughly prevent this kind of problem is to convert + * the result of any operation back to the expected type. In the above + * example, the assertion would need to be written as `assert(lane(l, + * vresult) == static_cast<unsigned short>(-lane(l, varg)));`. In + * practice, this should only be a problem with operations on unsigned + * types where the result may be "negative". Most code in Dune will want + * to operate on floating point types, where this is a non-issue. + * + * (Of couse, this is also a problem for code that operates on untrusted + * input, but you should not be doing that with Dune anyway). + * + * Still, when writing code using the SIMD abstractions, you should be + * aware that in the following snippet + * \code + * auto var1 = lane(0, -vec); + * auto var2 = -lane(0, vec); + * \endcode + * the exact types of `var1` and `var2` may be somewhat surprising. + * + * @section simd_abstraction_limit Limitations of the Abstraction Layer + * + * Since the abstraction layer cannot overload operators of SIMD types + * (that would be meddling with the domain of the library that provides + * the SIMD types), nor provide it's own constructors, there are severe + * limitations in what the abstraction layer guarantees. Besides the + * standard types, the first SIMD library supported is Vc, so that is + * where most of the limitations stem from; see \ref SIMDVcRestrictions in + * \ref SIMDVc. + * + * The biggest limitations are with masks. In Vc masks support a very + * restricted set of operations compared to other SIMD types, so in what + * follows we will distinguish between masks with a very small set of + * operations and between vectors with a larger set of operations. + * + * Here is a compact table of the limitations as a quick reference, + * together with suggested workarounds for the constructs that don't work. + * `s` denotes a scalar object/expression (i.e. of type `double` or in the + * case of masks `bool`). `v` denotes a vector/mask object/expression. + * `sv` means that both scalar and vector arguments are accepted. `V` + * denotes a vector/mask type. `@` means any applicable operator that is + * not otherwise listed. + * + * <!-- The following table is in orgtbl format -- If you are using emacs, + * you may want to enable the `orgtbl` minor mode. We substitute `|` + * with `¦` when describing or-operators so as to not confuse + * orgtbl. --> + * \code + | | Vectors | workaround | Masks | workaround | + |-------------------------+---------+----------------------------+-------------+------------------| + | V v(s); | y | | y | | + | V v = s; | y | V v(s); | *N* | V v(s); | + | V v{s}; | *N* | V v(s); | y | V v(s); | + | V v = {s}; | *N* | V v(s); | y | V v(s); | + |-------------------------+---------+----------------------------+-------------+------------------| + | v = s; | y | v = V(s); | *N* | v = V(s); | + | v = {s}; | *N* | v = V(s); | *N* | v = V(s); | + |-------------------------+---------+----------------------------+-------------+------------------| + | v++; ++v; | *N* | v += Scalar<V>(1); | *N*(n/a)[2] | v = V(true); | + | v--; --v; | *N* | v -= Scalar<V>(1); | n/a | | + |-------------------------+---------+----------------------------+-------------+------------------| + | +v; -v; | y | | *N* | none | + | !v; | y | | y | | + | ~v; | y | | *N* | none | + |-------------------------+---------+----------------------------+-------------+------------------| + | sv @ sv; but see below | y | | *N* | none | + |-------------------------+---------+----------------------------+-------------+------------------| + | s << v; s >> v; | *N* | v << V(s); | *N* | none | + |-------------------------+---------+----------------------------+-------------+------------------| + | v == v; v != v; | y | | *N* [1] | !(v ^ v); v ^ v; | + |-------------------------+---------+----------------------------+-------------+------------------| + | v & v; v ^ v; v ¦ v; | y | | y | | + | v && v; v ¦¦ v; | *N* | maskAnd(v,v); maskOr(v,v); | y | | + |-------------------------+---------+----------------------------+-------------+------------------| + | v @= sv; but see below | y | | *N* | none | + | v &= v; v ^= v; v ¦= v; | y | | y | | + |-------------------------+---------+----------------------------+-------------+------------------| + | v, v;[3,4] | *N* | void(v), v; | y | | + * \endcode + * + * Notes: + * + * - [1] In Vc, mask-mask `==` and `!=` operations exist, but the result + * is of type `bool`, i.e. a scalar. + * + * - [2] `++` (either kind) on bools is deprecated by the standard. Our + * test suite does not check for it on masks, but it was supported by Vc + * masks at some point. + * + * - [3] Contrary to the other operators, the expected result for `(sv1, + * sv2)` is exactly `sv2`, no broadcasting applied. + * + * - [4] Try to avoid the use of `operator,` unless both operands are + * built-in types if possible. Libraries had a tendency to overload + * `operator,` to provide for things like container initialization + * before C++11, and these overloads may still be present in the library + * you are using and replace the default meaning of `operator,`. + * + * Support levels: + * + * - `y`: operation generally works; some instances of the operation may + * not apply + * + * - `*N*`: operation generally does not work; some instances of the + * operation may not apply + * + * - `n/a`: operation does not apply (i.e. bitwise operations to + * floating-point operands, `--` (and in the future possibly `++`) to + * boolean operands, assignment operators to scalar left hand sides) + */ + + /** @name Basic interface + * + * Templates and functions in this group are directly implemented by + * templates and functions in namespace Overloads. + * + * @{ + */ + + //! Element type of some SIMD type + /** + * \tparam V The SIMD (mask or vector) type. `const`, `volatile` or + * reference qualifiers are automatically ignored. + * + * Not all operations that access the element of a vector return (a + * reference to) the scalar type -- some may return proxy objects instead. + * Use `autoCopy()` to make sure you are getting a prvalue of the scalar + * type. + * + * Implemented by `Overloads::ScalarType`. + */ + template<class V> + using Scalar = typename Overloads::ScalarType<std::decay_t<V> >::type; + + //! Construct SIMD type with different scalar type + /** + * \tparam S The new scalar type + * \tparam V The SIMD (mask or vector) type. + * + * The resulting type a SIMD vector of `S` with the same number of lanes + * as `V`. `const`, `volatile` or reference qualifiers in `S` and `V` are + * automatically ignored, and the result will have no such qualifiers. + * + * Implementations shall rebind to `LoopSIMD<S, lanes<V>()>` if they can't + * support a particular rebind natively. + * + * Implemented by `Overloads::RebindType`. + */ + template<class S, class V> + using Rebind = + typename Overloads::RebindType<std::decay_t<S>, std::decay_t<V>>::type; + + //! @} group Basic interface + + /** @name Syntactic Sugar + * + * Templates and functions in this group provide syntactic sugar, they are + * implemented using the functionality from @ref SimdInterfaceBase, and + * are not customizable by implementations. + * + * @{ + */ + + //! Mask type type of some SIMD type + /** + * \tparam V The SIMD (mask or vector) type. `const`, `volatile` or + * reference qualifiers are automatically ignored. + * + * The mask type is kind of a SIMD vector of `bool` with the same number + * of lanes as `V`. It results from comparison operations between values + * of type `V`. It is only "kind of" a SIMD vector, because the + * guaranteed supported operations are extremely limited. At the moment + * only the logical operators `&&`, `||` and `!` and the "bitwise" + * operators `&`, `^` and `|` between masks are supported, and even with + * those operators you cannot rely on automatic broadcasting of `bool` + * values. + * + * \note In particular, masks do not support comparison. As a workaround + * you can use `^` instead of `!=` and `!(m1 ^ m2)` instead of `m1 + * == m2`. (The reason why comparison is not supported is because + * in Vc `==` and `!=` between masks yield a single `bool` result + * and not a mask.) + * + * This is an alias for `Rebind<bool, V>`. + */ + template<class V> + using Mask = Rebind<bool, V>; + + //! @} group Syntactic Sugar + + /** @name Basic interface + * @{ + */ + + //! Number of lanes in a SIMD type + /** + * \tparam V The SIMD (mask or vector) type. `const`, `volatile` + * or reference qualifiers are automatically ignored. + * + * Implemented by `Overloads::LaneCount`. + */ + template<class V> + constexpr std::size_t lanes() + { + return Overloads::LaneCount<std::decay_t<V>>::value; + } + + //! Extract an element of a SIMD type + /** + * \param l Number of lane to extract + * \param v SIMD object to extract from + * + * \return If `v` is a non-`const` lvalue, a reference + * `Scalar<decay_t<V>>&`, or a proxy object through which the + * element of `v` may be modified. Overwise, `v` is a `const` + * lvalue or an rvalue, and the result is a prvalue (a temporary) + * of type `Scalar<decay_t<V>>`. + * + * Implemented by `Overloads::lane()`. + */ + template<class V> + decltype(auto) lane(std::size_t l, V &&v) + { + assert(l < lanes<V>()); + return lane(Overloads::ADLTag<7>{}, l, std::forward<V>(v)); + } + + //! Cast an expression from one implementation to another + /** + * Implemented by `Overloads::implCast()` + * + * Requires the scalar type and the number of lanes to match exactly. + * + * This is particularly useful for masks, which often know the type they + * were derived from. This can become a problem when doing a conditional + * operation e.g. on some floating point vector type, but with a mask + * derived from some index vector type. + * + * \note One of the few functions that explicitly take a template + * argument (`V` in this case). + */ + template<class V, class U> + constexpr V implCast(U &&u) + { + static_assert(std::is_same<Scalar<V>, Scalar<U> >::value, + "Scalar types must match exactly in implCast"); + static_assert(lanes<V>() == lanes<U>(), + "Number of lanes must match in implCast"); + return implCast(Overloads::ADLTag<7>{}, MetaType<std::decay_t<V> >{}, + std::forward<U>(u)); + } + + //! Broadcast a scalar to a vector explicitly + /** + * Implemented by `Overloads::broadcast()` + * + * This is useful because the syntax for broadcasting can vary wildly + * between implementations. + * + * \note One of the few functions that explicitly take a template + * argument (`V` in this case). + */ + template<class V, class S> + constexpr V broadcast(S s) + { + return broadcast(Overloads::ADLTag<7>{}, MetaType<std::decay_t<V> >{}, + std::move(s)); + } + + //! Like the ?: operator + /** + * Equivalent to + * \code + * V result; + * for(std::size_t l = 0; l < lanes(mask); ++l) + * lane(l, result) = + * ( lane(l, mask) ? lane(l, ifTrue) : lane(l ifFalse) ); + * return result; + * \endcode + * + * Implemented by `Overloads::cond()`. + */ + template<class M, class V> + V cond(M &&mask, const V &ifTrue, const V &ifFalse) + { + return cond(Overloads::ADLTag<7>{}, + implCast<Mask<V> >(std::forward<M>(mask)), ifTrue, ifFalse); + } + + //! Like the ?: operator + /** + * Overload for plain bool masks, accepting any simd type + * + * Implemented by `Overloads::cond()`. + */ + template<class V> + V cond(bool mask, const V &ifTrue, const V &ifFalse) + { + return mask ? ifTrue : ifFalse; + } + + //! The binary maximum value over two simd objects + /** + * Implemented by `Overloads::max()`. + */ + template<class V> + auto max(const V &v1, const V &v2) + { + return max(Overloads::ADLTag<7>{}, v1, v2); + } + + //! The binary minimum value over two simd objects + /** + * Implemented by `Overloads::min()`. + */ + template<class V> + auto min(const V &v1, const V &v2) + { + return min(Overloads::ADLTag<7>{}, v1, v2); + } + + //! Whether any entry is `true` + /** + * Implemented by `Overloads::anyTrue()`. + */ + template<class Mask> + bool anyTrue(const Mask &mask) + { + return anyTrue(Overloads::ADLTag<7>{}, mask); + } + + //! Whether all entries are `true` + /** + * Implemented by `Overloads::allTrue()`. + */ + template<class Mask> + bool allTrue(const Mask &mask) + { + return allTrue(Overloads::ADLTag<7>{}, mask); + } + + //! Whether any entry is `false` + /** + * Implemented by `Overloads::anyFalse()`. + */ + template<class Mask> + bool anyFalse(const Mask &mask) + { + return anyFalse(Overloads::ADLTag<7>{}, mask); + } + + //! Whether all entries are `false` + /** + * Implemented by `Overloads::allFalse()`. + */ + template<class Mask> + bool allFalse(const Mask &mask) + { + return allFalse(Overloads::ADLTag<7>{}, mask); + } + + //! The horizontal maximum value over all lanes + /** + * Implemented by `Overloads::max()`. + */ + template<class V> + Scalar<V> max(const V &v) + { + return max(Overloads::ADLTag<7>{}, v); + } + + //! The horizontal minimum value over all lanes + /** + * Implemented by `Overloads::min()`. + */ + template<class V> + Scalar<V> min(const V &v) + { + return min(Overloads::ADLTag<7>{}, v); + } + + //! Convert to mask, analogue of bool(s) for scalars + /** + * Implemented by `Overloads::mask()`. + */ + template<class V> + auto mask(const V &v) + { + return mask(Overloads::ADLTag<7>{}, v); + } + + //! Logic or of masks + /** + * Implemented by `Overloads::maskOr()`. + */ + template<class V1, class V2> + auto maskOr(const V1 &v1, const V2 &v2) + { + return maskOr(Overloads::ADLTag<7>{}, v1, v2); + } + + //! Logic and of masks + /** + * Implemented by `Overloads::maskAnd()`. + */ + template<class V1, class V2> + auto maskAnd(const V1 &v1, const V2 &v2) + { + return maskAnd(Overloads::ADLTag<7>{}, v1, v2); + } + + //! @} group Basic interface + + /** @name Syntactic Sugar + * + * Templates and functions in this group provide syntactic sugar, they are + * implemented using the functionality from @ref SimdInterfaceBase, and + * are not customizable by implementations. + * + * @{ + */ + + //! Number of lanes in a SIMD type + /** + * \tparam V The SIMD (mask or vector) type. + * + * The value of the parameter is ignored; the call is simply forwarded to + * `lanes<V>()`. + */ + template<class V> + std::size_t lanes(const V &) + { + return lanes<V>(); + } + + //! @} group Syntactic Sugar + + //! @} Group SIMDLib + + } // namespace Simd } // namespace Dune #endif // DUNE_COMMON_SIMD_INTERFACE_HH diff --git a/dune/common/simd/io.hh b/dune/common/simd/io.hh index d20194c8f5804828985e9aaf09e5b96a91b5557f..9a79173da8131c3b4efa6c37bc35bada0ca57065 100644 --- a/dune/common/simd/io.hh +++ b/dune/common/simd/io.hh @@ -3,14 +3,14 @@ #include <dune/internal/dune-common.hh> /** @file -* @brief IO interface of the SIMD abstraction -* @ingroup SIMDLib -* -* This file provides IO interface functions of the SIMD abstraction layer. -* -* This file is intended for direct inclusion by header making use of the IO -* interface. -*/ + * @brief IO interface of the SIMD abstraction + * @ingroup SIMDLib + * + * This file provides IO interface functions of the SIMD abstraction layer. + * + * This file is intended for direct inclusion by header making use of the IO + * interface. + */ #include <ios> #include <type_traits> @@ -21,97 +21,97 @@ namespace Dune { -namespace SimdImpl { - -template<class T> -class Inserter { -T value_; - -public: -Inserter(const T &value) : value_(value) {} - -template<class Stream, -class = std::enable_if_t<std::is_base_of<std::ios_base, -Stream>::value> > -friend Stream& operator<<(Stream &out, const Inserter &ins) -{ -const char *sep = "<"; -for(auto l : range(Simd::lanes(ins.value_))) -{ -out << sep << autoCopy(Simd::lane(l, ins.value_)); -sep = ", "; -} -out << '>'; -return out; -} -}; - -template<class V, class = std::enable_if_t<Simd::lanes<V>() != 1> > -Inserter<V> io(const V &v) -{ -return { v }; -} - -template<class V, class = std::enable_if_t<Simd::lanes<V>() == 1> > -Simd::Scalar<V> io(const V &v) -{ -return Simd::lane(0, v); -} - -} - -namespace Simd { - -/** @addtogroup SIMDLib -* -* @{ -* -*/ - -/** @name IO interface -* -* Templates and functions in this group provide syntactic sugar for IO. -* They are implemented using the functionality from @ref -* SimdInterfaceBase, and are not customizable by implementations. -* -* @{ -*/ - -//! construct a stream inserter -/** -* \tparam V The SIMD (mask or vector) type. -* -* Construct an object that can be inserted into an output stream. -* Insertion prints the vector values separated by a comma and a space, -* and surrounded by angular brackets. -*/ -template<class V> -auto vio(const V &v) -{ -return SimdImpl::Inserter<V>{ v }; -} - -//! construct a stream inserter -/** -* \tparam V The SIMD (mask or vector) type. -* -* Construct an object that can be inserted into an output stream. For -* one-lane vectors, behaves the same as scalar insertion. For multi-lane -* vectors, behaves as the inserter returned by `vio()`: insertion prints -* the vector values separated by a comma and a space, and surrounded by -* angular brackets. -*/ -template<class V> -auto io(const V &v) -{ -return SimdImpl::io(v); -} - -//! @} group IO interface - -//! @} Group SIMDLib - -} // namespace Simd + namespace SimdImpl { + + template<class T> + class Inserter { + T value_; + + public: + Inserter(const T &value) : value_(value) {} + + template<class Stream, + class = std::enable_if_t<std::is_base_of<std::ios_base, + Stream>::value> > + friend Stream& operator<<(Stream &out, const Inserter &ins) + { + const char *sep = "<"; + for(auto l : range(Simd::lanes(ins.value_))) + { + out << sep << autoCopy(Simd::lane(l, ins.value_)); + sep = ", "; + } + out << '>'; + return out; + } + }; + + template<class V, class = std::enable_if_t<Simd::lanes<V>() != 1> > + Inserter<V> io(const V &v) + { + return { v }; + } + + template<class V, class = std::enable_if_t<Simd::lanes<V>() == 1> > + Simd::Scalar<V> io(const V &v) + { + return Simd::lane(0, v); + } + + } + + namespace Simd { + + /** @addtogroup SIMDLib + * + * @{ + * + */ + + /** @name IO interface + * + * Templates and functions in this group provide syntactic sugar for IO. + * They are implemented using the functionality from @ref + * SimdInterfaceBase, and are not customizable by implementations. + * + * @{ + */ + + //! construct a stream inserter + /** + * \tparam V The SIMD (mask or vector) type. + * + * Construct an object that can be inserted into an output stream. + * Insertion prints the vector values separated by a comma and a space, + * and surrounded by angular brackets. + */ + template<class V> + auto vio(const V &v) + { + return SimdImpl::Inserter<V>{ v }; + } + + //! construct a stream inserter + /** + * \tparam V The SIMD (mask or vector) type. + * + * Construct an object that can be inserted into an output stream. For + * one-lane vectors, behaves the same as scalar insertion. For multi-lane + * vectors, behaves as the inserter returned by `vio()`: insertion prints + * the vector values separated by a comma and a space, and surrounded by + * angular brackets. + */ + template<class V> + auto io(const V &v) + { + return SimdImpl::io(v); + } + + //! @} group IO interface + + //! @} Group SIMDLib + + } // namespace Simd } // namespace Dune #endif // DUNE_COMMON_SIMD_IO_HH diff --git a/dune/common/simd/loop.hh b/dune/common/simd/loop.hh index 83592535a57a14a897fb7797566e2fd44e2a7812..5cce0c45599bc0fb9912983e8098555ea2048186 100644 --- a/dune/common/simd/loop.hh +++ b/dune/common/simd/loop.hh @@ -15,560 +15,560 @@ namespace Dune { /* -* silence warnings from GCC about using integer operands on a bool -* (when instantiated for T=bool) -*/ + * silence warnings from GCC about using integer operands on a bool + * (when instantiated for T=bool) + */ #if __GNUC__ >= 7 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wbool-operation" # pragma GCC diagnostic ignored "-Wint-in-bool-context" #endif -/** -* This class specifies a vector-like type deriving from std::array -* for memory management and basic accessibility. -* This type is capable of dealing with all (well-defined) operators -* and is usable with the SIMD-interface. -* -* @tparam T Base type. Could also be vectorized type. -* @tparam S Size -* @tparam minimum alignment. It is inherited to rebound types. -*/ - -template<class T, std::size_t S, std::size_t A = 0> -class alignas(A==0?alignof(T):A) LoopSIMD : public std::array<T,S> { - -public: - -//default constructor -LoopSIMD() { -assert(reinterpret_cast<uintptr_t>(this) % std::min(alignof(LoopSIMD<T,S,A>),alignof(std::max_align_t)) == 0); -} - -// broadcast constructor initializing the content with a given value -LoopSIMD(Simd::Scalar<T> i) : LoopSIMD() { -this->fill(i); -} - -template<std::size_t OA> -explicit LoopSIMD(const LoopSIMD<T,S,OA>& other) -: std::array<T,S>(other) -{ -assert(reinterpret_cast<uintptr_t>(this) % std::min(alignof(LoopSIMD<T,S,A>),alignof(std::max_align_t)) == 0); -} - -/* -* Definition of basic operators -*/ - -//Prefix operators + /** + * This class specifies a vector-like type deriving from std::array + * for memory management and basic accessibility. + * This type is capable of dealing with all (well-defined) operators + * and is usable with the SIMD-interface. + * + * @tparam T Base type. Could also be vectorized type. + * @tparam S Size + * @tparam minimum alignment. It is inherited to rebound types. + */ + + template<class T, std::size_t S, std::size_t A = 0> + class alignas(A==0?alignof(T):A) LoopSIMD : public std::array<T,S> { + + public: + + //default constructor + LoopSIMD() { + assert(reinterpret_cast<uintptr_t>(this) % std::min(alignof(LoopSIMD<T,S,A>),alignof(std::max_align_t)) == 0); + } + + // broadcast constructor initializing the content with a given value + LoopSIMD(Simd::Scalar<T> i) : LoopSIMD() { + this->fill(i); + } + + template<std::size_t OA> + explicit LoopSIMD(const LoopSIMD<T,S,OA>& other) + : std::array<T,S>(other) + { + assert(reinterpret_cast<uintptr_t>(this) % std::min(alignof(LoopSIMD<T,S,A>),alignof(std::max_align_t)) == 0); + } + + /* + * Definition of basic operators + */ + + //Prefix operators #define DUNE_SIMD_LOOP_PREFIX_OP(SYMBOL) \ -auto operator SYMBOL() { \ -for(std::size_t i=0; i<S; i++){ \ -SYMBOL(*this)[i]; \ -} \ -return *this; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_PREFIX_OP(++); -DUNE_SIMD_LOOP_PREFIX_OP(--); + auto operator SYMBOL() { \ + for(std::size_t i=0; i<S; i++){ \ + SYMBOL(*this)[i]; \ + } \ + return *this; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_PREFIX_OP(++); + DUNE_SIMD_LOOP_PREFIX_OP(--); #undef DUNE_SIMD_LOOP_PREFIX_OP -//Unary operators + //Unary operators #define DUNE_SIMD_LOOP_UNARY_OP(SYMBOL) \ -auto operator SYMBOL() const { \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = SYMBOL((*this)[i]); \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_UNARY_OP(+); -DUNE_SIMD_LOOP_UNARY_OP(-); -DUNE_SIMD_LOOP_UNARY_OP(~); - -auto operator!() const { -Simd::Mask<LoopSIMD<T,S,A>> out; -for(std::size_t i=0; i<S; i++){ -out[i] = !((*this)[i]); -} -return out; -} + auto operator SYMBOL() const { \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = SYMBOL((*this)[i]); \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_UNARY_OP(+); + DUNE_SIMD_LOOP_UNARY_OP(-); + DUNE_SIMD_LOOP_UNARY_OP(~); + + auto operator!() const { + Simd::Mask<LoopSIMD<T,S,A>> out; + for(std::size_t i=0; i<S; i++){ + out[i] = !((*this)[i]); + } + return out; + } #undef DUNE_SIMD_LOOP_UNARY_OP -//Postfix operators + //Postfix operators #define DUNE_SIMD_LOOP_POSTFIX_OP(SYMBOL) \ -auto operator SYMBOL(int){ \ -LoopSIMD<T,S,A> out = *this; \ -SYMBOL(*this); \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_POSTFIX_OP(++); -DUNE_SIMD_LOOP_POSTFIX_OP(--); + auto operator SYMBOL(int){ \ + LoopSIMD<T,S,A> out = *this; \ + SYMBOL(*this); \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_POSTFIX_OP(++); + DUNE_SIMD_LOOP_POSTFIX_OP(--); #undef DUNE_SIMD_LOOP_POSTFIX_OP -//Assignment operators + //Assignment operators #define DUNE_SIMD_LOOP_ASSIGNMENT_OP(SYMBOL) \ -auto operator SYMBOL(const Simd::Scalar<T> s) { \ -for(std::size_t i=0; i<S; i++){ \ -(*this)[i] SYMBOL s; \ -} \ -return *this; \ -} \ -\ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v) { \ -for(std::size_t i=0; i<S; i++){ \ -(*this)[i] SYMBOL v[i]; \ -} \ -return *this; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_ASSIGNMENT_OP(+=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(-=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(*=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(/=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(%=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(<<=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(>>=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(&=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(|=); -DUNE_SIMD_LOOP_ASSIGNMENT_OP(^=); + auto operator SYMBOL(const Simd::Scalar<T> s) { \ + for(std::size_t i=0; i<S; i++){ \ + (*this)[i] SYMBOL s; \ + } \ + return *this; \ + } \ + \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v) { \ + for(std::size_t i=0; i<S; i++){ \ + (*this)[i] SYMBOL v[i]; \ + } \ + return *this; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_ASSIGNMENT_OP(+=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(-=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(*=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(/=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(%=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(<<=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(>>=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(&=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(|=); + DUNE_SIMD_LOOP_ASSIGNMENT_OP(^=); #undef DUNE_SIMD_LOOP_ASSIGNMENT_OP -}; + }; -//Arithmetic operators + //Arithmetic operators #define DUNE_SIMD_LOOP_BINARY_OP(SYMBOL) \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const Simd::Scalar<T> s) { \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL s; \ -} \ -return out; \ -} \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const Simd::Scalar<T> s, const LoopSIMD<T,S,A> &v) { \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = s SYMBOL v[i]; \ -} \ -return out; \ -} \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ -const LoopSIMD<T,S,A> &w) { \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL w[i]; \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_BINARY_OP(+); -DUNE_SIMD_LOOP_BINARY_OP(-); -DUNE_SIMD_LOOP_BINARY_OP(*); -DUNE_SIMD_LOOP_BINARY_OP(/); -DUNE_SIMD_LOOP_BINARY_OP(%); - -DUNE_SIMD_LOOP_BINARY_OP(&); -DUNE_SIMD_LOOP_BINARY_OP(|); -DUNE_SIMD_LOOP_BINARY_OP(^); + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const Simd::Scalar<T> s) { \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL s; \ + } \ + return out; \ + } \ + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const Simd::Scalar<T> s, const LoopSIMD<T,S,A> &v) { \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = s SYMBOL v[i]; \ + } \ + return out; \ + } \ + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ + const LoopSIMD<T,S,A> &w) { \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL w[i]; \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_BINARY_OP(+); + DUNE_SIMD_LOOP_BINARY_OP(-); + DUNE_SIMD_LOOP_BINARY_OP(*); + DUNE_SIMD_LOOP_BINARY_OP(/); + DUNE_SIMD_LOOP_BINARY_OP(%); + + DUNE_SIMD_LOOP_BINARY_OP(&); + DUNE_SIMD_LOOP_BINARY_OP(|); + DUNE_SIMD_LOOP_BINARY_OP(^); #undef DUNE_SIMD_LOOP_BINARY_OP -//Bitshift operators + //Bitshift operators #define DUNE_SIMD_LOOP_BITSHIFT_OP(SYMBOL) \ -template<class T, std::size_t S, std::size_t A, class U> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const U s) { \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL s; \ -} \ -return out; \ -} \ -template<class T, std::size_t S, std::size_t A, class U, std::size_t AU> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ -const LoopSIMD<U,S,AU> &w) { \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL w[i]; \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_BITSHIFT_OP(<<); -DUNE_SIMD_LOOP_BITSHIFT_OP(>>); + template<class T, std::size_t S, std::size_t A, class U> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const U s) { \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL s; \ + } \ + return out; \ + } \ + template<class T, std::size_t S, std::size_t A, class U, std::size_t AU> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ + const LoopSIMD<U,S,AU> &w) { \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL w[i]; \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_BITSHIFT_OP(<<); + DUNE_SIMD_LOOP_BITSHIFT_OP(>>); #undef DUNE_SIMD_LOOP_BITSHIFT_OP -//Comparison operators + //Comparison operators #define DUNE_SIMD_LOOP_COMPARISON_OP(SYMBOL) \ -template<class T, std::size_t S, std::size_t A, class U> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const U s) { \ -Simd::Mask<LoopSIMD<T,S,A>> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL s; \ -} \ -return out; \ -} \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const Simd::Scalar<T> s, const LoopSIMD<T,S,A> &v) { \ -Simd::Mask<LoopSIMD<T,S,A>> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = s SYMBOL v[i]; \ -} \ -return out; \ -} \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ -const LoopSIMD<T,S,A> &w) { \ -Simd::Mask<LoopSIMD<T,S,A>> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL w[i]; \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_COMPARISON_OP(<); -DUNE_SIMD_LOOP_COMPARISON_OP(>); -DUNE_SIMD_LOOP_COMPARISON_OP(<=); -DUNE_SIMD_LOOP_COMPARISON_OP(>=); -DUNE_SIMD_LOOP_COMPARISON_OP(==); -DUNE_SIMD_LOOP_COMPARISON_OP(!=); + template<class T, std::size_t S, std::size_t A, class U> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const U s) { \ + Simd::Mask<LoopSIMD<T,S,A>> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL s; \ + } \ + return out; \ + } \ + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const Simd::Scalar<T> s, const LoopSIMD<T,S,A> &v) { \ + Simd::Mask<LoopSIMD<T,S,A>> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = s SYMBOL v[i]; \ + } \ + return out; \ + } \ + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ + const LoopSIMD<T,S,A> &w) { \ + Simd::Mask<LoopSIMD<T,S,A>> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL w[i]; \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_COMPARISON_OP(<); + DUNE_SIMD_LOOP_COMPARISON_OP(>); + DUNE_SIMD_LOOP_COMPARISON_OP(<=); + DUNE_SIMD_LOOP_COMPARISON_OP(>=); + DUNE_SIMD_LOOP_COMPARISON_OP(==); + DUNE_SIMD_LOOP_COMPARISON_OP(!=); #undef DUNE_SIMD_LOOP_COMPARISON_OP -//Boolean operators + //Boolean operators #define DUNE_SIMD_LOOP_BOOLEAN_OP(SYMBOL) \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const Simd::Scalar<T> s) { \ -Simd::Mask<LoopSIMD<T,S,A>> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL s; \ -} \ -return out; \ -} \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const Simd::Mask<T> s, const LoopSIMD<T,S,A> &v) { \ -Simd::Mask<LoopSIMD<T,S,A>> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = s SYMBOL v[i]; \ -} \ -return out; \ -} \ -template<class T, std::size_t S, std::size_t A> \ -auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ -const LoopSIMD<T,S,A> &w) { \ -Simd::Mask<LoopSIMD<T,S,A>> out; \ -for(std::size_t i=0; i<S; i++){ \ -out[i] = v[i] SYMBOL w[i]; \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_BOOLEAN_OP(&&); -DUNE_SIMD_LOOP_BOOLEAN_OP(||); + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, const Simd::Scalar<T> s) { \ + Simd::Mask<LoopSIMD<T,S,A>> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL s; \ + } \ + return out; \ + } \ + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const Simd::Mask<T> s, const LoopSIMD<T,S,A> &v) { \ + Simd::Mask<LoopSIMD<T,S,A>> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = s SYMBOL v[i]; \ + } \ + return out; \ + } \ + template<class T, std::size_t S, std::size_t A> \ + auto operator SYMBOL(const LoopSIMD<T,S,A> &v, \ + const LoopSIMD<T,S,A> &w) { \ + Simd::Mask<LoopSIMD<T,S,A>> out; \ + for(std::size_t i=0; i<S; i++){ \ + out[i] = v[i] SYMBOL w[i]; \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_BOOLEAN_OP(&&); + DUNE_SIMD_LOOP_BOOLEAN_OP(||); #undef DUNE_SIMD_LOOP_BOOLEAN_OP -//prints a given LoopSIMD -template<class T, std::size_t S, std::size_t A> -std::ostream& operator<< (std::ostream &os, const LoopSIMD<T,S,A> &v) { -os << "["; -for(std::size_t i=0; i<S-1; i++) { -os << v[i] << ", "; -} -os << v[S-1] << "]"; -return os; -} - -namespace Simd { -namespace Overloads { -/* -* Implementation/Overloads of the functions needed for -* SIMD-interface-compatibility -*/ - -//Implementation of SIMD-interface-types -template<class T, std::size_t S, std::size_t A> -struct ScalarType<LoopSIMD<T,S,A>> { -using type = Simd::Scalar<T>; -}; - -template<class U, class T, std::size_t S, std::size_t A> -struct RebindType<U, LoopSIMD<T,S,A>> { -using type = LoopSIMD<Simd::Rebind<U, T>,S,A>; -}; - -//Implementation of SIMD-interface-functionality -template<class T, std::size_t S, std::size_t A> -struct LaneCount<LoopSIMD<T,S,A>> : index_constant<S*lanes<T>()> {}; - -template<class T, std::size_t S, std::size_t A> -auto lane(ADLTag<5>, std::size_t l, LoopSIMD<T,S,A> &&v) --> decltype(std::move(Simd::lane(l%lanes<T>(), v[l/lanes<T>()]))) -{ -return std::move(Simd::lane(l%lanes<T>(), v[l/lanes<T>()])); -} - -template<class T, std::size_t S, std::size_t A> -auto lane(ADLTag<5>, std::size_t l, const LoopSIMD<T,S,A> &v) --> decltype(Simd::lane(l%lanes<T>(), v[l/lanes<T>()])) -{ -return Simd::lane(l%lanes<T>(), v[l/lanes<T>()]); -} - -template<class T, std::size_t S, std::size_t A> -auto lane(ADLTag<5>, std::size_t l, LoopSIMD<T,S,A> &v) --> decltype(Simd::lane(l%lanes<T>(), v[l/lanes<T>()])) -{ -return Simd::lane(l%lanes<T>(), v[l/lanes<T>()]); -} - -template<class T, std::size_t S, std::size_t AM, std::size_t AD> -auto cond(ADLTag<5>, Simd::Mask<LoopSIMD<T,S,AM>> mask, -LoopSIMD<T,S,AD> ifTrue, LoopSIMD<T,S,AD> ifFalse) { -LoopSIMD<T,S,AD> out; -for(std::size_t i=0; i<S; i++) { -out[i] = Simd::cond(mask[i], ifTrue[i], ifFalse[i]); -} -return out; -} - -template<class M, class T, std::size_t S, std::size_t A> -auto cond(ADLTag<5, std::is_same<bool, Simd::Scalar<M> >::value -&& Simd::lanes<M>() == Simd::lanes<LoopSIMD<T,S,A> >()>, -M mask, LoopSIMD<T,S,A> ifTrue, LoopSIMD<T,S,A> ifFalse) -{ -LoopSIMD<T,S,A> out; -for(auto l : range(Simd::lanes(mask))) -Simd::lane(l, out) = Simd::lane(l, mask) ? Simd::lane(l, ifTrue) : Simd::lane(l, ifFalse); -return out; -} - -template<class M, std::size_t S, std::size_t A> -bool anyTrue(ADLTag<5>, LoopSIMD<M,S,A> mask) { -bool out = false; -for(std::size_t i=0; i<S; i++) { -out |= Simd::anyTrue(mask[i]); -} -return out; -} - -template<class M, std::size_t S, std::size_t A> -bool allTrue(ADLTag<5>, LoopSIMD<M,S,A> mask) { -bool out = true; -for(std::size_t i=0; i<S; i++) { -out &= Simd::allTrue(mask[i]); -} -return out; -} - -template<class M, std::size_t S, std::size_t A> -bool anyFalse(ADLTag<5>, LoopSIMD<M,S,A> mask) { -bool out = false; -for(std::size_t i=0; i<S; i++) { -out |= Simd::anyFalse(mask[i]); -} -return out; -} - -template<class M, std::size_t S, std::size_t A> -bool allFalse(ADLTag<5>, LoopSIMD<M,S,A> mask) { -bool out = true; -for(std::size_t i=0; i<S; i++) { -out &= Simd::allFalse(mask[i]); -} -return out; -} -} //namespace Overloads - -} //namespace Simd - - -/* -* Overloads the unary cmath-operations. Operations requiring -* or returning more than one argument are not supported. -* Due to inconsistency with the return values, cmath-operations -* on integral types are also not supported- -*/ + //prints a given LoopSIMD + template<class T, std::size_t S, std::size_t A> + std::ostream& operator<< (std::ostream &os, const LoopSIMD<T,S,A> &v) { + os << "["; + for(std::size_t i=0; i<S-1; i++) { + os << v[i] << ", "; + } + os << v[S-1] << "]"; + return os; + } + + namespace Simd { + namespace Overloads { + /* + * Implementation/Overloads of the functions needed for + * SIMD-interface-compatibility + */ + + //Implementation of SIMD-interface-types + template<class T, std::size_t S, std::size_t A> + struct ScalarType<LoopSIMD<T,S,A>> { + using type = Simd::Scalar<T>; + }; + + template<class U, class T, std::size_t S, std::size_t A> + struct RebindType<U, LoopSIMD<T,S,A>> { + using type = LoopSIMD<Simd::Rebind<U, T>,S,A>; + }; + + //Implementation of SIMD-interface-functionality + template<class T, std::size_t S, std::size_t A> + struct LaneCount<LoopSIMD<T,S,A>> : index_constant<S*lanes<T>()> {}; + + template<class T, std::size_t S, std::size_t A> + auto lane(ADLTag<5>, std::size_t l, LoopSIMD<T,S,A> &&v) + -> decltype(std::move(Simd::lane(l%lanes<T>(), v[l/lanes<T>()]))) + { + return std::move(Simd::lane(l%lanes<T>(), v[l/lanes<T>()])); + } + + template<class T, std::size_t S, std::size_t A> + auto lane(ADLTag<5>, std::size_t l, const LoopSIMD<T,S,A> &v) + -> decltype(Simd::lane(l%lanes<T>(), v[l/lanes<T>()])) + { + return Simd::lane(l%lanes<T>(), v[l/lanes<T>()]); + } + + template<class T, std::size_t S, std::size_t A> + auto lane(ADLTag<5>, std::size_t l, LoopSIMD<T,S,A> &v) + -> decltype(Simd::lane(l%lanes<T>(), v[l/lanes<T>()])) + { + return Simd::lane(l%lanes<T>(), v[l/lanes<T>()]); + } + + template<class T, std::size_t S, std::size_t AM, std::size_t AD> + auto cond(ADLTag<5>, Simd::Mask<LoopSIMD<T,S,AM>> mask, + LoopSIMD<T,S,AD> ifTrue, LoopSIMD<T,S,AD> ifFalse) { + LoopSIMD<T,S,AD> out; + for(std::size_t i=0; i<S; i++) { + out[i] = Simd::cond(mask[i], ifTrue[i], ifFalse[i]); + } + return out; + } + + template<class M, class T, std::size_t S, std::size_t A> + auto cond(ADLTag<5, std::is_same<bool, Simd::Scalar<M> >::value + && Simd::lanes<M>() == Simd::lanes<LoopSIMD<T,S,A> >()>, + M mask, LoopSIMD<T,S,A> ifTrue, LoopSIMD<T,S,A> ifFalse) + { + LoopSIMD<T,S,A> out; + for(auto l : range(Simd::lanes(mask))) + Simd::lane(l, out) = Simd::lane(l, mask) ? Simd::lane(l, ifTrue) : Simd::lane(l, ifFalse); + return out; + } + + template<class M, std::size_t S, std::size_t A> + bool anyTrue(ADLTag<5>, LoopSIMD<M,S,A> mask) { + bool out = false; + for(std::size_t i=0; i<S; i++) { + out |= Simd::anyTrue(mask[i]); + } + return out; + } + + template<class M, std::size_t S, std::size_t A> + bool allTrue(ADLTag<5>, LoopSIMD<M,S,A> mask) { + bool out = true; + for(std::size_t i=0; i<S; i++) { + out &= Simd::allTrue(mask[i]); + } + return out; + } + + template<class M, std::size_t S, std::size_t A> + bool anyFalse(ADLTag<5>, LoopSIMD<M,S,A> mask) { + bool out = false; + for(std::size_t i=0; i<S; i++) { + out |= Simd::anyFalse(mask[i]); + } + return out; + } + + template<class M, std::size_t S, std::size_t A> + bool allFalse(ADLTag<5>, LoopSIMD<M,S,A> mask) { + bool out = true; + for(std::size_t i=0; i<S; i++) { + out &= Simd::allFalse(mask[i]); + } + return out; + } + } //namespace Overloads + + } //namespace Simd + + + /* + * Overloads the unary cmath-operations. Operations requiring + * or returning more than one argument are not supported. + * Due to inconsistency with the return values, cmath-operations + * on integral types are also not supported- + */ #define DUNE_SIMD_LOOP_CMATH_UNARY_OP(expr) \ -template<class T, std::size_t S, std::size_t A, typename Sfinae = \ -typename std::enable_if_t<!std::is_integral<Simd::Scalar<T>>::value> > \ -auto expr(const LoopSIMD<T,S,A> &v) { \ -using std::expr; \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++) { \ -out[i] = expr(v[i]); \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") + template<class T, std::size_t S, std::size_t A, typename Sfinae = \ + typename std::enable_if_t<!std::is_integral<Simd::Scalar<T>>::value> > \ + auto expr(const LoopSIMD<T,S,A> &v) { \ + using std::expr; \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++) { \ + out[i] = expr(v[i]); \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") #define DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(expr, returnType) \ -template<class T, std::size_t S, std::size_t A, typename Sfinae = \ -typename std::enable_if_t<!std::is_integral<Simd::Scalar<T>>::value> > \ -auto expr(const LoopSIMD<T,S,A> &v) { \ -using std::expr; \ -LoopSIMD<returnType,S> out; \ -for(std::size_t i=0; i<S; i++) { \ -out[i] = expr(v[i]); \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_CMATH_UNARY_OP(cos); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(sin); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(tan); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(acos); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(asin); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(atan); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(cosh); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(sinh); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(tanh); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(acosh); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(asinh); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(atanh); - -DUNE_SIMD_LOOP_CMATH_UNARY_OP(exp); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(log); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(log10); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(exp2); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(expm1); -DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(ilogb, int); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(log1p); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(log2); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(logb); - -DUNE_SIMD_LOOP_CMATH_UNARY_OP(sqrt); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(cbrt); - -DUNE_SIMD_LOOP_CMATH_UNARY_OP(erf); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(erfc); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(tgamma); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(lgamma); - -DUNE_SIMD_LOOP_CMATH_UNARY_OP(ceil); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(floor); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(trunc); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(round); -DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(lround, long); -DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(llround, long long); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(rint); -DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(lrint, long); -DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(llrint, long long); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(nearbyint); - -DUNE_SIMD_LOOP_CMATH_UNARY_OP(fabs); -DUNE_SIMD_LOOP_CMATH_UNARY_OP(abs); + template<class T, std::size_t S, std::size_t A, typename Sfinae = \ + typename std::enable_if_t<!std::is_integral<Simd::Scalar<T>>::value> > \ + auto expr(const LoopSIMD<T,S,A> &v) { \ + using std::expr; \ + LoopSIMD<returnType,S> out; \ + for(std::size_t i=0; i<S; i++) { \ + out[i] = expr(v[i]); \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_CMATH_UNARY_OP(cos); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(sin); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(tan); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(acos); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(asin); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(atan); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(cosh); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(sinh); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(tanh); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(acosh); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(asinh); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(atanh); + + DUNE_SIMD_LOOP_CMATH_UNARY_OP(exp); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(log); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(log10); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(exp2); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(expm1); + DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(ilogb, int); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(log1p); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(log2); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(logb); + + DUNE_SIMD_LOOP_CMATH_UNARY_OP(sqrt); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(cbrt); + + DUNE_SIMD_LOOP_CMATH_UNARY_OP(erf); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(erfc); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(tgamma); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(lgamma); + + DUNE_SIMD_LOOP_CMATH_UNARY_OP(ceil); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(floor); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(trunc); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(round); + DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(lround, long); + DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(llround, long long); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(rint); + DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(lrint, long); + DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN(llrint, long long); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(nearbyint); + + DUNE_SIMD_LOOP_CMATH_UNARY_OP(fabs); + DUNE_SIMD_LOOP_CMATH_UNARY_OP(abs); #undef DUNE_SIMD_LOOP_CMATH_UNARY_OP #undef DUNE_SIMD_LOOP_CMATH_UNARY_OP_WITH_RETURN -/* not implemented cmath-functions: -* atan2 -* frexp, idexp -* modf -* scalbn, scalbln -* pow -* hypot -* remainder, remquo -* copysign -* nan -* nextafter, nexttoward -* fdim, fmax, fmin -*/ - -/* -* Overloads specific functions usually provided by the std library -* More overloads will be provided should the need arise. -*/ + /* not implemented cmath-functions: + * atan2 + * frexp, idexp + * modf + * scalbn, scalbln + * pow + * hypot + * remainder, remquo + * copysign + * nan + * nextafter, nexttoward + * fdim, fmax, fmin + */ + + /* + * Overloads specific functions usually provided by the std library + * More overloads will be provided should the need arise. + */ #define DUNE_SIMD_LOOP_STD_UNARY_OP(expr) \ -template<class T, std::size_t S, std::size_t A> \ -auto expr(const LoopSIMD<T,S,A> &v) { \ -using std::expr; \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++) { \ -out[i] = expr(v[i]); \ -} \ -return out; \ -} \ -\ -template<class T, std::size_t S, std::size_t A> \ -auto expr(const LoopSIMD<std::complex<T>,S,A> &v) { \ -using std::expr; \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++) { \ -out[i] = expr(v[i]); \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_STD_UNARY_OP(real); -DUNE_SIMD_LOOP_STD_UNARY_OP(imag); + template<class T, std::size_t S, std::size_t A> \ + auto expr(const LoopSIMD<T,S,A> &v) { \ + using std::expr; \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++) { \ + out[i] = expr(v[i]); \ + } \ + return out; \ + } \ + \ + template<class T, std::size_t S, std::size_t A> \ + auto expr(const LoopSIMD<std::complex<T>,S,A> &v) { \ + using std::expr; \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++) { \ + out[i] = expr(v[i]); \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_STD_UNARY_OP(real); + DUNE_SIMD_LOOP_STD_UNARY_OP(imag); #undef DUNE_SIMD_LOOP_STD_UNARY_OP #define DUNE_SIMD_LOOP_STD_BINARY_OP(expr) \ -template<class T, std::size_t S, std::size_t A> \ -auto expr(const LoopSIMD<T,S,A> &v, const LoopSIMD<T,S,A> &w) { \ -using std::expr; \ -LoopSIMD<T,S,A> out; \ -for(std::size_t i=0; i<S; i++) { \ -out[i] = expr(v[i],w[i]); \ -} \ -return out; \ -} \ -static_assert(true, "expecting ;") - -DUNE_SIMD_LOOP_STD_BINARY_OP(max); -DUNE_SIMD_LOOP_STD_BINARY_OP(min); + template<class T, std::size_t S, std::size_t A> \ + auto expr(const LoopSIMD<T,S,A> &v, const LoopSIMD<T,S,A> &w) { \ + using std::expr; \ + LoopSIMD<T,S,A> out; \ + for(std::size_t i=0; i<S; i++) { \ + out[i] = expr(v[i],w[i]); \ + } \ + return out; \ + } \ + static_assert(true, "expecting ;") + + DUNE_SIMD_LOOP_STD_BINARY_OP(max); + DUNE_SIMD_LOOP_STD_BINARY_OP(min); #undef DUNE_SIMD_LOOP_STD_BINARY_OP -namespace MathOverloads { -template<class T, std::size_t S, std::size_t A> -auto isNaN(const LoopSIMD<T,S,A> &v, PriorityTag<3>, ADLTag) { -Simd::Mask<LoopSIMD<T,S,A>> out; -for(auto l : range(S)) -out[l] = Dune::isNaN(v[l]); -return out; -} - -template<class T, std::size_t S, std::size_t A> -auto isInf(const LoopSIMD<T,S,A> &v, PriorityTag<3>, ADLTag) { -Simd::Mask<LoopSIMD<T,S,A>> out; -for(auto l : range(S)) -out[l] = Dune::isInf(v[l]); -return out; -} - -template<class T, std::size_t S, std::size_t A> -auto isFinite(const LoopSIMD<T,S,A> &v, PriorityTag<3>, ADLTag) { -Simd::Mask<LoopSIMD<T,S,A>> out; -for(auto l : range(S)) -out[l] = Dune::isFinite(v[l]); -return out; -} -} //namepace MathOverloads - -template<class T, std::size_t S, std::size_t A> -struct IsNumber<LoopSIMD<T,S,A>> : -public std::integral_constant<bool, IsNumber<T>::value>{ -}; + namespace MathOverloads { + template<class T, std::size_t S, std::size_t A> + auto isNaN(const LoopSIMD<T,S,A> &v, PriorityTag<3>, ADLTag) { + Simd::Mask<LoopSIMD<T,S,A>> out; + for(auto l : range(S)) + out[l] = Dune::isNaN(v[l]); + return out; + } + + template<class T, std::size_t S, std::size_t A> + auto isInf(const LoopSIMD<T,S,A> &v, PriorityTag<3>, ADLTag) { + Simd::Mask<LoopSIMD<T,S,A>> out; + for(auto l : range(S)) + out[l] = Dune::isInf(v[l]); + return out; + } + + template<class T, std::size_t S, std::size_t A> + auto isFinite(const LoopSIMD<T,S,A> &v, PriorityTag<3>, ADLTag) { + Simd::Mask<LoopSIMD<T,S,A>> out; + for(auto l : range(S)) + out[l] = Dune::isFinite(v[l]); + return out; + } + } //namepace MathOverloads + + template<class T, std::size_t S, std::size_t A> + struct IsNumber<LoopSIMD<T,S,A>> : + public std::integral_constant<bool, IsNumber<T>::value>{ + }; #if __GNUC__ >= 7 # pragma GCC diagnostic pop diff --git a/dune/common/simd/simd.hh b/dune/common/simd/simd.hh index afe856cf3ca39dce99291851850c7da7013474da..197803261ad2ae0fe111c51ac5fcced40e2249b3 100644 --- a/dune/common/simd/simd.hh +++ b/dune/common/simd/simd.hh @@ -3,11 +3,11 @@ #include <dune/internal/dune-common.hh> /** @file -* @brief Include file for users of the SIMD abstraction layer -* -* Include this file if you want to be able to handle SIMD types -- do not -* include the internal headers directly. -*/ + * @brief Include file for users of the SIMD abstraction layer + * + * Include this file if you want to be able to handle SIMD types -- do not + * include the internal headers directly. + */ #include <dune/common/simd/interface.hh> #include <dune/common/simd/standard.hh> diff --git a/dune/common/simd/standard.hh b/dune/common/simd/standard.hh index cb9abddc3fe7a4ab67b3143f69a29db7cd698ae4..994d1fdbf4d97b1356aee4c099cbb1d38f698790 100644 --- a/dune/common/simd/standard.hh +++ b/dune/common/simd/standard.hh @@ -3,18 +3,18 @@ #include <dune/internal/dune-common.hh> /** @file -* @ingroup SIMDStandard -* @brief SIMD abstractions for the standard built-in types -* -* This file should not normally be included by users of the SIMD abstraction -* (i.e. other Dune headers). Neither should it be included by the -* translation units passing built-in types to Dune headers that in turn -* support SIMD types through the SIMD abstraction. Dune-functionality always -* supports built-in types. Either because that functionality does not -* support SIMD types and so only supports built-in types, or if it does -* support SIMD types it must include `<dune/common/simd/simd.hh>`, which in -* turn includes this header. -*/ + * @ingroup SIMDStandard + * @brief SIMD abstractions for the standard built-in types + * + * This file should not normally be included by users of the SIMD abstraction + * (i.e. other Dune headers). Neither should it be included by the + * translation units passing built-in types to Dune headers that in turn + * support SIMD types through the SIMD abstraction. Dune-functionality always + * supports built-in types. Either because that functionality does not + * support SIMD types and so only supports built-in types, or if it does + * support SIMD types it must include `<dune/common/simd/simd.hh>`, which in + * turn includes this header. + */ #include <cstddef> #include <type_traits> @@ -25,94 +25,94 @@ #include <dune/common/simd/defaults.hh> /** @defgroup SIMDStandard SIMD Abstraction Implementation for standard types -* @ingroup SIMDApp -* -* This implements the vectorization interface for scalar types. It applies -* to any type that does not have a specialized interface implementation. -* -* As an application developer, there is nothing special you need to do to get -* support for standard types in the vectorization abstraction. If the dune -* classes you are using provide support for vectorization, they will include -* `<dune/common/simd/simd.hh>`, which will pull in the abstraction for -* standard types automatically. You simply need to make sure that the types -* themselves are supported: -* - for built-in types there is nothing you need to do, -* - for `std::complex`, you need to `#include <complex>` -* - etc. -*/ + * @ingroup SIMDApp + * + * This implements the vectorization interface for scalar types. It applies + * to any type that does not have a specialized interface implementation. + * + * As an application developer, there is nothing special you need to do to get + * support for standard types in the vectorization abstraction. If the dune + * classes you are using provide support for vectorization, they will include + * `<dune/common/simd/simd.hh>`, which will pull in the abstraction for + * standard types automatically. You simply need to make sure that the types + * themselves are supported: + * - for built-in types there is nothing you need to do, + * - for `std::complex`, you need to `#include <complex>` + * - etc. + */ namespace Dune { -namespace Simd { - -namespace Overloads { - -/** @name Specialized classes and overloaded functions -* @ingroup SIMDStandard -* @{ -*/ - -//! should have a member type \c type -/** -* Implements Simd::Scalar -*/ -template<class V, class> -struct ScalarType { using type = V; }; - -//! should have a member type \c type -/** -* Implements Simd::Rebind -*/ -template<class S, class, class> -struct RebindType { using type = S; }; - -//! should be derived from an Dune::index_constant -/** -* Implements Simd::lanes() -*/ -template<class, class> -struct LaneCount : public index_constant<1> { }; - -//! implements Simd::lane() -/** -* This binds to rvalues and const lvalues. It would bind to non-const -* lvalues too, but those are caught by the overload with ADLTag<3>. -* Using a universal reference here would bind to any argument with a -* perfect match. This would mean ambiguous overloads with other -* abstractions, if those only declare overloads for `const TheirType &` -* and `TheirType &`, because because universal references match -* perfectly. -*/ -template<class V> -V lane(ADLTag<2>, std::size_t l, V v) -{ -return v; -} - -template<class V> -V &lane(ADLTag<3>, std::size_t l, V &v) -{ -return v; -} - -// No Simd::cond() implementation, the overload for bool masks in the -// interface is sufficient - -//! implements Simd::anyTrue() -inline bool anyTrue(ADLTag<2>, bool mask) { return mask; } - -//! implements Simd::allTrue() -inline bool allTrue(ADLTag<2>, bool mask) { return mask; } - -//! implements Simd::anyFalse() -inline bool anyFalse(ADLTag<2>, bool mask) { return !mask; } - -//! implements Simd::allFalse() -inline bool allFalse(ADLTag<2>, bool mask) { return !mask; } - -//! @} group SIMDStandard - -} // namespace Overloads -} // namespace Simd + namespace Simd { + + namespace Overloads { + + /** @name Specialized classes and overloaded functions + * @ingroup SIMDStandard + * @{ + */ + + //! should have a member type \c type + /** + * Implements Simd::Scalar + */ + template<class V, class> + struct ScalarType { using type = V; }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + */ + template<class S, class, class> + struct RebindType { using type = S; }; + + //! should be derived from an Dune::index_constant + /** + * Implements Simd::lanes() + */ + template<class, class> + struct LaneCount : public index_constant<1> { }; + + //! implements Simd::lane() + /** + * This binds to rvalues and const lvalues. It would bind to non-const + * lvalues too, but those are caught by the overload with ADLTag<3>. + * Using a universal reference here would bind to any argument with a + * perfect match. This would mean ambiguous overloads with other + * abstractions, if those only declare overloads for `const TheirType &` + * and `TheirType &`, because because universal references match + * perfectly. + */ + template<class V> + V lane(ADLTag<2>, std::size_t l, V v) + { + return v; + } + + template<class V> + V &lane(ADLTag<3>, std::size_t l, V &v) + { + return v; + } + + // No Simd::cond() implementation, the overload for bool masks in the + // interface is sufficient + + //! implements Simd::anyTrue() + inline bool anyTrue(ADLTag<2>, bool mask) { return mask; } + + //! implements Simd::allTrue() + inline bool allTrue(ADLTag<2>, bool mask) { return mask; } + + //! implements Simd::anyFalse() + inline bool anyFalse(ADLTag<2>, bool mask) { return !mask; } + + //! implements Simd::allFalse() + inline bool allFalse(ADLTag<2>, bool mask) { return !mask; } + + //! @} group SIMDStandard + + } // namespace Overloads + } // namespace Simd } // namespace Dune #endif // DUNE_COMMON_SIMD_STANDARD_HH diff --git a/dune/common/simd/test.hh b/dune/common/simd/test.hh index 8c447fd8d68ef254de2f8778c3e4cb7fd3225b15..7d9e15f4ae145c19d59505d529493c55e59103ea 100644 --- a/dune/common/simd/test.hh +++ b/dune/common/simd/test.hh @@ -3,10 +3,10 @@ #include <dune/internal/dune-common.hh> /** @file -* @brief Common tests for simd abstraction implementations -* -* This file is an interface header and may be included without restrictions. -*/ + * @brief Common tests for simd abstraction implementations + * + * This file is an interface header and may be included without restrictions. + */ #include <algorithm> #include <cstddef> @@ -31,2006 +31,2006 @@ #include <dune/common/unused.hh> namespace Dune { -namespace Simd { - -namespace Impl { - -template<class Expr, class SFINAE = void> -struct CanCall; // not defined unless Expr has the form Op(Args...) -template<class Op, class... Args, class SFINAE> -struct CanCall<Op(Args...), SFINAE> : std::false_type {}; -template<class Op, class... Args> -struct CanCall<Op(Args...), void_t<std::result_of_t<Op(Args...)> > > -: std::true_type -{}; - -template<class T, class SFINAE = void> -struct LessThenComparable : std::false_type {}; -template<class T> -struct LessThenComparable<T, void_t<decltype(std::declval<T>() -< std::declval<T>())> > : -std::true_type -{}; - -template<class Dst, class Src> -struct CopyConstHelper -{ -using type = Dst; -}; -template<class Dst, class Src> -struct CopyConstHelper<Dst, const Src> -{ -using type = std::add_const_t<Dst>; -}; - -template<class Dst, class Src> -struct CopyVolatileHelper -{ -using type = Dst; -}; -template<class Dst, class Src> -struct CopyVolatileHelper<Dst, volatile Src> -{ -using type = std::add_volatile_t<Dst>; -}; - -template<class Dst, class Src> -struct CopyReferenceHelper -{ -using type = Dst; -}; -template<class Dst, class Src> -struct CopyReferenceHelper<Dst, Src&> -{ -using type = std::add_lvalue_reference_t<Dst>; -}; - -template<class Dst, class Src> -struct CopyReferenceHelper<Dst, Src&&> -{ -using type = std::add_rvalue_reference_t<Dst>; -}; - -template<class Dst, class Src> -using CopyRefQual = typename CopyReferenceHelper< -typename CopyVolatileHelper< -typename CopyConstHelper< -std::decay_t<Dst>, -std::remove_reference_t<Src> ->::type, -std::remove_reference_t<Src> ->::type, -Src ->::type; - -template<class Mark, class Types, -class Indices = -std::make_index_sequence<TypeListSize<Types>::value - 1> > -struct RemoveEnd; -template<class Mark, class Types, std::size_t... I> -struct RemoveEnd<Mark, Types, std::index_sequence<I...>> -{ -using Back = TypeListEntry_t<TypeListSize<Types>::value - 1, Types>; -static_assert(std::is_same<Mark, Back>::value, -"TypeList not terminated by proper EndMark"); -using type = TypeList<TypeListEntry_t<I, Types>...>; -}; - -template<class T, class List, class = void> -struct TypeInList; - -template<class T> -struct TypeInList<T, TypeList<> > : std::false_type {}; - -template<class T, class... Rest> -struct TypeInList<T, TypeList<T, Rest...> > : std::true_type {}; - -template<class T, class Head, class... Rest> -struct TypeInList<T, TypeList<Head, Rest...>, -std::enable_if_t<!std::is_same<T, Head>::value> > : -TypeInList<T, TypeList<Rest...> >::type -{}; - -template<class T> -struct IsLoop : std::false_type {}; -template<class T, std::size_t S> -struct IsLoop<LoopSIMD<T, S> > : std::true_type {}; - -// used inside static_assert to trick the compiler into printing a list -// of types: -// -// static_assert(debugTypes<V>(Std::bool_constant<condition>{}), "msg"); -// -// Should include what the type `V` expands to in the error message. -template<class...> -constexpr bool debugTypes(std::true_type) { return true; } -template<class... Types> -[[deprecated]] -constexpr bool debugTypes(std::false_type) { return false; } - -} // namespace Impl - -//! final element marker for `RebindList` -struct EndMark {}; -//! A list of types with the final element removed -/** -* This is `TypeList<NoEndTypes..>`, where `NoEndTypes...` is `Types...` -* with the final element removed. The final element in `Types...` is -* required to be `EndMark`. -* -* This is useful to construct type lists in generated source files, since -* you don't need to avoid generating a trailing `,` in the list -- just -* terminate it with `EndMark`. -*/ -template<class... Types> -using RebindList = -typename Impl::RemoveEnd<EndMark, TypeList<Types...> >::type; - -//! check whether a type is an instance of LoopSIMD -template<class T> -using IsLoop = typename Impl::IsLoop<T>::type; - -class UnitTest { -bool good_ = true; -std::ostream &log_ = std::cerr; -// records the types for which checks have started running to avoid -// infinite recursion -std::unordered_set<std::type_index> seen_; - -//////////////////////////////////////////////////////////////////////// -// -// Helper functions -// - -void complain(const char *file, int line, const char *func, -const char *expr); - -void complain(const char *file, int line, const char *func, -const std::string &opname, const char *expr); - -// This macro is defined only within this file, do not use anywhere -// else. Doing the actual printing in an external function dramatically -// reduces memory use during compilation. Defined in such a way that -// the call will only happen for failed checks. + namespace Simd { + + namespace Impl { + + template<class Expr, class SFINAE = void> + struct CanCall; // not defined unless Expr has the form Op(Args...) + template<class Op, class... Args, class SFINAE> + struct CanCall<Op(Args...), SFINAE> : std::false_type {}; + template<class Op, class... Args> + struct CanCall<Op(Args...), void_t<std::result_of_t<Op(Args...)> > > + : std::true_type + {}; + + template<class T, class SFINAE = void> + struct LessThenComparable : std::false_type {}; + template<class T> + struct LessThenComparable<T, void_t<decltype(std::declval<T>() + < std::declval<T>())> > : + std::true_type + {}; + + template<class Dst, class Src> + struct CopyConstHelper + { + using type = Dst; + }; + template<class Dst, class Src> + struct CopyConstHelper<Dst, const Src> + { + using type = std::add_const_t<Dst>; + }; + + template<class Dst, class Src> + struct CopyVolatileHelper + { + using type = Dst; + }; + template<class Dst, class Src> + struct CopyVolatileHelper<Dst, volatile Src> + { + using type = std::add_volatile_t<Dst>; + }; + + template<class Dst, class Src> + struct CopyReferenceHelper + { + using type = Dst; + }; + template<class Dst, class Src> + struct CopyReferenceHelper<Dst, Src&> + { + using type = std::add_lvalue_reference_t<Dst>; + }; + + template<class Dst, class Src> + struct CopyReferenceHelper<Dst, Src&&> + { + using type = std::add_rvalue_reference_t<Dst>; + }; + + template<class Dst, class Src> + using CopyRefQual = typename CopyReferenceHelper< + typename CopyVolatileHelper< + typename CopyConstHelper< + std::decay_t<Dst>, + std::remove_reference_t<Src> + >::type, + std::remove_reference_t<Src> + >::type, + Src + >::type; + + template<class Mark, class Types, + class Indices = + std::make_index_sequence<TypeListSize<Types>::value - 1> > + struct RemoveEnd; + template<class Mark, class Types, std::size_t... I> + struct RemoveEnd<Mark, Types, std::index_sequence<I...>> + { + using Back = TypeListEntry_t<TypeListSize<Types>::value - 1, Types>; + static_assert(std::is_same<Mark, Back>::value, + "TypeList not terminated by proper EndMark"); + using type = TypeList<TypeListEntry_t<I, Types>...>; + }; + + template<class T, class List, class = void> + struct TypeInList; + + template<class T> + struct TypeInList<T, TypeList<> > : std::false_type {}; + + template<class T, class... Rest> + struct TypeInList<T, TypeList<T, Rest...> > : std::true_type {}; + + template<class T, class Head, class... Rest> + struct TypeInList<T, TypeList<Head, Rest...>, + std::enable_if_t<!std::is_same<T, Head>::value> > : + TypeInList<T, TypeList<Rest...> >::type + {}; + + template<class T> + struct IsLoop : std::false_type {}; + template<class T, std::size_t S> + struct IsLoop<LoopSIMD<T, S> > : std::true_type {}; + + // used inside static_assert to trick the compiler into printing a list + // of types: + // + // static_assert(debugTypes<V>(Std::bool_constant<condition>{}), "msg"); + // + // Should include what the type `V` expands to in the error message. + template<class...> + constexpr bool debugTypes(std::true_type) { return true; } + template<class... Types> + [[deprecated]] + constexpr bool debugTypes(std::false_type) { return false; } + + } // namespace Impl + + //! final element marker for `RebindList` + struct EndMark {}; + //! A list of types with the final element removed + /** + * This is `TypeList<NoEndTypes..>`, where `NoEndTypes...` is `Types...` + * with the final element removed. The final element in `Types...` is + * required to be `EndMark`. + * + * This is useful to construct type lists in generated source files, since + * you don't need to avoid generating a trailing `,` in the list -- just + * terminate it with `EndMark`. + */ + template<class... Types> + using RebindList = + typename Impl::RemoveEnd<EndMark, TypeList<Types...> >::type; + + //! check whether a type is an instance of LoopSIMD + template<class T> + using IsLoop = typename Impl::IsLoop<T>::type; + + class UnitTest { + bool good_ = true; + std::ostream &log_ = std::cerr; + // records the types for which checks have started running to avoid + // infinite recursion + std::unordered_set<std::type_index> seen_; + + //////////////////////////////////////////////////////////////////////// + // + // Helper functions + // + + void complain(const char *file, int line, const char *func, + const char *expr); + + void complain(const char *file, int line, const char *func, + const std::string &opname, const char *expr); + + // This macro is defined only within this file, do not use anywhere + // else. Doing the actual printing in an external function dramatically + // reduces memory use during compilation. Defined in such a way that + // the call will only happen for failed checks. #define DUNE_SIMD_CHECK(expr) \ -((expr) ? void() : complain(__FILE__, __LINE__, __func__, #expr)) + ((expr) ? void() : complain(__FILE__, __LINE__, __func__, #expr)) -// the function using this macro must define a way to compute the -// operator name in DUNE_SIMD_OPNAME + // the function using this macro must define a way to compute the + // operator name in DUNE_SIMD_OPNAME #define DUNE_SIMD_CHECK_OP(expr) \ -((expr) ? void() : complain(__FILE__, __LINE__, __func__, \ -DUNE_SIMD_OPNAME, #expr)) - -// "cast" into a prvalue -template<class T> -static std::decay_t<T> prvalue(T &&t) -{ -return std::forward<T>(t); -} - -// whether the vector is 42 in all lanes -template<class V> -static bool is42(const V &v) -{ -bool good = true; - -for(std::size_t l = 0; l < lanes(v); ++l) -// need to cast in case we have a mask type -good &= (lane(l, v) == Scalar<V>(42)); - -return good; -} - -// make a vector that contains the sequence { 1, 2, ... } -template<class V> -static V make123() -{ -// initialize to avoid undefined behaviour if assigning to lane() -// involves lvalue-to-rvalue conversions, e.g. due to bitmask -// operations. Avoid using broadcast<V>() for initialization to avoid -// test interdependencies. -V vec(Scalar<V>(0)); -for(std::size_t l = 0; l < lanes(vec); ++l) -lane(l, vec) = l + 1; -return vec; -} - -// whether the vector contains the sequence { 1, 2, ... } -template<class V> -static bool is123(const V &v) -{ -bool good = true; - -for(std::size_t l = 0; l < lanes(v); ++l) -// need to cast in case we have a mask type -good &= (lane(l, v) == Scalar<V>(l+1)); - -return good; -} - -template<class V> -static V leftVector() -{ -// Avoid using broadcast<V>() for initialization to avoid test -// interdependencies. -V res(Scalar<V>(0)); -for(std::size_t l = 0; l < lanes(res); ++l) -lane(l, res) = Scalar<V>(l+1); -return res; -} - -template<class V> -static V rightVector() -{ -// Avoid using broadcast<V>() for initialization to avoid test -// interdependencies. -V res(Scalar<V>(0)); -for(std::size_t l = 0; l < lanes(res); ++l) -// do not exceed number of bits in char (for shifts) -// avoid 0 (for / and %) -lane(l, res) = Scalar<V>((l)%7+1); -return res; -} - -template<class T> -static T leftScalar() -{ -return T(42); -} - -template<class T> -static T rightScalar() -{ -// do not exceed number of bits in char (for shifts) -// avoid 0 (for / and %) -return T(5); -} - -template<class Call> -using CanCall = Impl::CanCall<Call>; - -template<class Dst, class Src> -using CopyRefQual = Impl::CopyRefQual<Dst, Src>; - -// test whether the Op supports the operation on scalars. We do not use -// `lane()` to obtain the scalars, because that might return a proxy -// object, and we are interested in what exactly the scalar type can do, -// no a proxy that might have more overloads than needed. In addition, -// `lane()` may not preserve `const` and reference qualifiers. -template<class Op, class... Vectors> -using ScalarResult = -decltype(std::declval<Op>(). -scalar(std::declval<CopyRefQual<Scalar<Vectors>, -Vectors> >()...)); - -////////////////////////////////////////////////////////////////////// -// -// Check associated types -// - -template<class V> -void checkScalar() -{ -// check that the type Scalar<V> exists -using T = Scalar<V>; - -static_assert(std::is_same<T, std::decay_t<T> >::value, "Scalar types " -"must not be references, and must not include " -"cv-qualifiers"); -T DUNE_UNUSED a{}; -} - -template<class V> -[[deprecated("Warning: please include bool in the Rebinds for " -"simd type V, as Masks are not checked otherwise.")]] -void warnMissingMaskRebind(std::true_type) {} -template<class V> -void warnMissingMaskRebind(std::false_type) {} - -template<class V, class Rebinds, template<class> class RebindPrune, -template<class> class RebindAccept, class Recurse> -void checkRebindOf(Recurse recurse) -{ -Hybrid::forEach(Rebinds{}, [this,recurse](auto target) { -using T = typename decltype(target)::type; - -// check that the rebound type exists -using W = Rebind<T, V>; -log_ << "Type " << className<V>() << " rebound to " -<< className<T>() << " is " << className<W>() << std::endl; - -static_assert(std::is_same<W, std::decay_t<W> >::value, "Rebound " -"types must not be references, and must not include " -"cv-qualifiers"); -static_assert(lanes<V>() == lanes<W>(), "Rebound types must have " -"the same number of lanes as the original vector " -"types"); -static_assert(std::is_same<T, Scalar<W> >::value, "Rebound types " -"must have the bound-to scalar type"); - -if constexpr (RebindPrune<W>{}) { -log_ << "Pruning check of Simd type " << className<W>() -<< std::endl; -} -else { -using Impl::debugTypes; -static_assert(debugTypes<T, V, W>(RebindAccept<W>{}), -"Rebind<T, V> is W, but that is not accepted " -"by RebindAccept"); -recurse(MetaType<W>{}); -} -}); - -static_assert(std::is_same<Rebind<Scalar<V>, V>, V>::value, "A type " -"rebound to its own scalar type must be the same type " -"as the original type"); -static_assert(std::is_same<Rebind<bool, V>, Mask<V> >::value, "A type " -"rebound to bool must be the mask type for that type"); - -constexpr bool hasBool = Impl::TypeInList<bool, Rebinds>::value; -warnMissingMaskRebind<V>(Std::bool_constant<!hasBool>{}); -} - -////////////////////////////////////////////////////////////////////// -// -// Fundamental checks -// - -template<class V> -void checkLanes() -{ -// check lanes -static_assert(std::is_same<std::size_t, decltype(lanes<V>())>::value, -"return type of lanes<V>() should be std::size_t"); -static_assert(std::is_same<std::size_t, decltype(lanes(V{}))>::value, -"return type of lanes(V{}) should be std::size_t"); - -// the result of lanes<V>() must be constexpr -constexpr auto DUNE_UNUSED size = lanes<V>(); -// but the result of lanes(vec) does not need to be constexpr -DUNE_SIMD_CHECK(lanes<V>() == lanes(V{})); -} - -template<class V> -void checkDefaultConstruct() -{ -{ V DUNE_UNUSED vec; } -{ V DUNE_UNUSED vec{}; } -{ V DUNE_UNUSED vec = {}; } -} - -template<class V> -void checkLane() -{ -// Avoid using broadcast<V>() for initialization to avoid test -// interdependencies. -V vec(Scalar<V>(0)); -// check lane() on mutable lvalues -for(std::size_t l = 0; l < lanes(vec); ++l) -lane(l, vec) = l + 1; -for(std::size_t l = 0; l < lanes(vec); ++l) -DUNE_SIMD_CHECK(lane(l, vec) == Scalar<V>(l + 1)); -using MLRes = decltype(lane(0, vec)); -static_assert(std::is_same<MLRes, Scalar<V>&>::value || -std::is_same<MLRes, std::decay_t<MLRes> >::value, -"Result of lane() on a mutable lvalue vector must " -"either be a mutable reference to a scalar of that " -"vector or a proxy object (which itself may not be a " -"reference nor const)."); - -// check lane() on const lvalues -const V &vec2 = vec; -for(std::size_t l = 0; l < lanes(vec); ++l) -DUNE_SIMD_CHECK(lane(l, vec2) == Scalar<V>(l + 1)); -using CLRes = decltype(lane(0, vec2)); -static_assert(std::is_same<CLRes, const Scalar<V>&>::value || -std::is_same<CLRes, std::decay_t<CLRes> >::value, -"Result of lane() on a const lvalue vector must " -"either be a const lvalue reference to a scalar of that " -"vector or a proxy object (which itself may not be a " -"reference nor const)."); -static_assert(!std::is_assignable<CLRes, Scalar<V> >::value, -"Result of lane() on a const lvalue vector must not be " -"assignable from a scalar."); - -// check lane() on rvalues -for(std::size_t l = 0; l < lanes(vec); ++l) -DUNE_SIMD_CHECK(lane(l, prvalue(vec)) == Scalar<V>(l + 1)); -using RRes = decltype(lane(0, prvalue(vec))); -// TODO: do we really want to allow Scalar<V>&& here? If we allow it, -// then `auto &&res = lane(0, vec*vec);` creates a dangling reference, -// and the scalar (and even the vector types) are small enough to be -// passed in registers anyway. On the other hand, the only comparable -// accessor function in the standard library that I can think of is -// std::get(), and that does return an rvalue reference in this -// situation. However, that cannot assume anything about the size of -// the returned types. -static_assert(std::is_same<RRes, Scalar<V> >::value || -std::is_same<RRes, Scalar<V>&&>::value, -"Result of lane() on a rvalue vector V must be " -"Scalar<V> or Scalar<V>&&."); -// Can't assert non-assignable, fails for any typical class, -// e.g. std::complex<>. Would need to return const Scalar<V> or const -// Scalar<V>&&, which would inhibit moving from the return value. -// static_assert(!std::is_assignable<RRes, Scalar<V> >::value, -// "Result of lane() on a rvalue vector must not be " -// "assignable from a scalar."); -} - -// check non-default constructors -template<class V> -void checkCopyMoveConstruct() -{ -// elided copy/move constructors -{ V vec (make123<V>()); DUNE_SIMD_CHECK(is123(vec)); } -{ V vec = make123<V>() ; DUNE_SIMD_CHECK(is123(vec)); } -{ V vec {make123<V>()}; DUNE_SIMD_CHECK(is123(vec)); } -{ V vec = {make123<V>()}; DUNE_SIMD_CHECK(is123(vec)); } - -// copy constructors -{ V ref(make123<V>()); V vec (ref); -DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } -{ V ref(make123<V>()); V vec = ref ; -DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } -{ V ref(make123<V>()); V vec {ref}; -DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } -{ V ref(make123<V>()); V vec = {ref}; -DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } -{ const V ref(make123<V>()); V vec (ref); -DUNE_SIMD_CHECK(is123(vec)); } -{ const V ref(make123<V>()); V vec = ref ; -DUNE_SIMD_CHECK(is123(vec)); } -{ const V ref(make123<V>()); V vec {ref}; -DUNE_SIMD_CHECK(is123(vec)); } -{ const V ref(make123<V>()); V vec = {ref}; -DUNE_SIMD_CHECK(is123(vec)); } - -// move constructors -{ V ref(make123<V>()); V vec (std::move(ref)); -DUNE_SIMD_CHECK(is123(vec)); } -{ V ref(make123<V>()); V vec = std::move(ref) ; -DUNE_SIMD_CHECK(is123(vec)); } -{ V ref(make123<V>()); V vec {std::move(ref)}; -DUNE_SIMD_CHECK(is123(vec)); } -{ V ref(make123<V>()); V vec = {std::move(ref)}; -DUNE_SIMD_CHECK(is123(vec)); } -} - -template<class V> -void checkBroadcastVectorConstruct() -{ -// broadcast copy constructors -{ Scalar<V> ref = 42; V vec (ref); -DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -{ Scalar<V> ref = 42; V vec = ref ; -DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -// { Scalar<V> ref = 42; V vec {ref}; -// DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -// { Scalar<V> ref = 42; V vec = {ref}; -// DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -{ const Scalar<V> ref = 42; V vec (ref); -DUNE_SIMD_CHECK(is42(vec)); } -{ const Scalar<V> ref = 42; V vec = ref ; -DUNE_SIMD_CHECK(is42(vec)); } -// { const Scalar<V> ref = 42; V vec {ref}; -// DUNE_SIMD_CHECK(is42(vec)); } -// { const Scalar<V> ref = 42; V vec = {ref}; -// DUNE_SIMD_CHECK(is42(vec)); } - -// broadcast move constructors -{ Scalar<V> ref = 42; V vec (std::move(ref)); -DUNE_SIMD_CHECK(is42(vec)); } -{ Scalar<V> ref = 42; V vec = std::move(ref) ; -DUNE_SIMD_CHECK(is42(vec)); } -// { Scalar<V> ref = 42; V vec {std::move(ref)}; -// DUNE_SIMD_CHECK(is42(vec)); } -// { Scalar<V> ref = 42; V vec = {std::move(ref)}; -// DUNE_SIMD_CHECK(is42(vec)); } -} - -template<class V> -void checkBroadcastMaskConstruct() -{ -// broadcast copy constructors -{ Scalar<V> ref = 42; V vec (ref); -DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -// { Scalar<V> ref = 42; V vec = ref ; -// DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -{ Scalar<V> ref = 42; V vec {ref}; -DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -// { Scalar<V> ref = 42; V vec = {ref}; -// DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -{ const Scalar<V> ref = 42; V vec (ref); -DUNE_SIMD_CHECK(is42(vec)); } -// { const Scalar<V> ref = 42; V vec = ref ; -// DUNE_SIMD_CHECK(is42(vec)); } -{ const Scalar<V> ref = 42; V vec {ref}; -DUNE_SIMD_CHECK(is42(vec)); } -// { const Scalar<V> ref = 42; V vec = {ref}; -// DUNE_SIMD_CHECK(is42(vec)); } - -// broadcast move constructors -{ Scalar<V> ref = 42; V vec (std::move(ref)); -DUNE_SIMD_CHECK(is42(vec)); } -// { Scalar<V> ref = 42; V vec = std::move(ref) ; -// DUNE_SIMD_CHECK(is42(vec)); } -{ Scalar<V> ref = 42; V vec {std::move(ref)}; -DUNE_SIMD_CHECK(is42(vec)); } -// { Scalar<V> ref = 42; V vec = {std::move(ref)}; -// DUNE_SIMD_CHECK(is42(vec)); } -} - -// check the implCast function -template<class FromV, class ToV> -void checkImplCast() -{ -{ // lvalue arg -FromV fromVec = make123<FromV>(); -auto toVec = implCast<ToV>(fromVec); -static_assert(std::is_same<decltype(toVec), ToV>::value, -"Unexpected result type for implCast<ToV>(FromV&)"); -DUNE_SIMD_CHECK(is123(fromVec)); -DUNE_SIMD_CHECK(is123(toVec)); -} - -{ // const lvalue arg -const FromV fromVec = make123<FromV>(); -auto toVec = implCast<ToV>(fromVec); -static_assert(std::is_same<decltype(toVec), ToV>::value, -"Unexpected result type for implCast<ToV>(const " -"FromV&)"); -DUNE_SIMD_CHECK(is123(toVec)); -} - -{ // rvalue arg -auto toVec = implCast<ToV>(make123<FromV>()); -static_assert(std::is_same<decltype(toVec), ToV>::value, -"Unexpected result type for implCast<ToV>(FromV&&)"); -DUNE_SIMD_CHECK(is123(toVec)); -} -} - -// check the implCast function -template<class V> -void checkImplCast() -{ -// check against LoopSIMD -using LoopV = Dune::LoopSIMD<Scalar<V>, lanes<V>()>; - -checkImplCast<V, V>(); -checkImplCast<V, LoopV>(); -checkImplCast<LoopV, V>(); -} - -// check the broadcast function -template<class V> -void checkBroadcast() -{ -// broadcast function -{ // lvalue arg -Scalar<V> ref = 42; -auto vec = broadcast<V>(ref); -static_assert(std::is_same<decltype(vec), V>::value, -"Unexpected result type for broadcast<V>()"); -DUNE_SIMD_CHECK(is42(vec)); -DUNE_SIMD_CHECK(ref == Scalar<V>(42)); -} - -{ // const lvalue arg -const Scalar<V> ref = 42; -auto vec = broadcast<V>(ref); -static_assert(std::is_same<decltype(vec), V>::value, -"Unexpected result type for broadcast<V>()"); -DUNE_SIMD_CHECK(is42(vec)); -} - -{ // rvalue arg -auto vec = broadcast<V>(Scalar<V>(42)); -static_assert(std::is_same<decltype(vec), V>::value, -"Unexpected result type for broadcast<V>()"); -DUNE_SIMD_CHECK(is42(vec)); -} - -{ // int arg -auto vec = broadcast<V>(42); -static_assert(std::is_same<decltype(vec), V>::value, -"Unexpected result type for broadcast<V>()"); -DUNE_SIMD_CHECK(is42(vec)); -} - -{ // double arg -auto vec = broadcast<V>(42.0); -static_assert(std::is_same<decltype(vec), V>::value, -"Unexpected result type for broadcast<V>()"); -DUNE_SIMD_CHECK(is42(vec)); -} -} - -template<class V> -void checkBracedAssign() -{ -// copy assignment -{ V ref = make123<V>(); V vec; vec = {ref}; -DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } -{ const V ref = make123<V>(); V vec; vec = {ref}; -DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } - -// move assignment -{ V vec; vec = {make123<V>()}; DUNE_SIMD_CHECK(is123(vec)); } -} - -template<class V> -void checkBracedBroadcastAssign() -{ -// nothing works here -// // broadcast copy assignment -// { Scalar<V> ref = 42; V vec; vec = {ref}; -// DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } -// { const Scalar<V> ref = 42; V vec; vec = {ref}; -// DUNE_SIMD_CHECK(is42(vec)); } - -// // broadcast move assignment -// { Scalar<V> ref = 42; V vec; vec = {std::move(ref)}; -// DUNE_SIMD_CHECK(is42(vec)); } -} - -////////////////////////////////////////////////////////////////////// -// -// checks for unary operators -// + ((expr) ? void() : complain(__FILE__, __LINE__, __func__, \ + DUNE_SIMD_OPNAME, #expr)) + + // "cast" into a prvalue + template<class T> + static std::decay_t<T> prvalue(T &&t) + { + return std::forward<T>(t); + } + + // whether the vector is 42 in all lanes + template<class V> + static bool is42(const V &v) + { + bool good = true; + + for(std::size_t l = 0; l < lanes(v); ++l) + // need to cast in case we have a mask type + good &= (lane(l, v) == Scalar<V>(42)); + + return good; + } + + // make a vector that contains the sequence { 1, 2, ... } + template<class V> + static V make123() + { + // initialize to avoid undefined behaviour if assigning to lane() + // involves lvalue-to-rvalue conversions, e.g. due to bitmask + // operations. Avoid using broadcast<V>() for initialization to avoid + // test interdependencies. + V vec(Scalar<V>(0)); + for(std::size_t l = 0; l < lanes(vec); ++l) + lane(l, vec) = l + 1; + return vec; + } + + // whether the vector contains the sequence { 1, 2, ... } + template<class V> + static bool is123(const V &v) + { + bool good = true; + + for(std::size_t l = 0; l < lanes(v); ++l) + // need to cast in case we have a mask type + good &= (lane(l, v) == Scalar<V>(l+1)); + + return good; + } + + template<class V> + static V leftVector() + { + // Avoid using broadcast<V>() for initialization to avoid test + // interdependencies. + V res(Scalar<V>(0)); + for(std::size_t l = 0; l < lanes(res); ++l) + lane(l, res) = Scalar<V>(l+1); + return res; + } + + template<class V> + static V rightVector() + { + // Avoid using broadcast<V>() for initialization to avoid test + // interdependencies. + V res(Scalar<V>(0)); + for(std::size_t l = 0; l < lanes(res); ++l) + // do not exceed number of bits in char (for shifts) + // avoid 0 (for / and %) + lane(l, res) = Scalar<V>((l)%7+1); + return res; + } + + template<class T> + static T leftScalar() + { + return T(42); + } + + template<class T> + static T rightScalar() + { + // do not exceed number of bits in char (for shifts) + // avoid 0 (for / and %) + return T(5); + } + + template<class Call> + using CanCall = Impl::CanCall<Call>; + + template<class Dst, class Src> + using CopyRefQual = Impl::CopyRefQual<Dst, Src>; + + // test whether the Op supports the operation on scalars. We do not use + // `lane()` to obtain the scalars, because that might return a proxy + // object, and we are interested in what exactly the scalar type can do, + // no a proxy that might have more overloads than needed. In addition, + // `lane()` may not preserve `const` and reference qualifiers. + template<class Op, class... Vectors> + using ScalarResult = + decltype(std::declval<Op>(). + scalar(std::declval<CopyRefQual<Scalar<Vectors>, + Vectors> >()...)); + + ////////////////////////////////////////////////////////////////////// + // + // Check associated types + // + + template<class V> + void checkScalar() + { + // check that the type Scalar<V> exists + using T = Scalar<V>; + + static_assert(std::is_same<T, std::decay_t<T> >::value, "Scalar types " + "must not be references, and must not include " + "cv-qualifiers"); + T DUNE_UNUSED a{}; + } + + template<class V> + [[deprecated("Warning: please include bool in the Rebinds for " + "simd type V, as Masks are not checked otherwise.")]] + void warnMissingMaskRebind(std::true_type) {} + template<class V> + void warnMissingMaskRebind(std::false_type) {} + + template<class V, class Rebinds, template<class> class RebindPrune, + template<class> class RebindAccept, class Recurse> + void checkRebindOf(Recurse recurse) + { + Hybrid::forEach(Rebinds{}, [this,recurse](auto target) { + using T = typename decltype(target)::type; + + // check that the rebound type exists + using W = Rebind<T, V>; + log_ << "Type " << className<V>() << " rebound to " + << className<T>() << " is " << className<W>() << std::endl; + + static_assert(std::is_same<W, std::decay_t<W> >::value, "Rebound " + "types must not be references, and must not include " + "cv-qualifiers"); + static_assert(lanes<V>() == lanes<W>(), "Rebound types must have " + "the same number of lanes as the original vector " + "types"); + static_assert(std::is_same<T, Scalar<W> >::value, "Rebound types " + "must have the bound-to scalar type"); + + if constexpr (RebindPrune<W>{}) { + log_ << "Pruning check of Simd type " << className<W>() + << std::endl; + } + else { + using Impl::debugTypes; + static_assert(debugTypes<T, V, W>(RebindAccept<W>{}), + "Rebind<T, V> is W, but that is not accepted " + "by RebindAccept"); + recurse(MetaType<W>{}); + } + }); + + static_assert(std::is_same<Rebind<Scalar<V>, V>, V>::value, "A type " + "rebound to its own scalar type must be the same type " + "as the original type"); + static_assert(std::is_same<Rebind<bool, V>, Mask<V> >::value, "A type " + "rebound to bool must be the mask type for that type"); + + constexpr bool hasBool = Impl::TypeInList<bool, Rebinds>::value; + warnMissingMaskRebind<V>(Std::bool_constant<!hasBool>{}); + } + + ////////////////////////////////////////////////////////////////////// + // + // Fundamental checks + // + + template<class V> + void checkLanes() + { + // check lanes + static_assert(std::is_same<std::size_t, decltype(lanes<V>())>::value, + "return type of lanes<V>() should be std::size_t"); + static_assert(std::is_same<std::size_t, decltype(lanes(V{}))>::value, + "return type of lanes(V{}) should be std::size_t"); + + // the result of lanes<V>() must be constexpr + constexpr auto DUNE_UNUSED size = lanes<V>(); + // but the result of lanes(vec) does not need to be constexpr + DUNE_SIMD_CHECK(lanes<V>() == lanes(V{})); + } + + template<class V> + void checkDefaultConstruct() + { + { V DUNE_UNUSED vec; } + { V DUNE_UNUSED vec{}; } + { V DUNE_UNUSED vec = {}; } + } + + template<class V> + void checkLane() + { + // Avoid using broadcast<V>() for initialization to avoid test + // interdependencies. + V vec(Scalar<V>(0)); + // check lane() on mutable lvalues + for(std::size_t l = 0; l < lanes(vec); ++l) + lane(l, vec) = l + 1; + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(lane(l, vec) == Scalar<V>(l + 1)); + using MLRes = decltype(lane(0, vec)); + static_assert(std::is_same<MLRes, Scalar<V>&>::value || + std::is_same<MLRes, std::decay_t<MLRes> >::value, + "Result of lane() on a mutable lvalue vector must " + "either be a mutable reference to a scalar of that " + "vector or a proxy object (which itself may not be a " + "reference nor const)."); + + // check lane() on const lvalues + const V &vec2 = vec; + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(lane(l, vec2) == Scalar<V>(l + 1)); + using CLRes = decltype(lane(0, vec2)); + static_assert(std::is_same<CLRes, const Scalar<V>&>::value || + std::is_same<CLRes, std::decay_t<CLRes> >::value, + "Result of lane() on a const lvalue vector must " + "either be a const lvalue reference to a scalar of that " + "vector or a proxy object (which itself may not be a " + "reference nor const)."); + static_assert(!std::is_assignable<CLRes, Scalar<V> >::value, + "Result of lane() on a const lvalue vector must not be " + "assignable from a scalar."); + + // check lane() on rvalues + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(lane(l, prvalue(vec)) == Scalar<V>(l + 1)); + using RRes = decltype(lane(0, prvalue(vec))); + // TODO: do we really want to allow Scalar<V>&& here? If we allow it, + // then `auto &&res = lane(0, vec*vec);` creates a dangling reference, + // and the scalar (and even the vector types) are small enough to be + // passed in registers anyway. On the other hand, the only comparable + // accessor function in the standard library that I can think of is + // std::get(), and that does return an rvalue reference in this + // situation. However, that cannot assume anything about the size of + // the returned types. + static_assert(std::is_same<RRes, Scalar<V> >::value || + std::is_same<RRes, Scalar<V>&&>::value, + "Result of lane() on a rvalue vector V must be " + "Scalar<V> or Scalar<V>&&."); + // Can't assert non-assignable, fails for any typical class, + // e.g. std::complex<>. Would need to return const Scalar<V> or const + // Scalar<V>&&, which would inhibit moving from the return value. + // static_assert(!std::is_assignable<RRes, Scalar<V> >::value, + // "Result of lane() on a rvalue vector must not be " + // "assignable from a scalar."); + } + + // check non-default constructors + template<class V> + void checkCopyMoveConstruct() + { + // elided copy/move constructors + { V vec (make123<V>()); DUNE_SIMD_CHECK(is123(vec)); } + { V vec = make123<V>() ; DUNE_SIMD_CHECK(is123(vec)); } + { V vec {make123<V>()}; DUNE_SIMD_CHECK(is123(vec)); } + { V vec = {make123<V>()}; DUNE_SIMD_CHECK(is123(vec)); } + + // copy constructors + { V ref(make123<V>()); V vec (ref); + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { V ref(make123<V>()); V vec = ref ; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { V ref(make123<V>()); V vec {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { V ref(make123<V>()); V vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { const V ref(make123<V>()); V vec (ref); + DUNE_SIMD_CHECK(is123(vec)); } + { const V ref(make123<V>()); V vec = ref ; + DUNE_SIMD_CHECK(is123(vec)); } + { const V ref(make123<V>()); V vec {ref}; + DUNE_SIMD_CHECK(is123(vec)); } + { const V ref(make123<V>()); V vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); } + + // move constructors + { V ref(make123<V>()); V vec (std::move(ref)); + DUNE_SIMD_CHECK(is123(vec)); } + { V ref(make123<V>()); V vec = std::move(ref) ; + DUNE_SIMD_CHECK(is123(vec)); } + { V ref(make123<V>()); V vec {std::move(ref)}; + DUNE_SIMD_CHECK(is123(vec)); } + { V ref(make123<V>()); V vec = {std::move(ref)}; + DUNE_SIMD_CHECK(is123(vec)); } + } + + template<class V> + void checkBroadcastVectorConstruct() + { + // broadcast copy constructors + { Scalar<V> ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + { Scalar<V> ref = 42; V vec = ref ; + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + // { Scalar<V> ref = 42; V vec {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + // { Scalar<V> ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + { const Scalar<V> ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); } + { const Scalar<V> ref = 42; V vec = ref ; + DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar<V> ref = 42; V vec {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar<V> ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + + // broadcast move constructors + { Scalar<V> ref = 42; V vec (std::move(ref)); + DUNE_SIMD_CHECK(is42(vec)); } + { Scalar<V> ref = 42; V vec = std::move(ref) ; + DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar<V> ref = 42; V vec {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar<V> ref = 42; V vec = {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + } + + template<class V> + void checkBroadcastMaskConstruct() + { + // broadcast copy constructors + { Scalar<V> ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + // { Scalar<V> ref = 42; V vec = ref ; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + { Scalar<V> ref = 42; V vec {ref}; + DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + // { Scalar<V> ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + { const Scalar<V> ref = 42; V vec (ref); + DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar<V> ref = 42; V vec = ref ; + // DUNE_SIMD_CHECK(is42(vec)); } + { const Scalar<V> ref = 42; V vec {ref}; + DUNE_SIMD_CHECK(is42(vec)); } + // { const Scalar<V> ref = 42; V vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + + // broadcast move constructors + { Scalar<V> ref = 42; V vec (std::move(ref)); + DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar<V> ref = 42; V vec = std::move(ref) ; + // DUNE_SIMD_CHECK(is42(vec)); } + { Scalar<V> ref = 42; V vec {std::move(ref)}; + DUNE_SIMD_CHECK(is42(vec)); } + // { Scalar<V> ref = 42; V vec = {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + } + + // check the implCast function + template<class FromV, class ToV> + void checkImplCast() + { + { // lvalue arg + FromV fromVec = make123<FromV>(); + auto toVec = implCast<ToV>(fromVec); + static_assert(std::is_same<decltype(toVec), ToV>::value, + "Unexpected result type for implCast<ToV>(FromV&)"); + DUNE_SIMD_CHECK(is123(fromVec)); + DUNE_SIMD_CHECK(is123(toVec)); + } + + { // const lvalue arg + const FromV fromVec = make123<FromV>(); + auto toVec = implCast<ToV>(fromVec); + static_assert(std::is_same<decltype(toVec), ToV>::value, + "Unexpected result type for implCast<ToV>(const " + "FromV&)"); + DUNE_SIMD_CHECK(is123(toVec)); + } + + { // rvalue arg + auto toVec = implCast<ToV>(make123<FromV>()); + static_assert(std::is_same<decltype(toVec), ToV>::value, + "Unexpected result type for implCast<ToV>(FromV&&)"); + DUNE_SIMD_CHECK(is123(toVec)); + } + } + + // check the implCast function + template<class V> + void checkImplCast() + { + // check against LoopSIMD + using LoopV = Dune::LoopSIMD<Scalar<V>, lanes<V>()>; + + checkImplCast<V, V>(); + checkImplCast<V, LoopV>(); + checkImplCast<LoopV, V>(); + } + + // check the broadcast function + template<class V> + void checkBroadcast() + { + // broadcast function + { // lvalue arg + Scalar<V> ref = 42; + auto vec = broadcast<V>(ref); + static_assert(std::is_same<decltype(vec), V>::value, + "Unexpected result type for broadcast<V>()"); + DUNE_SIMD_CHECK(is42(vec)); + DUNE_SIMD_CHECK(ref == Scalar<V>(42)); + } + + { // const lvalue arg + const Scalar<V> ref = 42; + auto vec = broadcast<V>(ref); + static_assert(std::is_same<decltype(vec), V>::value, + "Unexpected result type for broadcast<V>()"); + DUNE_SIMD_CHECK(is42(vec)); + } + + { // rvalue arg + auto vec = broadcast<V>(Scalar<V>(42)); + static_assert(std::is_same<decltype(vec), V>::value, + "Unexpected result type for broadcast<V>()"); + DUNE_SIMD_CHECK(is42(vec)); + } + + { // int arg + auto vec = broadcast<V>(42); + static_assert(std::is_same<decltype(vec), V>::value, + "Unexpected result type for broadcast<V>()"); + DUNE_SIMD_CHECK(is42(vec)); + } + + { // double arg + auto vec = broadcast<V>(42.0); + static_assert(std::is_same<decltype(vec), V>::value, + "Unexpected result type for broadcast<V>()"); + DUNE_SIMD_CHECK(is42(vec)); + } + } + + template<class V> + void checkBracedAssign() + { + // copy assignment + { V ref = make123<V>(); V vec; vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + { const V ref = make123<V>(); V vec; vec = {ref}; + DUNE_SIMD_CHECK(is123(vec)); DUNE_SIMD_CHECK(is123(ref)); } + + // move assignment + { V vec; vec = {make123<V>()}; DUNE_SIMD_CHECK(is123(vec)); } + } + + template<class V> + void checkBracedBroadcastAssign() + { + // nothing works here + // // broadcast copy assignment + // { Scalar<V> ref = 42; V vec; vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); DUNE_SIMD_CHECK(ref == Scalar<V>(42)); } + // { const Scalar<V> ref = 42; V vec; vec = {ref}; + // DUNE_SIMD_CHECK(is42(vec)); } + + // // broadcast move assignment + // { Scalar<V> ref = 42; V vec; vec = {std::move(ref)}; + // DUNE_SIMD_CHECK(is42(vec)); } + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for unary operators + // #define DUNE_SIMD_POSTFIX_OP(NAME, SYMBOL) \ -struct OpPostfix##NAME \ -{ \ -template<class V> \ -auto operator()(V&& v) const \ --> decltype(std::forward<V>(v) SYMBOL) \ -{ \ -return std::forward<V>(v) SYMBOL; \ -} \ -} + struct OpPostfix##NAME \ + { \ + template<class V> \ + auto operator()(V&& v) const \ + -> decltype(std::forward<V>(v) SYMBOL) \ + { \ + return std::forward<V>(v) SYMBOL; \ + } \ + } #define DUNE_SIMD_PREFIX_OP(NAME, SYMBOL) \ -struct OpPrefix##NAME \ -{ \ -template<class V> \ -auto operator()(V&& v) const \ --> decltype(SYMBOL std::forward<V>(v)) \ -{ \ -return SYMBOL std::forward<V>(v); \ -} \ -} - -DUNE_SIMD_POSTFIX_OP(Decrement, -- ); -DUNE_SIMD_POSTFIX_OP(Increment, ++ ); - -DUNE_SIMD_PREFIX_OP (Decrement, -- ); -DUNE_SIMD_PREFIX_OP (Increment, ++ ); - -DUNE_SIMD_PREFIX_OP (Plus, + ); -DUNE_SIMD_PREFIX_OP (Minus, - ); -DUNE_SIMD_PREFIX_OP (LogicNot, ! ); -// Do not warn about ~ being applied to bool. (1) Yes, doing that is -// weird, but we do want to test the weird stuff too. (2) It avoids -// running into <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82040> on -// g++-7.0 through 7.2. Also, ignore -Wpragmas to not warn about an -// unknown -Wbool-operation on compilers that do not know that option. + struct OpPrefix##NAME \ + { \ + template<class V> \ + auto operator()(V&& v) const \ + -> decltype(SYMBOL std::forward<V>(v)) \ + { \ + return SYMBOL std::forward<V>(v); \ + } \ + } + + DUNE_SIMD_POSTFIX_OP(Decrement, -- ); + DUNE_SIMD_POSTFIX_OP(Increment, ++ ); + + DUNE_SIMD_PREFIX_OP (Decrement, -- ); + DUNE_SIMD_PREFIX_OP (Increment, ++ ); + + DUNE_SIMD_PREFIX_OP (Plus, + ); + DUNE_SIMD_PREFIX_OP (Minus, - ); + DUNE_SIMD_PREFIX_OP (LogicNot, ! ); + // Do not warn about ~ being applied to bool. (1) Yes, doing that is + // weird, but we do want to test the weird stuff too. (2) It avoids + // running into <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82040> on + // g++-7.0 through 7.2. Also, ignore -Wpragmas to not warn about an + // unknown -Wbool-operation on compilers that do not know that option. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wunknown-warning-option" // clang 6.0.1 #pragma GCC diagnostic ignored "-Wbool-operation" -DUNE_SIMD_PREFIX_OP (BitNot, ~ ); + DUNE_SIMD_PREFIX_OP (BitNot, ~ ); #pragma GCC diagnostic pop #undef DUNE_SIMD_POSTFIX_OP #undef DUNE_SIMD_PREFIX_OP -template<class V, class Op> -std::enable_if_t< -CanCall<Op(decltype(lane(0, std::declval<V>())))>::value> -checkUnaryOpV(Op op) -{ + template<class V, class Op> + std::enable_if_t< + CanCall<Op(decltype(lane(0, std::declval<V>())))>::value> + checkUnaryOpV(Op op) + { #define DUNE_SIMD_OPNAME (className<Op(V)>()) -// arguments -auto val = leftVector<std::decay_t<V>>(); - -// copy the arguments in case V is a references -auto arg = val; -auto &&result = op(static_cast<V>(arg)); -using T = Scalar<std::decay_t<decltype(result)> >; -for(std::size_t l = 0; l < lanes(val); ++l) -{ -// `op` might promote the argument. This is a problem if the -// argument of the operation on the right of the `==` is -// e.g. `(unsigned short)1` and the operation is e.g. unary `-`. -// Then the argument is promoted to `int` before applying the -// negation, and the result is `(int)-1`. However, the left side of -// the `==` is still `(unsigned short)-1`, which typically is the -// same as `(unsigned short)65535`. The `==` promotes the left side -// before comparing, so that becomes `(int)65535`. It will then -// compare `(int)65535` and `(int)-1` and rightly declare them to be -// not equal. - -// To work around this, we explicitly convert the right side of the -// `==` to the scalar type before comparing. -DUNE_SIMD_CHECK_OP -(lane(l, result) -== static_cast<T>(op(lane(l, static_cast<V>(val))))); -} -// op might modify val, verify that any such modification also happens -// in the vector case -for(std::size_t l = 0; l < lanes<std::decay_t<V> >(); ++l) -DUNE_SIMD_CHECK_OP(lane(l, val) == lane(l, arg)); + // arguments + auto val = leftVector<std::decay_t<V>>(); + + // copy the arguments in case V is a references + auto arg = val; + auto &&result = op(static_cast<V>(arg)); + using T = Scalar<std::decay_t<decltype(result)> >; + for(std::size_t l = 0; l < lanes(val); ++l) + { + // `op` might promote the argument. This is a problem if the + // argument of the operation on the right of the `==` is + // e.g. `(unsigned short)1` and the operation is e.g. unary `-`. + // Then the argument is promoted to `int` before applying the + // negation, and the result is `(int)-1`. However, the left side of + // the `==` is still `(unsigned short)-1`, which typically is the + // same as `(unsigned short)65535`. The `==` promotes the left side + // before comparing, so that becomes `(int)65535`. It will then + // compare `(int)65535` and `(int)-1` and rightly declare them to be + // not equal. + + // To work around this, we explicitly convert the right side of the + // `==` to the scalar type before comparing. + DUNE_SIMD_CHECK_OP + (lane(l, result) + == static_cast<T>(op(lane(l, static_cast<V>(val))))); + } + // op might modify val, verify that any such modification also happens + // in the vector case + for(std::size_t l = 0; l < lanes<std::decay_t<V> >(); ++l) + DUNE_SIMD_CHECK_OP(lane(l, val) == lane(l, arg)); #undef DUNE_SIMD_OPNAME -} - -template<class V, class Op> -std::enable_if_t< -!CanCall<Op(decltype(lane(0, std::declval<V>())))>::value> -checkUnaryOpV(Op op) -{ -// log_ << "No " << className<Op(decltype(lane(0, std::declval<V>())))>() -// << std::endl -// << " ==> Not checking " << className<Op(V)>() << std::endl; -} - -template<class V, class Op> -void checkUnaryOpsV(Op op) -{ -checkUnaryOpV<V&>(op); -checkUnaryOpV<const V&>(op); -checkUnaryOpV<V&&>(op); -} - -////////////////////////////////////////////////////////////////////// -// -// checks for binary operators -// - -// The operators contain an `operator()`, which will be invoked for both -// scalar and vector arguments. The function `scalar()` is used the -// test whether the scalar types support the operation (via -// `ScalarResult`). The difference is that `scalar()` should only ever -// receive `const`-ref-qualified version of `Scalar<V>`, while the -// `operator()` may also be called with proxies representing scalars. + } + + template<class V, class Op> + std::enable_if_t< + !CanCall<Op(decltype(lane(0, std::declval<V>())))>::value> + checkUnaryOpV(Op op) + { + // log_ << "No " << className<Op(decltype(lane(0, std::declval<V>())))>() + // << std::endl + // << " ==> Not checking " << className<Op(V)>() << std::endl; + } + + template<class V, class Op> + void checkUnaryOpsV(Op op) + { + checkUnaryOpV<V&>(op); + checkUnaryOpV<const V&>(op); + checkUnaryOpV<V&&>(op); + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for binary operators + // + + // The operators contain an `operator()`, which will be invoked for both + // scalar and vector arguments. The function `scalar()` is used the + // test whether the scalar types support the operation (via + // `ScalarResult`). The difference is that `scalar()` should only ever + // receive `const`-ref-qualified version of `Scalar<V>`, while the + // `operator()` may also be called with proxies representing scalars. #define DUNE_SIMD_INFIX_OP(NAME, SYMBOL) \ -struct OpInfix##NAME \ -{ \ -template<class V1, class V2> \ -decltype(auto) operator()(V1&& v1, V2&& v2) const \ -{ \ -return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \ -} \ -template<class S1, class S2> \ -auto scalar(S1&& s1, S2&& s2) const \ --> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \ -} - -// for assign ops, accept only non-const lvalue arguments for scalars. -// This is needed for class scalars (e.g. std::complex) because -// non-const class rvalues are actually usually assignable. Though that -// assignment happens to a temporary, and thus is lost. Except that the -// tests would bind the result of the assignment to a reference. And -// because that result is returned from a function by reference, even -// though it is a temporary passed as an argument to that function, -// accessing the result later is undefined behaviour. + struct OpInfix##NAME \ + { \ + template<class V1, class V2> \ + decltype(auto) operator()(V1&& v1, V2&& v2) const \ + { \ + return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \ + } \ + template<class S1, class S2> \ + auto scalar(S1&& s1, S2&& s2) const \ + -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \ + } + + // for assign ops, accept only non-const lvalue arguments for scalars. + // This is needed for class scalars (e.g. std::complex) because + // non-const class rvalues are actually usually assignable. Though that + // assignment happens to a temporary, and thus is lost. Except that the + // tests would bind the result of the assignment to a reference. And + // because that result is returned from a function by reference, even + // though it is a temporary passed as an argument to that function, + // accessing the result later is undefined behaviour. #define DUNE_SIMD_ASSIGN_OP(NAME, SYMBOL) \ -struct OpInfix##NAME \ -{ \ -template<class V1, class V2> \ -decltype(auto) operator()(V1&& v1, V2&& v2) const \ -{ \ -return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \ -} \ -template<class S1, class S2> \ -auto scalar(S1& s1, S2&& s2) const \ --> decltype(s1 SYMBOL std::forward<S2>(s2)); \ -} + struct OpInfix##NAME \ + { \ + template<class V1, class V2> \ + decltype(auto) operator()(V1&& v1, V2&& v2) const \ + { \ + return std::forward<V1>(v1) SYMBOL std::forward<V2>(v2); \ + } \ + template<class S1, class S2> \ + auto scalar(S1& s1, S2&& s2) const \ + -> decltype(s1 SYMBOL std::forward<S2>(s2)); \ + } #define DUNE_SIMD_REPL_OP(NAME, REPLFN, SYMBOL) \ -struct OpInfix##NAME \ -{ \ -template<class V1, class V2> \ -decltype(auto) operator()(V1&& v1, V2&& v2) const \ -{ \ -return Simd::REPLFN(std::forward<V1>(v1), std::forward<V2>(v2)); \ -} \ -template<class S1, class S2> \ -auto scalar(S1&& s1, S2&& s2) const \ --> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \ -} - -DUNE_SIMD_INFIX_OP(Mul, * ); -DUNE_SIMD_INFIX_OP(Div, / ); -DUNE_SIMD_INFIX_OP(Remainder, % ); - -DUNE_SIMD_INFIX_OP(Plus, + ); -DUNE_SIMD_INFIX_OP(Minus, - ); - -DUNE_SIMD_INFIX_OP(LeftShift, << ); -DUNE_SIMD_INFIX_OP(RightShift, >> ); - -DUNE_SIMD_INFIX_OP(Less, < ); -DUNE_SIMD_INFIX_OP(Greater, > ); -DUNE_SIMD_INFIX_OP(LessEqual, <= ); -DUNE_SIMD_INFIX_OP(GreaterEqual, >= ); - -DUNE_SIMD_INFIX_OP(Equal, == ); -DUNE_SIMD_INFIX_OP(NotEqual, != ); - -DUNE_SIMD_INFIX_OP(BitAnd, & ); -DUNE_SIMD_INFIX_OP(BitXor, ^ ); -DUNE_SIMD_INFIX_OP(BitOr, | ); - -// Those are not supported in any meaningful way by vectorclass -// We need to test replacement functions maskAnd() and maskOr() instead. -DUNE_SIMD_REPL_OP(LogicAnd, maskAnd, && ); -DUNE_SIMD_REPL_OP(LogicOr, maskOr, || ); - -DUNE_SIMD_ASSIGN_OP(Assign, = ); -DUNE_SIMD_ASSIGN_OP(AssignMul, *= ); -DUNE_SIMD_ASSIGN_OP(AssignDiv, /= ); -DUNE_SIMD_ASSIGN_OP(AssignRemainder, %= ); -DUNE_SIMD_ASSIGN_OP(AssignPlus, += ); -DUNE_SIMD_ASSIGN_OP(AssignMinus, -= ); -DUNE_SIMD_ASSIGN_OP(AssignLeftShift, <<=); -DUNE_SIMD_ASSIGN_OP(AssignRightShift, >>=); -DUNE_SIMD_ASSIGN_OP(AssignAnd, &= ); -DUNE_SIMD_ASSIGN_OP(AssignXor, ^= ); -DUNE_SIMD_ASSIGN_OP(AssignOr, |= ); + struct OpInfix##NAME \ + { \ + template<class V1, class V2> \ + decltype(auto) operator()(V1&& v1, V2&& v2) const \ + { \ + return Simd::REPLFN(std::forward<V1>(v1), std::forward<V2>(v2)); \ + } \ + template<class S1, class S2> \ + auto scalar(S1&& s1, S2&& s2) const \ + -> decltype(std::forward<S1>(s1) SYMBOL std::forward<S2>(s2)); \ + } + + DUNE_SIMD_INFIX_OP(Mul, * ); + DUNE_SIMD_INFIX_OP(Div, / ); + DUNE_SIMD_INFIX_OP(Remainder, % ); + + DUNE_SIMD_INFIX_OP(Plus, + ); + DUNE_SIMD_INFIX_OP(Minus, - ); + + DUNE_SIMD_INFIX_OP(LeftShift, << ); + DUNE_SIMD_INFIX_OP(RightShift, >> ); + + DUNE_SIMD_INFIX_OP(Less, < ); + DUNE_SIMD_INFIX_OP(Greater, > ); + DUNE_SIMD_INFIX_OP(LessEqual, <= ); + DUNE_SIMD_INFIX_OP(GreaterEqual, >= ); + + DUNE_SIMD_INFIX_OP(Equal, == ); + DUNE_SIMD_INFIX_OP(NotEqual, != ); + + DUNE_SIMD_INFIX_OP(BitAnd, & ); + DUNE_SIMD_INFIX_OP(BitXor, ^ ); + DUNE_SIMD_INFIX_OP(BitOr, | ); + + // Those are not supported in any meaningful way by vectorclass + // We need to test replacement functions maskAnd() and maskOr() instead. + DUNE_SIMD_REPL_OP(LogicAnd, maskAnd, && ); + DUNE_SIMD_REPL_OP(LogicOr, maskOr, || ); + + DUNE_SIMD_ASSIGN_OP(Assign, = ); + DUNE_SIMD_ASSIGN_OP(AssignMul, *= ); + DUNE_SIMD_ASSIGN_OP(AssignDiv, /= ); + DUNE_SIMD_ASSIGN_OP(AssignRemainder, %= ); + DUNE_SIMD_ASSIGN_OP(AssignPlus, += ); + DUNE_SIMD_ASSIGN_OP(AssignMinus, -= ); + DUNE_SIMD_ASSIGN_OP(AssignLeftShift, <<=); + DUNE_SIMD_ASSIGN_OP(AssignRightShift, >>=); + DUNE_SIMD_ASSIGN_OP(AssignAnd, &= ); + DUNE_SIMD_ASSIGN_OP(AssignXor, ^= ); + DUNE_SIMD_ASSIGN_OP(AssignOr, |= ); #undef DUNE_SIMD_INFIX_OP #undef DUNE_SIMD_REPL_OP #undef DUNE_SIMD_ASSIGN_OP -// just used as a tag -struct OpInfixComma {}; + // just used as a tag + struct OpInfixComma {}; -template<class T1, class T2> -void checkCommaOp(const std::decay_t<T1> &val1, -const std::decay_t<T2> &val2) -{ + template<class T1, class T2> + void checkCommaOp(const std::decay_t<T1> &val1, + const std::decay_t<T2> &val2) + { #define DUNE_SIMD_OPNAME (className<OpInfixComma(T1, T2)>()) -static_assert(std::is_same<decltype((std::declval<T1>(), -std::declval<T2>())), T2>::value, -"Type and value category of the comma operator must " -"match that of the second operand"); - -// copy the arguments in case T1 or T2 are references -auto arg1 = val1; -auto arg2 = val2; -// Do not warn that the left side of the comma operator is unused. -// Seems to work for g++-4.9 and clang++-3.8. Appears to be harmless -// for icpc (14 and 17), and icpc does not seem to issue a warning -// anyway. + static_assert(std::is_same<decltype((std::declval<T1>(), + std::declval<T2>())), T2>::value, + "Type and value category of the comma operator must " + "match that of the second operand"); + + // copy the arguments in case T1 or T2 are references + auto arg1 = val1; + auto arg2 = val2; + // Do not warn that the left side of the comma operator is unused. + // Seems to work for g++-4.9 and clang++-3.8. Appears to be harmless + // for icpc (14 and 17), and icpc does not seem to issue a warning + // anyway. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-value" -auto &&result = (static_cast<T1>(arg1), -static_cast<T2>(arg2)); + auto &&result = (static_cast<T1>(arg1), + static_cast<T2>(arg2)); #pragma GCC diagnostic pop -if(std::is_reference<T2>::value) -{ -// comma should return the same object as the second argument for -// lvalues and xvalues -DUNE_SIMD_CHECK_OP(&result == &arg2); -// it should not modify any arguments -DUNE_SIMD_CHECK_OP(allTrue(val1 == arg1)); -DUNE_SIMD_CHECK_OP(allTrue(val2 == arg2)); -} -else -{ -// comma should return the same value as the second argument for -// prvalues -DUNE_SIMD_CHECK_OP(allTrue(result == arg2)); -// it should not modify any arguments -DUNE_SIMD_CHECK_OP(allTrue(val1 == arg1)); -// second argument is a prvalue, any modifications happen to a -// temporary and we can't detect them -} + if(std::is_reference<T2>::value) + { + // comma should return the same object as the second argument for + // lvalues and xvalues + DUNE_SIMD_CHECK_OP(&result == &arg2); + // it should not modify any arguments + DUNE_SIMD_CHECK_OP(allTrue(val1 == arg1)); + DUNE_SIMD_CHECK_OP(allTrue(val2 == arg2)); + } + else + { + // comma should return the same value as the second argument for + // prvalues + DUNE_SIMD_CHECK_OP(allTrue(result == arg2)); + // it should not modify any arguments + DUNE_SIMD_CHECK_OP(allTrue(val1 == arg1)); + // second argument is a prvalue, any modifications happen to a + // temporary and we can't detect them + } #undef DUNE_SIMD_OPNAME -} - -////////////////////////////////////////////////////////////////////// -// -// checks for vector-vector binary operations -// - -// We check the following candidate operation -// -// vopres = vop1 @ vop2 -// -// against the reference operation -// -// arefres[l] = aref1[l] @ aref2[l] foreach l -// -// v... variables are simd-vectors and a... variables are arrays. The -// operation may modify the operands, but if is does the modification -// needs to happen in both the candidate and the reference. -// -// We do the following checks: -// 1. lanes(vopres) == lanes(vop1) -// 2. lane(l, vopres) == arefres[l] foreach l -// 3. lane(l, vop1) == aref1[l] foreach l -// 4. lane(l, vop2) == aref2[l] foreach l -template<class V1, class V2, class Op> -std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, V2> > -checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op) -{ + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for vector-vector binary operations + // + + // We check the following candidate operation + // + // vopres = vop1 @ vop2 + // + // against the reference operation + // + // arefres[l] = aref1[l] @ aref2[l] foreach l + // + // v... variables are simd-vectors and a... variables are arrays. The + // operation may modify the operands, but if is does the modification + // needs to happen in both the candidate and the reference. + // + // We do the following checks: + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == arefres[l] foreach l + // 3. lane(l, vop1) == aref1[l] foreach l + // 4. lane(l, vop2) == aref2[l] foreach l + template<class V1, class V2, class Op> + std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, V2> > + checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op) + { #define DUNE_SIMD_OPNAME (className<Op(V1, V2)>()) -static_assert(std::is_same<std::decay_t<V1>, std::decay_t<V2> >::value, -"Internal testsystem error: called with two types that " -"don't decay to the same thing"); - -// reference arguments -auto vref1 = leftVector<std::decay_t<V1>>(); -auto vref2 = rightVector<std::decay_t<V2>>(); - -// candidate arguments -auto vop1 = vref1; -auto vop2 = vref2; - -// candidate operation -auto &&vopres = op(static_cast<V1>(vop1), static_cast<V2>(vop2)); -using VR = decltype(vopres); - -// check 1. lanes(vopres) == lanes(vop1) -static_assert(lanes<std::decay_t<VR> >() == lanes<std::decay_t<V1> >(), -"The result must have the same number of lanes as the " -"operands."); - -// do the reference operation, and simultaneously -// check 2. lane(l, vopres) == arefres[l] foreach l -using T = Scalar<std::decay_t<VR> >; -for(auto l : range(lanes(vopres))) -{ -// see the lengthy comment in `checkUnaryOpV()` as to why the -// `static_cast` around the `op()` is necessary -DUNE_SIMD_CHECK_OP -(lane(l, vopres) -== static_cast<T>(op(lane(l, static_cast<V1>(vref1)), -lane(l, static_cast<V2>(vref2))))); -} - -// check 3. lane(l, vop1) == aref1[l] foreach l -for(auto l : range(lanes(vop1))) -DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); - -// check 4. lane(l, vop2) == aref2[l] foreach l -for(auto l : range(lanes(vop2))) -DUNE_SIMD_CHECK_OP(lane(l, vop2) == lane(l, vref2)); + static_assert(std::is_same<std::decay_t<V1>, std::decay_t<V2> >::value, + "Internal testsystem error: called with two types that " + "don't decay to the same thing"); + + // reference arguments + auto vref1 = leftVector<std::decay_t<V1>>(); + auto vref2 = rightVector<std::decay_t<V2>>(); + + // candidate arguments + auto vop1 = vref1; + auto vop2 = vref2; + + // candidate operation + auto &&vopres = op(static_cast<V1>(vop1), static_cast<V2>(vop2)); + using VR = decltype(vopres); + + // check 1. lanes(vopres) == lanes(vop1) + static_assert(lanes<std::decay_t<VR> >() == lanes<std::decay_t<V1> >(), + "The result must have the same number of lanes as the " + "operands."); + + // do the reference operation, and simultaneously + // check 2. lane(l, vopres) == arefres[l] foreach l + using T = Scalar<std::decay_t<VR> >; + for(auto l : range(lanes(vopres))) + { + // see the lengthy comment in `checkUnaryOpV()` as to why the + // `static_cast` around the `op()` is necessary + DUNE_SIMD_CHECK_OP + (lane(l, vopres) + == static_cast<T>(op(lane(l, static_cast<V1>(vref1)), + lane(l, static_cast<V2>(vref2))))); + } + + // check 3. lane(l, vop1) == aref1[l] foreach l + for(auto l : range(lanes(vop1))) + DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); + + // check 4. lane(l, vop2) == aref2[l] foreach l + for(auto l : range(lanes(vop2))) + DUNE_SIMD_CHECK_OP(lane(l, vop2) == lane(l, vref2)); #undef DUNE_SIMD_OPNAME -} - -template<class V1, class V2, class Op> -std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, V2> > -checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op) -{ -// log_ << "No " << className<Op(decltype(lane(0, std::declval<V1>())), -// decltype(lane(0, std::declval<V2>())))>() -// << std::endl -// << " ==> Not checking " << className<Op(V1, V2)>() << std::endl; -} - -template<class V1, class V2> -void checkBinaryOpVV(MetaType<V1>, MetaType<V2>, OpInfixComma) -{ -static_assert(std::is_same<std::decay_t<V1>, std::decay_t<V2> >::value, -"Internal testsystem error: called with two types that " -"don't decay to the same thing"); - -checkCommaOp<V1, V2>(leftVector<std::decay_t<V1>>(), -rightVector<std::decay_t<V2>>()); -} - -////////////////////////////////////////////////////////////////////// -// -// checks for vector-scalar binary operations -// - -// We check the following candidate operation -// -// vopres = vop1 @ sop2 -// -// against the reference operation -// -// arefres[l] = aref1[l] @ sref2 foreach l -// -// v... variables are simd-vectors, a... variables are arrays, and -// s... variables are scalars. The operation may modify the left -// operand, but if is does the modifications needs to happen in both the -// candidate and the reference. -// -// We do the following checks: -// 1. lanes(vopres) == lanes(vop1) -// 2. lane(l, vopres) == arefres[l] foreach l -// 3. lane(l, vop1) == aref1[l] foreach l -// 4. sop2 is never modified -// 5. sref2 is never modified -// -// In fact, if the property "sref2 is never modified" is violated that -// means the operation is unsuitable for an automatic broadcast of the -// second operand and should not be checked. There are no operations in -// the standard where the second operand is modified like this, but -// there are operations where the first operand is modified -- and this -// check is used for those ops as well by exchanging the first and second -// argument below. - -template<class V1, class T2, class Op> -std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, T2> > -checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op) -{ + } + + template<class V1, class V2, class Op> + std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, V2> > + checkBinaryOpVV(MetaType<V1>, MetaType<V2>, Op op) + { + // log_ << "No " << className<Op(decltype(lane(0, std::declval<V1>())), + // decltype(lane(0, std::declval<V2>())))>() + // << std::endl + // << " ==> Not checking " << className<Op(V1, V2)>() << std::endl; + } + + template<class V1, class V2> + void checkBinaryOpVV(MetaType<V1>, MetaType<V2>, OpInfixComma) + { + static_assert(std::is_same<std::decay_t<V1>, std::decay_t<V2> >::value, + "Internal testsystem error: called with two types that " + "don't decay to the same thing"); + + checkCommaOp<V1, V2>(leftVector<std::decay_t<V1>>(), + rightVector<std::decay_t<V2>>()); + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for vector-scalar binary operations + // + + // We check the following candidate operation + // + // vopres = vop1 @ sop2 + // + // against the reference operation + // + // arefres[l] = aref1[l] @ sref2 foreach l + // + // v... variables are simd-vectors, a... variables are arrays, and + // s... variables are scalars. The operation may modify the left + // operand, but if is does the modifications needs to happen in both the + // candidate and the reference. + // + // We do the following checks: + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == arefres[l] foreach l + // 3. lane(l, vop1) == aref1[l] foreach l + // 4. sop2 is never modified + // 5. sref2 is never modified + // + // In fact, if the property "sref2 is never modified" is violated that + // means the operation is unsuitable for an automatic broadcast of the + // second operand and should not be checked. There are no operations in + // the standard where the second operand is modified like this, but + // there are operations where the first operand is modified -- and this + // check is used for those ops as well by exchanging the first and second + // argument below. + + template<class V1, class T2, class Op> + std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, T2> > + checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op) + { #define DUNE_SIMD_OPNAME (className<Op(V1, T2)>()) -static_assert(std::is_same<Scalar<std::decay_t<V1> >, -std::decay_t<T2> >::value, -"Internal testsystem error: called with a scalar that " -"does not match the vector type."); - -// initial values -auto sinit2 = rightScalar<std::decay_t<T2>>(); - -// reference arguments -auto vref1 = leftVector<std::decay_t<V1>>(); -auto sref2 = sinit2; - -// candidate arguments -auto vop1 = vref1; -auto sop2 = sref2; - -// candidate operation -auto &&vopres = op(static_cast<V1>(vop1), static_cast<T2>(sop2)); -using VR = decltype(vopres); - -// check 1. lanes(vopres) == lanes(vop1) -static_assert(lanes<std::decay_t<VR> >() == lanes<std::decay_t<V1> >(), -"The result must have the same number of lanes as the " -"operands."); - -// check 4. sop2 is never modified -DUNE_SIMD_CHECK_OP(sop2 == sinit2); - -// do the reference operation, and simultaneously check 2. and 5. -using T = Scalar<std::decay_t<decltype(vopres)> >; -for(auto l : range(lanes(vopres))) -{ -// check 2. lane(l, vopres) == arefres[l] foreach l -// see the lengthy comment in `checkUnaryOpV()` as to why the -// `static_cast` around the `op()` is necessary -DUNE_SIMD_CHECK_OP -(lane(l, vopres) -== static_cast<T>(op(lane(l, static_cast<V1>(vref1)), -static_cast<T2>(sref2) ))); -// check 5. sref2 is never modified -DUNE_SIMD_CHECK_OP(sref2 == sinit2); -} - -// check 3. lane(l, vop1) == aref1[l] foreach l -for(auto l : range(lanes(vop1))) -DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); + static_assert(std::is_same<Scalar<std::decay_t<V1> >, + std::decay_t<T2> >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); + + // initial values + auto sinit2 = rightScalar<std::decay_t<T2>>(); + + // reference arguments + auto vref1 = leftVector<std::decay_t<V1>>(); + auto sref2 = sinit2; + + // candidate arguments + auto vop1 = vref1; + auto sop2 = sref2; + + // candidate operation + auto &&vopres = op(static_cast<V1>(vop1), static_cast<T2>(sop2)); + using VR = decltype(vopres); + + // check 1. lanes(vopres) == lanes(vop1) + static_assert(lanes<std::decay_t<VR> >() == lanes<std::decay_t<V1> >(), + "The result must have the same number of lanes as the " + "operands."); + + // check 4. sop2 is never modified + DUNE_SIMD_CHECK_OP(sop2 == sinit2); + + // do the reference operation, and simultaneously check 2. and 5. + using T = Scalar<std::decay_t<decltype(vopres)> >; + for(auto l : range(lanes(vopres))) + { + // check 2. lane(l, vopres) == arefres[l] foreach l + // see the lengthy comment in `checkUnaryOpV()` as to why the + // `static_cast` around the `op()` is necessary + DUNE_SIMD_CHECK_OP + (lane(l, vopres) + == static_cast<T>(op(lane(l, static_cast<V1>(vref1)), + static_cast<T2>(sref2) ))); + // check 5. sref2 is never modified + DUNE_SIMD_CHECK_OP(sref2 == sinit2); + } + + // check 3. lane(l, vop1) == aref1[l] foreach l + for(auto l : range(lanes(vop1))) + DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); #undef DUNE_SIMD_OPNAME -} - -template<class V1, class T2, class Op> -std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, T2> > -checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op) -{ -// log_ << "No " -// << className<Op(decltype(lane(0, std::declval<V1>())), T2)>() -// << std::endl -// << " ==> Not checking " << className<Op(V1, T2)>() << std::endl; -} - -template<class V1, class T2> -void checkBinaryOpVS(MetaType<V1>, MetaType<T2>, OpInfixComma) -{ -static_assert(std::is_same<Scalar<std::decay_t<V1> >, -std::decay_t<T2> >::value, -"Internal testsystem error: called with a scalar that " -"does not match the vector type."); - -checkCommaOp<V1, T2>(leftVector<std::decay_t<V1>>(), -rightScalar<std::decay_t<T2>>()); -} - -////////////////////////////////////////////////////////////////////// -// -// cross-check scalar-vector binary operations against vector-vector -// - -// We check the following candidate operation -// -// vopres = vop1 @ vop2, where vop2 = broadcast(sref2) -// -// against the reference operation -// -// vrefres = vref1 @ sref2 -// -// v... variables are simd-vectors, a... variables are arrays, and -// s... variables are scalars. -// -// We could check the following properties -// 1. lanes(vopres) == lanes(vop1) -// 2. lane(l, vopres) == lane(l, vrefres) foreach l -// 3. lane(l, vop1) == lane(l, vref1) foreach l -// but these are given by checking the operation against the scalar -// operation in the vector@vector and vector@scalar cases above. -// -// The only thing left to check is: -// 4. lane(l, vop2) foreach l is never modified - -template<class V1, class T2, class Op> -std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, T2> > -checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op) -{ + } + + template<class V1, class T2, class Op> + std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, T2> > + checkBinaryOpVS(MetaType<V1>, MetaType<T2>, Op op) + { + // log_ << "No " + // << className<Op(decltype(lane(0, std::declval<V1>())), T2)>() + // << std::endl + // << " ==> Not checking " << className<Op(V1, T2)>() << std::endl; + } + + template<class V1, class T2> + void checkBinaryOpVS(MetaType<V1>, MetaType<T2>, OpInfixComma) + { + static_assert(std::is_same<Scalar<std::decay_t<V1> >, + std::decay_t<T2> >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); + + checkCommaOp<V1, T2>(leftVector<std::decay_t<V1>>(), + rightScalar<std::decay_t<T2>>()); + } + + ////////////////////////////////////////////////////////////////////// + // + // cross-check scalar-vector binary operations against vector-vector + // + + // We check the following candidate operation + // + // vopres = vop1 @ vop2, where vop2 = broadcast(sref2) + // + // against the reference operation + // + // vrefres = vref1 @ sref2 + // + // v... variables are simd-vectors, a... variables are arrays, and + // s... variables are scalars. + // + // We could check the following properties + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == lane(l, vrefres) foreach l + // 3. lane(l, vop1) == lane(l, vref1) foreach l + // but these are given by checking the operation against the scalar + // operation in the vector@vector and vector@scalar cases above. + // + // The only thing left to check is: + // 4. lane(l, vop2) foreach l is never modified + + template<class V1, class T2, class Op> + std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, T2> > + checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op) + { #define DUNE_SIMD_OPNAME (className<Op(V1, T2)>()) -static_assert(std::is_same<Scalar<std::decay_t<V1> >, -std::decay_t<T2> >::value, -"Internal testsystem error: called with a scalar that " -"does not match the vector type."); + static_assert(std::is_same<Scalar<std::decay_t<V1> >, + std::decay_t<T2> >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); -// initial values -auto sinit2 = rightScalar<std::decay_t<T2>>(); + // initial values + auto sinit2 = rightScalar<std::decay_t<T2>>(); -// reference arguments -auto vop1 = leftVector<std::decay_t<V1>>(); -using V2 = CopyRefQual<V1, T2>; -std::decay_t<V2> vop2(sinit2); + // reference arguments + auto vop1 = leftVector<std::decay_t<V1>>(); + using V2 = CopyRefQual<V1, T2>; + std::decay_t<V2> vop2(sinit2); -// candidate operation -op(static_cast<V1>(vop1), static_cast<V2>(vop2)); + // candidate operation + op(static_cast<V1>(vop1), static_cast<V2>(vop2)); -// 4. lane(l, vop2) foreach l is never modified -for(auto l : range(lanes(vop2))) -DUNE_SIMD_CHECK_OP(lane(l, vop2) == sinit2); + // 4. lane(l, vop2) foreach l is never modified + for(auto l : range(lanes(vop2))) + DUNE_SIMD_CHECK_OP(lane(l, vop2) == sinit2); #undef DUNE_SIMD_OPNAME -} - -template<class V1, class T2, class Op> -std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, T2> > -checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op) -{ -// log_ << "No " -// << className<Op(decltype(lane(0, std::declval<V1>())), T2)>() -// << std::endl -// << " ==> Not checking " << className<Op(V1, T2)>() << std::endl; -} - -template<class V1, class T2> -void checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, OpInfixComma) -{ } - -////////////////////////////////////////////////////////////////////// -// -// checks for vector-proxy binary operations -// - -// We check the following candidate operation -// -// vopres = vop1 @ pop2 -// -// against the reference operation -// -// arefres[l] = aref1[l] @ sref2 foreach l -// -// v... variables are simd-vectors, a... variables are arrays, -// p... variables are proxies of simd-vector entries and s... variables -// are scalars. The operation may modify the left operand, but if is -// does the modifications needs to happen in both the candidate and the -// reference. -// -// We do the following checks: -// 1. lanes(vopres) == lanes(vop1) -// 2. lane(l, vopres) == arefres[l] foreach l -// 3. lane(l, vop1) == aref1[l] foreach l -// 4. pop2 is never modified -// 5. sref2 is never modified -// -// In fact, if the property "sref2 is never modified" is violated that -// means the operation is unsuitable for an automatic broadcast of the -// second operand and should not be checked. There are no operations in -// the standard where the second operand is modified like this, but -// there are operations where the first operand is modified -- and this -// check is used for those ops as well by exchanging the first and second -// argument below. - -template<class V1, class V2, class Op> -std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, V2> > -checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op) -{ -using P2 = decltype(lane(0, std::declval<V2>())); -using T2 = CopyRefQual<Scalar<V2>, V2>; + } + + template<class V1, class T2, class Op> + std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, T2> > + checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, Op op) + { + // log_ << "No " + // << className<Op(decltype(lane(0, std::declval<V1>())), T2)>() + // << std::endl + // << " ==> Not checking " << className<Op(V1, T2)>() << std::endl; + } + + template<class V1, class T2> + void checkBinaryOpVVAgainstVS(MetaType<V1>, MetaType<T2>, OpInfixComma) + { } + + ////////////////////////////////////////////////////////////////////// + // + // checks for vector-proxy binary operations + // + + // We check the following candidate operation + // + // vopres = vop1 @ pop2 + // + // against the reference operation + // + // arefres[l] = aref1[l] @ sref2 foreach l + // + // v... variables are simd-vectors, a... variables are arrays, + // p... variables are proxies of simd-vector entries and s... variables + // are scalars. The operation may modify the left operand, but if is + // does the modifications needs to happen in both the candidate and the + // reference. + // + // We do the following checks: + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == arefres[l] foreach l + // 3. lane(l, vop1) == aref1[l] foreach l + // 4. pop2 is never modified + // 5. sref2 is never modified + // + // In fact, if the property "sref2 is never modified" is violated that + // means the operation is unsuitable for an automatic broadcast of the + // second operand and should not be checked. There are no operations in + // the standard where the second operand is modified like this, but + // there are operations where the first operand is modified -- and this + // check is used for those ops as well by exchanging the first and second + // argument below. + + template<class V1, class V2, class Op> + std::enable_if_t<Std::is_detected_v<ScalarResult, Op, V1, V2> > + checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op) + { + using P2 = decltype(lane(0, std::declval<V2>())); + using T2 = CopyRefQual<Scalar<V2>, V2>; #define DUNE_SIMD_OPNAME (className<Op(V1, P2)>()) -static_assert(std::is_same<Scalar<V1>, Scalar<V2> >::value, -"Internal testsystem error: called with two vector " -"types whose scalar types don't match."); - -// initial values -auto sinit2 = rightScalar<Scalar<V2>>(); - -// reference arguments -auto vref1 = leftVector<std::decay_t<V1>>(); -auto sref2 = sinit2; - -// candidate arguments -auto vop1 = vref1; -auto vop2 = std::decay_t<V2>(Scalar<V2>(0)); -lane(0, vop2) = sref2; // pop2 is just a name for `lane(0, vop2)` - -// candidate operation -auto &&vopres = -op(static_cast<V1>(vop1), lane(0, static_cast<V2>(vop2))); -using VR = decltype(vopres); - -// check 1. lanes(vopres) == lanes(vop1) -static_assert(lanes<std::decay_t<VR> >() == lanes<std::decay_t<V1> >(), -"The result must have the same number of lanes as the " -"operands."); - -// check 4. pop2 is never modified -DUNE_SIMD_CHECK_OP(lane(0, vop2) == sinit2); - -// do the reference operation, and simultaneously check 2. and 5. -using T = Scalar<decltype(vopres)>; -for(auto l : range(lanes(vopres))) -{ -// check 2. lane(l, vopres) == arefres[l] foreach l -// see the lengthy comment in `checkUnaryOpV()` as to why the -// `static_cast` around the `op()` is necessary -DUNE_SIMD_CHECK_OP -(lane(l, vopres) -== static_cast<T>(op(lane(l, static_cast<V1>(vref1)), -static_cast<T2>(sref2) ))); -// check 5. sref2 is never modified -DUNE_SIMD_CHECK_OP(sref2 == sinit2); -} - -// check 3. lane(l, vop1) == aref1[l] foreach l -for(auto l : range(lanes(vop1))) -DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); + static_assert(std::is_same<Scalar<V1>, Scalar<V2> >::value, + "Internal testsystem error: called with two vector " + "types whose scalar types don't match."); + + // initial values + auto sinit2 = rightScalar<Scalar<V2>>(); + + // reference arguments + auto vref1 = leftVector<std::decay_t<V1>>(); + auto sref2 = sinit2; + + // candidate arguments + auto vop1 = vref1; + auto vop2 = std::decay_t<V2>(Scalar<V2>(0)); + lane(0, vop2) = sref2; // pop2 is just a name for `lane(0, vop2)` + + // candidate operation + auto &&vopres = + op(static_cast<V1>(vop1), lane(0, static_cast<V2>(vop2))); + using VR = decltype(vopres); + + // check 1. lanes(vopres) == lanes(vop1) + static_assert(lanes<std::decay_t<VR> >() == lanes<std::decay_t<V1> >(), + "The result must have the same number of lanes as the " + "operands."); + + // check 4. pop2 is never modified + DUNE_SIMD_CHECK_OP(lane(0, vop2) == sinit2); + + // do the reference operation, and simultaneously check 2. and 5. + using T = Scalar<decltype(vopres)>; + for(auto l : range(lanes(vopres))) + { + // check 2. lane(l, vopres) == arefres[l] foreach l + // see the lengthy comment in `checkUnaryOpV()` as to why the + // `static_cast` around the `op()` is necessary + DUNE_SIMD_CHECK_OP + (lane(l, vopres) + == static_cast<T>(op(lane(l, static_cast<V1>(vref1)), + static_cast<T2>(sref2) ))); + // check 5. sref2 is never modified + DUNE_SIMD_CHECK_OP(sref2 == sinit2); + } + + // check 3. lane(l, vop1) == aref1[l] foreach l + for(auto l : range(lanes(vop1))) + DUNE_SIMD_CHECK_OP(lane(l, vop1) == lane(l, vref1)); #undef DUNE_SIMD_OPNAME -} - -template<class V1, class V2, class Op> -std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, V2> > -checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op) -{ -// log_ << "No " -// << className<Op(decltype(lane(0, std::declval<V1>())), T2)>() -// << std::endl -// << " ==> Not checking " << className<Op(V1, T2)>() << std::endl; -} - -template<class V1, class V2> -void checkBinaryOpVP(MetaType<V1>, MetaType<V2>, OpInfixComma) -{ -// Don't really know how to check comma operator for proxies -} - -////////////////////////////////////////////////////////////////////// -// -// checks for (scalar/proxy)-vector binary operations -// - -template<class Op> -struct OpInfixSwappedArgs -{ -Op orig; - -template<class V1, class V2> -decltype(auto) operator()(V1&& v1, V2&& v2) const -{ -return orig(std::forward<V2>(v2), std::forward<V1>(v1)); -} -template<class S1, class S2> -auto scalar(S1&& s1, S2&& s2) const --> decltype(orig.scalar(std::forward<S2>(s2), std::forward<S1>(s1))); -}; - -template<class T1, class V2, class Op> -void checkBinaryOpSV(MetaType<T1> t1, MetaType<V2> v2, Op op) -{ -checkBinaryOpVS(v2, t1, OpInfixSwappedArgs<Op>{op}); -} - -template<class T1, class V2> -void checkBinaryOpSV(MetaType<T1>, MetaType<V2>, OpInfixComma) -{ -static_assert(std::is_same<std::decay_t<T1>, -Scalar<std::decay_t<V2> > >::value, -"Internal testsystem error: called with a scalar that " -"does not match the vector type."); - -checkCommaOp<T1, V2>(leftScalar<std::decay_t<T1>>(), -rightVector<std::decay_t<V2>>()); -} - -template<class V1, class V2, class Op> -void checkBinaryOpPV(MetaType<V1> v1, MetaType<V2> v2, Op op) -{ -checkBinaryOpVP(v2, v1, OpInfixSwappedArgs<Op>{op}); -} - -template<class V1, class V2> -void checkBinaryOpPV(MetaType<V1>, MetaType<V2>, OpInfixComma) -{ -// Don't really know how to check comma operator for proxies -} - -////////////////////////////////////////////////////////////////////// -// -// cross-check scalar-vector binary operations against vector-vector -// - -// We check the following candidate operation -// -// vopres = vop1 @ vop2, where vop2 = broadcast(sref2) -// -// against the reference operation -// -// vrefres = vref1 @ sref2 -// -// v... variables are simd-vectors, a... variables are arrays, and -// s... variables are scalars. -// -// We could check the following properties -// 1. lanes(vopres) == lanes(vop1) -// 2. lane(l, vopres) == lane(l, vrefres) foreach l -// 3. lane(l, vop1) == lane(l, vref1) foreach l -// but these are given by checking the operation against the scalar -// operation in the vector@vector and vector@scalar cases above. -// -// The only thing left to check is: -// 4. lane(l, vop2) foreach l is never modified - -template<class T1, class V2, class Op> -void checkBinaryOpVVAgainstSV(MetaType<T1> t1, MetaType<V2> v2, Op op) -{ -checkBinaryOpVVAgainstVS(v2, t1, OpInfixSwappedArgs<Op>{op}); -} - -template<class V1, class T2> -void checkBinaryOpVVAgainstSV(MetaType<V1>, MetaType<T2>, OpInfixComma) -{ } - -////////////////////////////////////////////////////////////////////// -// -// Invoke the checks for all combinations -// - -template<class T1, class T2, bool condition, class Checker> -void checkBinaryRefQual(Checker checker) -{ -if constexpr (condition) { -Hybrid::forEach(TypeList<T1&, const T1&, T1&&>{}, [=] (auto t1) { -Hybrid::forEach(TypeList<T2&, const T2&, T2&&>{}, [=] (auto t2) { -checker(t1, t2); -}); -}); -} -} - -template<class V, class Checker> -void checkBinaryOps(Checker checker) -{ -using Std::bool_constant; - -constexpr bool isMask = std::is_same<Scalar<V>, bool>::value; - -constexpr bool do_ = false; -constexpr bool do_SV = true; -constexpr bool do_VV = true; -constexpr bool do_VS = true; + } + + template<class V1, class V2, class Op> + std::enable_if_t<!Std::is_detected_v<ScalarResult, Op, V1, V2> > + checkBinaryOpVP(MetaType<V1>, MetaType<V2>, Op op) + { + // log_ << "No " + // << className<Op(decltype(lane(0, std::declval<V1>())), T2)>() + // << std::endl + // << " ==> Not checking " << className<Op(V1, T2)>() << std::endl; + } + + template<class V1, class V2> + void checkBinaryOpVP(MetaType<V1>, MetaType<V2>, OpInfixComma) + { + // Don't really know how to check comma operator for proxies + } + + ////////////////////////////////////////////////////////////////////// + // + // checks for (scalar/proxy)-vector binary operations + // + + template<class Op> + struct OpInfixSwappedArgs + { + Op orig; + + template<class V1, class V2> + decltype(auto) operator()(V1&& v1, V2&& v2) const + { + return orig(std::forward<V2>(v2), std::forward<V1>(v1)); + } + template<class S1, class S2> + auto scalar(S1&& s1, S2&& s2) const + -> decltype(orig.scalar(std::forward<S2>(s2), std::forward<S1>(s1))); + }; + + template<class T1, class V2, class Op> + void checkBinaryOpSV(MetaType<T1> t1, MetaType<V2> v2, Op op) + { + checkBinaryOpVS(v2, t1, OpInfixSwappedArgs<Op>{op}); + } + + template<class T1, class V2> + void checkBinaryOpSV(MetaType<T1>, MetaType<V2>, OpInfixComma) + { + static_assert(std::is_same<std::decay_t<T1>, + Scalar<std::decay_t<V2> > >::value, + "Internal testsystem error: called with a scalar that " + "does not match the vector type."); + + checkCommaOp<T1, V2>(leftScalar<std::decay_t<T1>>(), + rightVector<std::decay_t<V2>>()); + } + + template<class V1, class V2, class Op> + void checkBinaryOpPV(MetaType<V1> v1, MetaType<V2> v2, Op op) + { + checkBinaryOpVP(v2, v1, OpInfixSwappedArgs<Op>{op}); + } + + template<class V1, class V2> + void checkBinaryOpPV(MetaType<V1>, MetaType<V2>, OpInfixComma) + { + // Don't really know how to check comma operator for proxies + } + + ////////////////////////////////////////////////////////////////////// + // + // cross-check scalar-vector binary operations against vector-vector + // + + // We check the following candidate operation + // + // vopres = vop1 @ vop2, where vop2 = broadcast(sref2) + // + // against the reference operation + // + // vrefres = vref1 @ sref2 + // + // v... variables are simd-vectors, a... variables are arrays, and + // s... variables are scalars. + // + // We could check the following properties + // 1. lanes(vopres) == lanes(vop1) + // 2. lane(l, vopres) == lane(l, vrefres) foreach l + // 3. lane(l, vop1) == lane(l, vref1) foreach l + // but these are given by checking the operation against the scalar + // operation in the vector@vector and vector@scalar cases above. + // + // The only thing left to check is: + // 4. lane(l, vop2) foreach l is never modified + + template<class T1, class V2, class Op> + void checkBinaryOpVVAgainstSV(MetaType<T1> t1, MetaType<V2> v2, Op op) + { + checkBinaryOpVVAgainstVS(v2, t1, OpInfixSwappedArgs<Op>{op}); + } + + template<class V1, class T2> + void checkBinaryOpVVAgainstSV(MetaType<V1>, MetaType<T2>, OpInfixComma) + { } + + ////////////////////////////////////////////////////////////////////// + // + // Invoke the checks for all combinations + // + + template<class T1, class T2, bool condition, class Checker> + void checkBinaryRefQual(Checker checker) + { + if constexpr (condition) { + Hybrid::forEach(TypeList<T1&, const T1&, T1&&>{}, [=] (auto t1) { + Hybrid::forEach(TypeList<T2&, const T2&, T2&&>{}, [=] (auto t2) { + checker(t1, t2); + }); + }); + } + } + + template<class V, class Checker> + void checkBinaryOps(Checker checker) + { + using Std::bool_constant; + + constexpr bool isMask = std::is_same<Scalar<V>, bool>::value; + + constexpr bool do_ = false; + constexpr bool do_SV = true; + constexpr bool do_VV = true; + constexpr bool do_VS = true; #define DUNE_SIMD_DO(M1, M2, M3, V1, V2, V3, NAME) \ -checker(bool_constant<isMask ? do_##M1 : do_##V1>{}, \ -bool_constant<isMask ? do_##M2 : do_##V2>{}, \ -bool_constant<isMask ? do_##M3 : do_##V3>{}, \ -Op##NAME{}) + checker(bool_constant<isMask ? do_##M1 : do_##V1>{}, \ + bool_constant<isMask ? do_##M2 : do_##V2>{}, \ + bool_constant<isMask ? do_##M3 : do_##V3>{}, \ + Op##NAME{}) -// (Mask , Vector , Name ); + // (Mask , Vector , Name ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixMul ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixDiv ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixRemainder ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixMul ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixDiv ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixRemainder ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixPlus ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixMinus ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixPlus ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixMinus ); -DUNE_SIMD_DO( , , , , VV, VS, InfixLeftShift ); -DUNE_SIMD_DO( , , , , VV, VS, InfixRightShift ); + DUNE_SIMD_DO( , , , , VV, VS, InfixLeftShift ); + DUNE_SIMD_DO( , , , , VV, VS, InfixRightShift ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixLess ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixGreater ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixLessEqual ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixGreaterEqual ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixLess ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixGreater ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixLessEqual ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixGreaterEqual ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixEqual ); -DUNE_SIMD_DO( , , , SV, VV, VS, InfixNotEqual ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixEqual ); + DUNE_SIMD_DO( , , , SV, VV, VS, InfixNotEqual ); -DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitAnd ); -DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitXor ); -DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitOr ); + DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitAnd ); + DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitXor ); + DUNE_SIMD_DO( , VV, , SV, VV, VS, InfixBitOr ); -DUNE_SIMD_DO(SV, VV, VS, SV, VV, VS, InfixLogicAnd ); -DUNE_SIMD_DO(SV, VV, VS, SV, VV, VS, InfixLogicOr ); + DUNE_SIMD_DO(SV, VV, VS, SV, VV, VS, InfixLogicAnd ); + DUNE_SIMD_DO(SV, VV, VS, SV, VV, VS, InfixLogicOr ); -DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssign ); -DUNE_SIMD_DO( , , , , VV, VS, InfixAssignMul ); -DUNE_SIMD_DO( , , , , VV, VS, InfixAssignDiv ); -DUNE_SIMD_DO( , , , , VV, VS, InfixAssignRemainder ); -DUNE_SIMD_DO( , , , , VV, VS, InfixAssignPlus ); -DUNE_SIMD_DO( , , , , VV, VS, InfixAssignMinus ); -DUNE_SIMD_DO( , , , , VV, VS, InfixAssignLeftShift ); -DUNE_SIMD_DO( , , , , VV, VS, InfixAssignRightShift); -DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignAnd ); -DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignXor ); -DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignOr ); + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssign ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignMul ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignDiv ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignRemainder ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignPlus ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignMinus ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignLeftShift ); + DUNE_SIMD_DO( , , , , VV, VS, InfixAssignRightShift); + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignAnd ); + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignXor ); + DUNE_SIMD_DO( , VV, , , VV, VS, InfixAssignOr ); -DUNE_SIMD_DO(SV, VV, VS, SV, , VS, InfixComma ); + DUNE_SIMD_DO(SV, VV, VS, SV, , VS, InfixComma ); #undef DUNE_SIMD_DO -} - -////////////////////////////////////////////////////////////////////// -// -// SIMD interface functions -// - -template<class V> -void checkAutoCopy() -{ -using RValueResult = decltype(autoCopy(lane(0, std::declval<V>()))); -static_assert(std::is_same<RValueResult, Scalar<V> >::value, -"Result of autoCopy() must always be Scalar<V>"); - -using MutableLValueResult = -decltype(autoCopy(lane(0, std::declval<V&>()))); -static_assert(std::is_same<MutableLValueResult, Scalar<V> >::value, -"Result of autoCopy() must always be Scalar<V>"); - -using ConstLValueResult = -decltype(autoCopy(lane(0, std::declval<const V&>()))); -static_assert(std::is_same<ConstLValueResult, Scalar<V> >::value, -"Result of autoCopy() must always be Scalar<V>"); - -V vec = make123<V>(); -for(std::size_t l = 0; l < lanes(vec); ++l) -DUNE_SIMD_CHECK(autoCopy(lane(l, vec)) == Scalar<V>(l+1)); -} - -// may only be called for mask types -template<class M> -void checkBoolReductions() -{ -M trueVec(true); - -// mutable lvalue -DUNE_SIMD_CHECK(allTrue (static_cast<M&>(trueVec)) == true); -DUNE_SIMD_CHECK(anyTrue (static_cast<M&>(trueVec)) == true); -DUNE_SIMD_CHECK(allFalse(static_cast<M&>(trueVec)) == false); -DUNE_SIMD_CHECK(anyFalse(static_cast<M&>(trueVec)) == false); - -// const lvalue -DUNE_SIMD_CHECK(allTrue (static_cast<const M&>(trueVec)) == true); -DUNE_SIMD_CHECK(anyTrue (static_cast<const M&>(trueVec)) == true); -DUNE_SIMD_CHECK(allFalse(static_cast<const M&>(trueVec)) == false); -DUNE_SIMD_CHECK(anyFalse(static_cast<const M&>(trueVec)) == false); - -// rvalue -DUNE_SIMD_CHECK(allTrue (M(true)) == true); -DUNE_SIMD_CHECK(anyTrue (M(true)) == true); -DUNE_SIMD_CHECK(allFalse(M(true)) == false); -DUNE_SIMD_CHECK(anyFalse(M(true)) == false); - -M falseVec(false); - -// mutable lvalue -DUNE_SIMD_CHECK(allTrue (static_cast<M&>(falseVec)) == false); -DUNE_SIMD_CHECK(anyTrue (static_cast<M&>(falseVec)) == false); -DUNE_SIMD_CHECK(allFalse(static_cast<M&>(falseVec)) == true); -DUNE_SIMD_CHECK(anyFalse(static_cast<M&>(falseVec)) == true); - -// const lvalue -DUNE_SIMD_CHECK(allTrue (static_cast<const M&>(falseVec)) == false); -DUNE_SIMD_CHECK(anyTrue (static_cast<const M&>(falseVec)) == false); -DUNE_SIMD_CHECK(allFalse(static_cast<const M&>(falseVec)) == true); -DUNE_SIMD_CHECK(anyFalse(static_cast<const M&>(falseVec)) == true); - -// rvalue -DUNE_SIMD_CHECK(allTrue (M(false)) == false); -DUNE_SIMD_CHECK(anyTrue (M(false)) == false); -DUNE_SIMD_CHECK(allFalse(M(false)) == true); -DUNE_SIMD_CHECK(anyFalse(M(false)) == true); - -auto mixedVec = broadcast<M>(0); -for(std::size_t l = 0; l < lanes(mixedVec); ++l) -lane(l, mixedVec) = (l % 2); - -// mutable lvalue -DUNE_SIMD_CHECK -(allTrue (static_cast<M&>(mixedVec)) == false); -DUNE_SIMD_CHECK -(anyTrue (static_cast<M&>(mixedVec)) == (lanes<M>() > 1)); -DUNE_SIMD_CHECK -(allFalse(static_cast<M&>(mixedVec)) == (lanes<M>() == 1)); -DUNE_SIMD_CHECK -(anyFalse(static_cast<M&>(mixedVec)) == true); - -// const lvalue -DUNE_SIMD_CHECK -(allTrue (static_cast<const M&>(mixedVec)) == false); -DUNE_SIMD_CHECK -(anyTrue (static_cast<const M&>(mixedVec)) == (lanes<M>() > 1)); -DUNE_SIMD_CHECK -(allFalse(static_cast<const M&>(mixedVec)) == (lanes<M>() == 1)); -DUNE_SIMD_CHECK -(anyFalse(static_cast<const M&>(mixedVec)) == true); - -// rvalue -DUNE_SIMD_CHECK(allTrue (M(mixedVec)) == false); -DUNE_SIMD_CHECK(anyTrue (M(mixedVec)) == (lanes<M>() > 1)); -DUNE_SIMD_CHECK(allFalse(M(mixedVec)) == (lanes<M>() == 1)); -DUNE_SIMD_CHECK(anyFalse(M(mixedVec)) == true); -} - -template<class V> -void checkCond() -{ -using M = Mask<V>; - -static_assert -(std::is_same<decltype(cond(std::declval<M>(), std::declval<V>(), -std::declval<V>())), V>::value, -"The result of cond(M, V, V) should have exactly the type V"); - -static_assert -(std::is_same<decltype(cond(std::declval<const M&>(), -std::declval<const V&>(), -std::declval<const V&>())), V>::value, -"The result of cond(const M&, const V&, const V&) should have " -"exactly the type V"); - -static_assert -(std::is_same<decltype(cond(std::declval<M&>(), std::declval<V&>(), -std::declval<V&>())), V>::value, -"The result of cond(M&, V&, V&) should have exactly the type V"); - -V vec1 = leftVector<V>(); -V vec2 = rightVector<V>(); - -DUNE_SIMD_CHECK(allTrue(cond(M(true), vec1, vec2) == vec1)); -DUNE_SIMD_CHECK(allTrue(cond(M(false), vec1, vec2) == vec2)); - -auto mixedResult = broadcast<V>(0); -auto mixedMask = broadcast<M>(false); -for(std::size_t l = 0; l < lanes(mixedMask); ++l) -{ -lane(l, mixedMask ) = (l % 2); -lane(l, mixedResult) = lane(l, (l % 2) ? vec1 : vec2); -} - -DUNE_SIMD_CHECK(allTrue(cond(mixedMask, vec1, vec2) == mixedResult)); -} - -template<class V> -void checkBoolCond() -{ -static_assert -(std::is_same<decltype(cond(std::declval<bool>(), std::declval<V>(), -std::declval<V>())), V>::value, -"The result of cond(bool, V, V) should have exactly the type V"); - -static_assert -(std::is_same<decltype(cond(std::declval<const bool&>(), -std::declval<const V&>(), -std::declval<const V&>())), V>::value, -"The result of cond(const bool&, const V&, const V&) should have " -"exactly the type V"); - -static_assert -(std::is_same<decltype(cond(std::declval<bool&>(), -std::declval<V&>(), -std::declval<V&>())), V>::value, -"The result of cond(bool&, V&, V&) should have exactly the type V"); - -V vec1 = leftVector<V>(); -V vec2 = rightVector<V>(); - -DUNE_SIMD_CHECK(allTrue(cond(true, vec1, vec2) == vec1)); -DUNE_SIMD_CHECK(allTrue(cond(false, vec1, vec2) == vec2)); -} - -template<class V> -std::enable_if_t<!Impl::LessThenComparable<Scalar<V> >::value> -checkHorizontalMinMax() {} - -template<class V> -std::enable_if_t<Impl::LessThenComparable<Scalar<V> >::value> -checkHorizontalMinMax() -{ -static_assert -(std::is_same<decltype(max(std::declval<V>())), Scalar<V> >::value, -"The result of max(V) should be exactly Scalar<V>"); - -static_assert -(std::is_same<decltype(min(std::declval<V>())), Scalar<V> >::value, -"The result of min(V) should be exactly Scalar<V>"); - -static_assert -(std::is_same<decltype(max(std::declval<V&>())), Scalar<V> >::value, -"The result of max(V) should be exactly Scalar<V>"); - -static_assert -(std::is_same<decltype(min(std::declval<V&>())), Scalar<V> >::value, -"The result of min(V) should be exactly Scalar<V>"); - -const V vec1 = leftVector<V>(); - -DUNE_SIMD_CHECK(max(vec1) == Scalar<V>(lanes(vec1))); -DUNE_SIMD_CHECK(min(vec1) == Scalar<V>(1)); -} - -template<class V> -std::enable_if_t<!Impl::LessThenComparable<Scalar<V> >::value> -checkBinaryMinMax() {} - -template<class V> -std::enable_if_t<Impl::LessThenComparable<Scalar<V> >::value> -checkBinaryMinMax() -{ -using std::max; -using std::min; - -static_assert -(std::is_same<decltype(Simd::max(std::declval<V>(), -std::declval<V>())), V>::value, -"The result of Simd::max(V, V) should be exactly V"); -static_assert -(std::is_same<decltype(Simd::min(std::declval<V>(), -std::declval<V>())), V>::value, -"The result of Simd::min(V, V) should be exactly V"); - -static_assert -(std::is_same<decltype(Simd::max(std::declval<V&>(), -std::declval<V&>())), V>::value, -"The result of Simd::max(V&, V&) should be exactly V"); -static_assert -(std::is_same<decltype(Simd::min(std::declval<V&>(), -std::declval<V&>())), V>::value, -"The result of Simd::min(V&, V&) should be exactly V"); - -const V arg1 = leftVector<V>(); -const V arg2 = rightVector<V>(); - -V maxExp(Scalar<V>(0)), minExp(Scalar<V>(0)); -for(auto l : range(lanes<V>())) -{ -lane(l, maxExp) = max(lane(l, arg1), lane(l, arg2)); -lane(l, minExp) = min(lane(l, arg1), lane(l, arg2)); -} - -DUNE_SIMD_CHECK(allTrue(maxExp == Simd::max(arg1, arg2))); -DUNE_SIMD_CHECK(allTrue(minExp == Simd::min(arg1, arg2))); -} - -template<class V> -void checkIO() -{ -const V vec1 = leftVector<V>(); - -std::string reference; -{ -const char *sep = ""; -for(auto l : range(lanes(vec1))) -{ -std::ostringstream stream; -stream << lane(l, vec1); - -reference += sep; -reference += stream.str(); -sep = ", "; -} -} - -{ -std::ostringstream stream; -stream << io(vec1); -if(lanes(vec1) == 1) -DUNE_SIMD_CHECK(stream.str() == reference); -else -DUNE_SIMD_CHECK(stream.str() == "<" + reference + ">"); -} - -{ -std::ostringstream stream; -stream << vio(vec1); -DUNE_SIMD_CHECK(stream.str() == "<" + reference + ">"); -} -} + } + + ////////////////////////////////////////////////////////////////////// + // + // SIMD interface functions + // + + template<class V> + void checkAutoCopy() + { + using RValueResult = decltype(autoCopy(lane(0, std::declval<V>()))); + static_assert(std::is_same<RValueResult, Scalar<V> >::value, + "Result of autoCopy() must always be Scalar<V>"); + + using MutableLValueResult = + decltype(autoCopy(lane(0, std::declval<V&>()))); + static_assert(std::is_same<MutableLValueResult, Scalar<V> >::value, + "Result of autoCopy() must always be Scalar<V>"); + + using ConstLValueResult = + decltype(autoCopy(lane(0, std::declval<const V&>()))); + static_assert(std::is_same<ConstLValueResult, Scalar<V> >::value, + "Result of autoCopy() must always be Scalar<V>"); + + V vec = make123<V>(); + for(std::size_t l = 0; l < lanes(vec); ++l) + DUNE_SIMD_CHECK(autoCopy(lane(l, vec)) == Scalar<V>(l+1)); + } + + // may only be called for mask types + template<class M> + void checkBoolReductions() + { + M trueVec(true); + + // mutable lvalue + DUNE_SIMD_CHECK(allTrue (static_cast<M&>(trueVec)) == true); + DUNE_SIMD_CHECK(anyTrue (static_cast<M&>(trueVec)) == true); + DUNE_SIMD_CHECK(allFalse(static_cast<M&>(trueVec)) == false); + DUNE_SIMD_CHECK(anyFalse(static_cast<M&>(trueVec)) == false); + + // const lvalue + DUNE_SIMD_CHECK(allTrue (static_cast<const M&>(trueVec)) == true); + DUNE_SIMD_CHECK(anyTrue (static_cast<const M&>(trueVec)) == true); + DUNE_SIMD_CHECK(allFalse(static_cast<const M&>(trueVec)) == false); + DUNE_SIMD_CHECK(anyFalse(static_cast<const M&>(trueVec)) == false); + + // rvalue + DUNE_SIMD_CHECK(allTrue (M(true)) == true); + DUNE_SIMD_CHECK(anyTrue (M(true)) == true); + DUNE_SIMD_CHECK(allFalse(M(true)) == false); + DUNE_SIMD_CHECK(anyFalse(M(true)) == false); + + M falseVec(false); + + // mutable lvalue + DUNE_SIMD_CHECK(allTrue (static_cast<M&>(falseVec)) == false); + DUNE_SIMD_CHECK(anyTrue (static_cast<M&>(falseVec)) == false); + DUNE_SIMD_CHECK(allFalse(static_cast<M&>(falseVec)) == true); + DUNE_SIMD_CHECK(anyFalse(static_cast<M&>(falseVec)) == true); + + // const lvalue + DUNE_SIMD_CHECK(allTrue (static_cast<const M&>(falseVec)) == false); + DUNE_SIMD_CHECK(anyTrue (static_cast<const M&>(falseVec)) == false); + DUNE_SIMD_CHECK(allFalse(static_cast<const M&>(falseVec)) == true); + DUNE_SIMD_CHECK(anyFalse(static_cast<const M&>(falseVec)) == true); + + // rvalue + DUNE_SIMD_CHECK(allTrue (M(false)) == false); + DUNE_SIMD_CHECK(anyTrue (M(false)) == false); + DUNE_SIMD_CHECK(allFalse(M(false)) == true); + DUNE_SIMD_CHECK(anyFalse(M(false)) == true); + + auto mixedVec = broadcast<M>(0); + for(std::size_t l = 0; l < lanes(mixedVec); ++l) + lane(l, mixedVec) = (l % 2); + + // mutable lvalue + DUNE_SIMD_CHECK + (allTrue (static_cast<M&>(mixedVec)) == false); + DUNE_SIMD_CHECK + (anyTrue (static_cast<M&>(mixedVec)) == (lanes<M>() > 1)); + DUNE_SIMD_CHECK + (allFalse(static_cast<M&>(mixedVec)) == (lanes<M>() == 1)); + DUNE_SIMD_CHECK + (anyFalse(static_cast<M&>(mixedVec)) == true); + + // const lvalue + DUNE_SIMD_CHECK + (allTrue (static_cast<const M&>(mixedVec)) == false); + DUNE_SIMD_CHECK + (anyTrue (static_cast<const M&>(mixedVec)) == (lanes<M>() > 1)); + DUNE_SIMD_CHECK + (allFalse(static_cast<const M&>(mixedVec)) == (lanes<M>() == 1)); + DUNE_SIMD_CHECK + (anyFalse(static_cast<const M&>(mixedVec)) == true); + + // rvalue + DUNE_SIMD_CHECK(allTrue (M(mixedVec)) == false); + DUNE_SIMD_CHECK(anyTrue (M(mixedVec)) == (lanes<M>() > 1)); + DUNE_SIMD_CHECK(allFalse(M(mixedVec)) == (lanes<M>() == 1)); + DUNE_SIMD_CHECK(anyFalse(M(mixedVec)) == true); + } + + template<class V> + void checkCond() + { + using M = Mask<V>; + + static_assert + (std::is_same<decltype(cond(std::declval<M>(), std::declval<V>(), + std::declval<V>())), V>::value, + "The result of cond(M, V, V) should have exactly the type V"); + + static_assert + (std::is_same<decltype(cond(std::declval<const M&>(), + std::declval<const V&>(), + std::declval<const V&>())), V>::value, + "The result of cond(const M&, const V&, const V&) should have " + "exactly the type V"); + + static_assert + (std::is_same<decltype(cond(std::declval<M&>(), std::declval<V&>(), + std::declval<V&>())), V>::value, + "The result of cond(M&, V&, V&) should have exactly the type V"); + + V vec1 = leftVector<V>(); + V vec2 = rightVector<V>(); + + DUNE_SIMD_CHECK(allTrue(cond(M(true), vec1, vec2) == vec1)); + DUNE_SIMD_CHECK(allTrue(cond(M(false), vec1, vec2) == vec2)); + + auto mixedResult = broadcast<V>(0); + auto mixedMask = broadcast<M>(false); + for(std::size_t l = 0; l < lanes(mixedMask); ++l) + { + lane(l, mixedMask ) = (l % 2); + lane(l, mixedResult) = lane(l, (l % 2) ? vec1 : vec2); + } + + DUNE_SIMD_CHECK(allTrue(cond(mixedMask, vec1, vec2) == mixedResult)); + } + + template<class V> + void checkBoolCond() + { + static_assert + (std::is_same<decltype(cond(std::declval<bool>(), std::declval<V>(), + std::declval<V>())), V>::value, + "The result of cond(bool, V, V) should have exactly the type V"); + + static_assert + (std::is_same<decltype(cond(std::declval<const bool&>(), + std::declval<const V&>(), + std::declval<const V&>())), V>::value, + "The result of cond(const bool&, const V&, const V&) should have " + "exactly the type V"); + + static_assert + (std::is_same<decltype(cond(std::declval<bool&>(), + std::declval<V&>(), + std::declval<V&>())), V>::value, + "The result of cond(bool&, V&, V&) should have exactly the type V"); + + V vec1 = leftVector<V>(); + V vec2 = rightVector<V>(); + + DUNE_SIMD_CHECK(allTrue(cond(true, vec1, vec2) == vec1)); + DUNE_SIMD_CHECK(allTrue(cond(false, vec1, vec2) == vec2)); + } + + template<class V> + std::enable_if_t<!Impl::LessThenComparable<Scalar<V> >::value> + checkHorizontalMinMax() {} + + template<class V> + std::enable_if_t<Impl::LessThenComparable<Scalar<V> >::value> + checkHorizontalMinMax() + { + static_assert + (std::is_same<decltype(max(std::declval<V>())), Scalar<V> >::value, + "The result of max(V) should be exactly Scalar<V>"); + + static_assert + (std::is_same<decltype(min(std::declval<V>())), Scalar<V> >::value, + "The result of min(V) should be exactly Scalar<V>"); + + static_assert + (std::is_same<decltype(max(std::declval<V&>())), Scalar<V> >::value, + "The result of max(V) should be exactly Scalar<V>"); + + static_assert + (std::is_same<decltype(min(std::declval<V&>())), Scalar<V> >::value, + "The result of min(V) should be exactly Scalar<V>"); + + const V vec1 = leftVector<V>(); + + DUNE_SIMD_CHECK(max(vec1) == Scalar<V>(lanes(vec1))); + DUNE_SIMD_CHECK(min(vec1) == Scalar<V>(1)); + } + + template<class V> + std::enable_if_t<!Impl::LessThenComparable<Scalar<V> >::value> + checkBinaryMinMax() {} + + template<class V> + std::enable_if_t<Impl::LessThenComparable<Scalar<V> >::value> + checkBinaryMinMax() + { + using std::max; + using std::min; + + static_assert + (std::is_same<decltype(Simd::max(std::declval<V>(), + std::declval<V>())), V>::value, + "The result of Simd::max(V, V) should be exactly V"); + static_assert + (std::is_same<decltype(Simd::min(std::declval<V>(), + std::declval<V>())), V>::value, + "The result of Simd::min(V, V) should be exactly V"); + + static_assert + (std::is_same<decltype(Simd::max(std::declval<V&>(), + std::declval<V&>())), V>::value, + "The result of Simd::max(V&, V&) should be exactly V"); + static_assert + (std::is_same<decltype(Simd::min(std::declval<V&>(), + std::declval<V&>())), V>::value, + "The result of Simd::min(V&, V&) should be exactly V"); + + const V arg1 = leftVector<V>(); + const V arg2 = rightVector<V>(); + + V maxExp(Scalar<V>(0)), minExp(Scalar<V>(0)); + for(auto l : range(lanes<V>())) + { + lane(l, maxExp) = max(lane(l, arg1), lane(l, arg2)); + lane(l, minExp) = min(lane(l, arg1), lane(l, arg2)); + } + + DUNE_SIMD_CHECK(allTrue(maxExp == Simd::max(arg1, arg2))); + DUNE_SIMD_CHECK(allTrue(minExp == Simd::min(arg1, arg2))); + } + + template<class V> + void checkIO() + { + const V vec1 = leftVector<V>(); + + std::string reference; + { + const char *sep = ""; + for(auto l : range(lanes(vec1))) + { + std::ostringstream stream; + stream << lane(l, vec1); + + reference += sep; + reference += stream.str(); + sep = ", "; + } + } + + { + std::ostringstream stream; + stream << io(vec1); + if(lanes(vec1) == 1) + DUNE_SIMD_CHECK(stream.str() == reference); + else + DUNE_SIMD_CHECK(stream.str() == "<" + reference + ">"); + } + + { + std::ostringstream stream; + stream << vio(vec1); + DUNE_SIMD_CHECK(stream.str() == "<" + reference + ">"); + } + } #undef DUNE_SIMD_CHECK -public: -/** -* @name Test instantiation points -* -* These functions should not be called directly, but serve as explicit -* instantiation points to keep memory usage bounded during compilation. -* There should be an explicit instantiation declaration (`extern -* template ...`) in the the overall header of your unit test for each -* type that is tested (possibly implicitly tested due to recursive -* checks). Similarly, there should be an explicit instantiation -* definition (`template ...`) in a separate translation unit. Ideally, -* there should be one translation unit per explicit instantiation -* definition, otherwise each of them will contribute to the overall -* memory used during compilation. -* -* If explicitly instantiating the top-level instantiation point -* `checkType()` is not sufficient, there are further instantiation -* points for improved granularity. The hierarchy of instantiation -* points is: -* - `checkType()` -* - `checkNonOps()` -* - `checkUnaryOps()` -* - `checkBinaryOps()` -* - `checkBinaryOpsVectorVector()` -* - `checkBinaryOpsScalarVector()` -* - `checkBinaryOpsVectorScalar()` -* - `checkBinaryOpsProxyVector()` -* - `checkBinaryOpsVectorProxy()` -* -* Each instantiation point in the hierarchy implicitly instantiates its -* descendants, unless there are explicit instantiation declarations for -* them. However, for future-proofing it can make sense to explicitly -* instantiate nodes in the hierarchy even if all their children are -* already explicitly instantiated. This will limit the impact of -* instantiation points added in the future. -* -* For an example of how to do the instantiations, look at -* `standardtest`, there is cmake machinery to support you. -* -* Background: The compiler can use a lot of memory when compiling a -* unit test for many Simd vector types. E.g. for standardtest.cc, -* which tests all the fundamental arithmetic types plus \c -* std::complex, g++ 4.9.2 (-g -O0 -Wall on x86_64 GNU/Linux) used -* ~6GByte. -* -* One mitigation was to explicitly instantiate \c checkVector() (a -* previous, now obsolete incarnation of this instantiation machinery) -* for the types that are tested. Still after doing that, -* standardtest.cc needed ~1.5GByte during compilation, which is more -* than the compilation units that actually instantiated \c -* checkVector() (which clocked in at maximum at around 800MB, depending -* on how many instantiations they contained). -* -* The second mitigation was to define \c checkVector() outside of the -* class. I have no idea why this helped, but it made compilation use -* less than ~100MByte. (Yes, functions defined inside the class are -* implicitly \c inline, but the function is a template so it has inline -* semantics even when defined outside of the class. And I tried \c -* __attribute__((__noinline__)), which had no effect on memory -* consumption.) -* -* @{ -*/ -template<class V> void checkType(); -template<class V> void checkNonOps(); -template<class V> void checkUnaryOps(); -template<class V> void checkBinaryOps(); -template<class V> void checkBinaryOpsVectorVector(); -template<class V> void checkBinaryOpsScalarVector(); -template<class V> void checkBinaryOpsVectorScalar(); -template<class V> void checkBinaryOpsProxyVector(); -template<class V> void checkBinaryOpsVectorProxy(); -/** @} Group Test instantiation points */ - -//! run unit tests for simd vector type V -/** -* This function will also ensure that `check<W>()` is run, for any type -* `W = Rebind<R, V>` where `R` is in `Rebinds`, and -* `RebindPrune<W>::value == false`. No test will be run twice for a -* given type. -* -* If the result of `Rebind` is not pruned by `RebindPrune`, it will be -* passed to `RebindAccept`. If that rejects the type, a static -* assertion will trigger. -* -* \tparam Rebinds A list of types, usually in the form of a -* `TypeList`. -* \tparam RebindPrune A type predicate determining whether to run -* `check()` for types obtained from `Rebinds`. -* \tparam RebindAccept A type predicate determining whether a type is -* acceptable as the result of a `Rebind`. -*/ -template<class V, class Rebinds, -template<class> class RebindPrune = IsLoop, -template<class> class RebindAccept = Std::to_true_type> -void check() { -// check whether the test for this type already started -if(seen_.emplace(typeid (V)).second == false) -{ -// type already seen, nothing to do -return; -} - -// do these first so everything that appears after "Checking SIMD type -// ..." really pertains to that type -auto recurse = [this](auto w) { -using W = typename decltype(w)::type; -this->template check<W, Rebinds, RebindPrune, RebindAccept>(); -}; -checkRebindOf<V, Rebinds, RebindPrune, RebindAccept>(recurse); - -checkType<V>(); -} - -//! whether all tests succeeded -bool good() const -{ -return good_; -} - -}; // class UnitTest - -template<class V> void UnitTest::checkType() -{ -static_assert(std::is_same<V, std::decay_t<V> >::value, "Simd types " -"must not be references, and must not include " -"cv-qualifiers"); - -log_ << "Checking SIMD type " << className<V>() << std::endl; - -checkNonOps<V>(); -checkUnaryOps<V>(); -checkBinaryOps<V>(); -} -template<class V> void UnitTest::checkNonOps() -{ -constexpr auto isMask = typename std::is_same<Scalar<V>, bool>::type{}; - -checkLanes<V>(); -checkScalar<V>(); - -checkDefaultConstruct<V>(); -checkLane<V>(); -checkCopyMoveConstruct<V>(); -checkImplCast<V>(); -checkBroadcast<V>(); -if constexpr (isMask) -this->template checkBroadcastMaskConstruct<V>(); -else -this->template checkBroadcastVectorConstruct<V>(); -checkBracedAssign<V>(); -checkBracedBroadcastAssign<V>(); - -checkAutoCopy<V>(); -checkCond<V>(); -checkBoolCond<V>(); - -if constexpr (isMask) -this->template checkBoolReductions<V>(); -// checkBoolReductions() is not applicable for non-masks - -checkHorizontalMinMax<V>(); -checkBinaryMinMax<V>(); -checkIO<V>(); -} -template<class V> void UnitTest::checkUnaryOps() -{ -if constexpr (std::is_same_v<Scalar<V>, bool>) { -// check mask -auto check = [this](auto op) { -this->template checkUnaryOpsV<V>(op); -}; - -// postfix -// check(OpPostfixDecrement{}); -// clang deprecation warning if bool++ is tested -// check(OpPostfixIncrement{}); - -// prefix -// check(OpPrefixDecrement{}); -// clang deprecation warning if ++bool is tested -// check(OpPrefixIncrement{}); - -// check(OpPrefixPlus{}); -// check(OpPrefixMinus{}); -check(OpPrefixLogicNot{}); -// check(OpPrefixBitNot{}); -} -else { -// check vector -auto check = [this](auto op) { -this->template checkUnaryOpsV<V>(op); -}; - -// postfix -// check(OpPostfixDecrement{}); -// check(OpPostfixIncrement{}); - -// prefix -// check(OpPrefixDecrement{}); -// check(OpPrefixIncrement{}); - -// check(OpPrefixPlus{}); -check(OpPrefixMinus{}); -check(OpPrefixLogicNot{}); -check(OpPrefixBitNot{}); -} -} -template<class V> void UnitTest::checkBinaryOps() -{ -checkBinaryOpsVectorVector<V>(); -checkBinaryOpsScalarVector<V>(); -checkBinaryOpsVectorScalar<V>(); -checkBinaryOpsProxyVector<V>(); -checkBinaryOpsVectorProxy<V>(); -} -template<class V> void UnitTest::checkBinaryOpsVectorVector() -{ -auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { -auto check = [this,op](auto t1, auto t2) { -this->checkBinaryOpVV(t1, t2, op); -}; -this->checkBinaryRefQual<V, V, doVV>(check); -}; -checkBinaryOps<V>(checker); -} -template<class V> void UnitTest::checkBinaryOpsScalarVector() -{ -auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { -auto check = [this,op](auto t1, auto t2) { -this->checkBinaryOpSV(t1, t2, op); -}; -this->checkBinaryRefQual<Scalar<V>, V, doSV>(check); - -auto crossCheck = [this,op](auto t1, auto t2) { -this->checkBinaryOpVVAgainstSV(t1, t2, op); -}; -this->checkBinaryRefQual<Scalar<V>, V, doSV && doVV>(crossCheck); -}; -checkBinaryOps<V>(checker); -} -template<class V> void UnitTest::checkBinaryOpsVectorScalar() -{ -auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { -auto check = [this,op](auto t1, auto t2) { -this->checkBinaryOpVS(t1, t2, op); -}; -this->checkBinaryRefQual<V, Scalar<V>, doVS>(check); - -auto crossCheck = [this,op](auto t1, auto t2) { -this->checkBinaryOpVVAgainstVS(t1, t2, op); -}; -this->checkBinaryRefQual<V, Scalar<V>, doVV && doVS>(crossCheck); -}; -checkBinaryOps<V>(checker); -} -template<class V> void UnitTest::checkBinaryOpsProxyVector() -{ -auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { -auto check = [this,op](auto t1, auto t2) { -this->checkBinaryOpPV(t1, t2, op); -}; -this->checkBinaryRefQual<V, V, doSV>(check); -}; -checkBinaryOps<V>(checker); -} -template<class V> void UnitTest::checkBinaryOpsVectorProxy() -{ -auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { -auto check = [this,op](auto t1, auto t2) { -this->checkBinaryOpVP(t1, t2, op); -}; -this->checkBinaryRefQual<V, V, doVS>(check); -}; -checkBinaryOps<V>(checker); -} - -} // namespace Simd + public: + /** + * @name Test instantiation points + * + * These functions should not be called directly, but serve as explicit + * instantiation points to keep memory usage bounded during compilation. + * There should be an explicit instantiation declaration (`extern + * template ...`) in the the overall header of your unit test for each + * type that is tested (possibly implicitly tested due to recursive + * checks). Similarly, there should be an explicit instantiation + * definition (`template ...`) in a separate translation unit. Ideally, + * there should be one translation unit per explicit instantiation + * definition, otherwise each of them will contribute to the overall + * memory used during compilation. + * + * If explicitly instantiating the top-level instantiation point + * `checkType()` is not sufficient, there are further instantiation + * points for improved granularity. The hierarchy of instantiation + * points is: + * - `checkType()` + * - `checkNonOps()` + * - `checkUnaryOps()` + * - `checkBinaryOps()` + * - `checkBinaryOpsVectorVector()` + * - `checkBinaryOpsScalarVector()` + * - `checkBinaryOpsVectorScalar()` + * - `checkBinaryOpsProxyVector()` + * - `checkBinaryOpsVectorProxy()` + * + * Each instantiation point in the hierarchy implicitly instantiates its + * descendants, unless there are explicit instantiation declarations for + * them. However, for future-proofing it can make sense to explicitly + * instantiate nodes in the hierarchy even if all their children are + * already explicitly instantiated. This will limit the impact of + * instantiation points added in the future. + * + * For an example of how to do the instantiations, look at + * `standardtest`, there is cmake machinery to support you. + * + * Background: The compiler can use a lot of memory when compiling a + * unit test for many Simd vector types. E.g. for standardtest.cc, + * which tests all the fundamental arithmetic types plus \c + * std::complex, g++ 4.9.2 (-g -O0 -Wall on x86_64 GNU/Linux) used + * ~6GByte. + * + * One mitigation was to explicitly instantiate \c checkVector() (a + * previous, now obsolete incarnation of this instantiation machinery) + * for the types that are tested. Still after doing that, + * standardtest.cc needed ~1.5GByte during compilation, which is more + * than the compilation units that actually instantiated \c + * checkVector() (which clocked in at maximum at around 800MB, depending + * on how many instantiations they contained). + * + * The second mitigation was to define \c checkVector() outside of the + * class. I have no idea why this helped, but it made compilation use + * less than ~100MByte. (Yes, functions defined inside the class are + * implicitly \c inline, but the function is a template so it has inline + * semantics even when defined outside of the class. And I tried \c + * __attribute__((__noinline__)), which had no effect on memory + * consumption.) + * + * @{ + */ + template<class V> void checkType(); + template<class V> void checkNonOps(); + template<class V> void checkUnaryOps(); + template<class V> void checkBinaryOps(); + template<class V> void checkBinaryOpsVectorVector(); + template<class V> void checkBinaryOpsScalarVector(); + template<class V> void checkBinaryOpsVectorScalar(); + template<class V> void checkBinaryOpsProxyVector(); + template<class V> void checkBinaryOpsVectorProxy(); + /** @} Group Test instantiation points */ + + //! run unit tests for simd vector type V + /** + * This function will also ensure that `check<W>()` is run, for any type + * `W = Rebind<R, V>` where `R` is in `Rebinds`, and + * `RebindPrune<W>::value == false`. No test will be run twice for a + * given type. + * + * If the result of `Rebind` is not pruned by `RebindPrune`, it will be + * passed to `RebindAccept`. If that rejects the type, a static + * assertion will trigger. + * + * \tparam Rebinds A list of types, usually in the form of a + * `TypeList`. + * \tparam RebindPrune A type predicate determining whether to run + * `check()` for types obtained from `Rebinds`. + * \tparam RebindAccept A type predicate determining whether a type is + * acceptable as the result of a `Rebind`. + */ + template<class V, class Rebinds, + template<class> class RebindPrune = IsLoop, + template<class> class RebindAccept = Std::to_true_type> + void check() { + // check whether the test for this type already started + if(seen_.emplace(typeid (V)).second == false) + { + // type already seen, nothing to do + return; + } + + // do these first so everything that appears after "Checking SIMD type + // ..." really pertains to that type + auto recurse = [this](auto w) { + using W = typename decltype(w)::type; + this->template check<W, Rebinds, RebindPrune, RebindAccept>(); + }; + checkRebindOf<V, Rebinds, RebindPrune, RebindAccept>(recurse); + + checkType<V>(); + } + + //! whether all tests succeeded + bool good() const + { + return good_; + } + + }; // class UnitTest + + template<class V> void UnitTest::checkType() + { + static_assert(std::is_same<V, std::decay_t<V> >::value, "Simd types " + "must not be references, and must not include " + "cv-qualifiers"); + + log_ << "Checking SIMD type " << className<V>() << std::endl; + + checkNonOps<V>(); + checkUnaryOps<V>(); + checkBinaryOps<V>(); + } + template<class V> void UnitTest::checkNonOps() + { + constexpr auto isMask = typename std::is_same<Scalar<V>, bool>::type{}; + + checkLanes<V>(); + checkScalar<V>(); + + checkDefaultConstruct<V>(); + checkLane<V>(); + checkCopyMoveConstruct<V>(); + checkImplCast<V>(); + checkBroadcast<V>(); + if constexpr (isMask) + this->template checkBroadcastMaskConstruct<V>(); + else + this->template checkBroadcastVectorConstruct<V>(); + checkBracedAssign<V>(); + checkBracedBroadcastAssign<V>(); + + checkAutoCopy<V>(); + checkCond<V>(); + checkBoolCond<V>(); + + if constexpr (isMask) + this->template checkBoolReductions<V>(); + // checkBoolReductions() is not applicable for non-masks + + checkHorizontalMinMax<V>(); + checkBinaryMinMax<V>(); + checkIO<V>(); + } + template<class V> void UnitTest::checkUnaryOps() + { + if constexpr (std::is_same_v<Scalar<V>, bool>) { + // check mask + auto check = [this](auto op) { + this->template checkUnaryOpsV<V>(op); + }; + + // postfix + // check(OpPostfixDecrement{}); + // clang deprecation warning if bool++ is tested + // check(OpPostfixIncrement{}); + + // prefix + // check(OpPrefixDecrement{}); + // clang deprecation warning if ++bool is tested + // check(OpPrefixIncrement{}); + + // check(OpPrefixPlus{}); + // check(OpPrefixMinus{}); + check(OpPrefixLogicNot{}); + // check(OpPrefixBitNot{}); + } + else { + // check vector + auto check = [this](auto op) { + this->template checkUnaryOpsV<V>(op); + }; + + // postfix + // check(OpPostfixDecrement{}); + // check(OpPostfixIncrement{}); + + // prefix + // check(OpPrefixDecrement{}); + // check(OpPrefixIncrement{}); + + // check(OpPrefixPlus{}); + check(OpPrefixMinus{}); + check(OpPrefixLogicNot{}); + check(OpPrefixBitNot{}); + } + } + template<class V> void UnitTest::checkBinaryOps() + { + checkBinaryOpsVectorVector<V>(); + checkBinaryOpsScalarVector<V>(); + checkBinaryOpsVectorScalar<V>(); + checkBinaryOpsProxyVector<V>(); + checkBinaryOpsVectorProxy<V>(); + } + template<class V> void UnitTest::checkBinaryOpsVectorVector() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpVV(t1, t2, op); + }; + this->checkBinaryRefQual<V, V, doVV>(check); + }; + checkBinaryOps<V>(checker); + } + template<class V> void UnitTest::checkBinaryOpsScalarVector() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpSV(t1, t2, op); + }; + this->checkBinaryRefQual<Scalar<V>, V, doSV>(check); + + auto crossCheck = [this,op](auto t1, auto t2) { + this->checkBinaryOpVVAgainstSV(t1, t2, op); + }; + this->checkBinaryRefQual<Scalar<V>, V, doSV && doVV>(crossCheck); + }; + checkBinaryOps<V>(checker); + } + template<class V> void UnitTest::checkBinaryOpsVectorScalar() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpVS(t1, t2, op); + }; + this->checkBinaryRefQual<V, Scalar<V>, doVS>(check); + + auto crossCheck = [this,op](auto t1, auto t2) { + this->checkBinaryOpVVAgainstVS(t1, t2, op); + }; + this->checkBinaryRefQual<V, Scalar<V>, doVV && doVS>(crossCheck); + }; + checkBinaryOps<V>(checker); + } + template<class V> void UnitTest::checkBinaryOpsProxyVector() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpPV(t1, t2, op); + }; + this->checkBinaryRefQual<V, V, doSV>(check); + }; + checkBinaryOps<V>(checker); + } + template<class V> void UnitTest::checkBinaryOpsVectorProxy() + { + auto checker = [this](auto doSV, auto doVV, auto doVS, auto op) { + auto check = [this,op](auto t1, auto t2) { + this->checkBinaryOpVP(t1, t2, op); + }; + this->checkBinaryRefQual<V, V, doVS>(check); + }; + checkBinaryOps<V>(checker); + } + + } // namespace Simd } // namespace Dune #endif // DUNE_COMMON_SIMD_TEST_HH diff --git a/dune/common/simd/vc.hh b/dune/common/simd/vc.hh index 2d9fcc9474909bc356700775a473fcf22d7d0a11..2086fa85a6fbc3bbe0e6af1b64975b968be80d80 100644 --- a/dune/common/simd/vc.hh +++ b/dune/common/simd/vc.hh @@ -3,9 +3,9 @@ #include <dune/internal/dune-common.hh> /** @file -* @ingroup SIMDVc -* @brief SIMD abstractions for Vc -*/ + * @ingroup SIMDVc + * @brief SIMD abstractions for Vc + */ #include <cstddef> #include <type_traits> @@ -19,742 +19,742 @@ #include <dune/common/vc.hh> /** @defgroup SIMDVc SIMD Abstraction Implementation for Vc -* @ingroup SIMDApp -* -* This implements the vectorization interface for Vc types, namely -* `Vc::Vector`, `Vc::Mask`, `Vc::SimdArray` and `Vc::SimdMask`. -* -* As an application developer, you need to `#include -* <dune/common/simd/vc.hh>`. You need to make sure that `HAVE_VC` is true -* before doing so: -* -* - If your program works both in the presence and the absence of Vc, wrap -* the include in `#if HAVE_VC` and `#endif` -* -* - If you write a unit test, in your `CMakeLists.txt` use -* `dune_add_test(... CMAKE_GUARD Vc_FOUND)` -* -* You also need to make sure that the compiler uses the correct flags and -* that the linker can find the library. (The compilation flags include one -* that ensures a name mangling scheme that can distiguish the -* compiler-intrinsic vector types from non-vector types is used.) -* -* - Either use `add_dune_vc_flags(your_application)` in `CMakeLists.txt`, -* -* - or use `dune_enable_all_packages()` in your module's toplevel -* `CMakeLists.txt`. -* -* There should be no need to explicitly call `find_package(Vc)` in your -* `CMakeLists.txt`, dune-common already does that. If your module can't live -* without Vc, you may however want to do something like this in your -* `cmake/modules/YourModuleMacros.cmake`: -* \code -* if(NOT Vc_FOUND) -* message(SEND_ERROR "This module requires Vc") -* endif(NOT Vc_FOUND) -* \endcode -* -* If you just want to compile dune, and have Vc installed to a location where -* cmake is not looking by default, you need to add that location to -* `CMAKE_PREFIX_PATH`. E.g. pass `-DCMAKE_PREFIX_PATH=$VCDIR` to cmake, for -* instance by including that in the variable `CMAKE_FLAGS` in the options -* file that you pass to dunecontrol. `$VCDIR` should be the same directory -* that you passed in `-DCMAKE_INSTALL_PREFIX=...` to cmake when you compiled -* Vc, i.e. Vc's main include file should be found under -* `$VCDIR/include/Vc/Vc`, and the library under `$VCDIR/lib/libVc.a` or -* similar. -* -* @section SIMDVcRestrictions Restrictions -* -* During thorough testing of the Vc abstraction implementation it turned out -* that certain operations were not supported, or were buggy. This meant that -* the tests had to be relaxed, and/or some restrictions had to made as to how -* things must be done through the SIMD abstraction, see @ref -* simd_abstraction_limit. -* -* For future reference, here is a detailed list of things that certain Vc -* types do or don't support. `s` denotes a scalar object/expression (i.e. of -* type `double` or in the case of masks `bool`). `v` denotes a vector/mask -* object/expression. `sv` means that both scalar and vector arguments are -* accepted. `V` denotes a vector/mask type. `@` means any applicable -* operator that is not otherwise listed. -* -* <!-- The following table is in orgtbl format -- If you are using emacs, you -* may want to enable the `orgtbl` minor mode. We substitute `|` with -* `¦` when describing or-operators so as to not confuse orgtbl. --> -* \code -| | Vector | Vector | SimdArray | SimdArray | Masks[4] | -| | <double> AVX | <int> SSE | <double,4> | <int,4> | | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| V v(s); | y | y | y | y | y | -| V v = s; | y | y | y | y | *N* | -| V v{s}; | *N* | y | *N* | *N* | y | -| V v = {s}; | *N* | y | *N* | *N* | *N* | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| v = s; | y | y | y | y | *N* | -| v = {s}; | *N* | *N* | *N* | *N* | *N* | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| v++; ++v; | y | y | *N* | *N* | y(n/a)[2] | -| v--; --v; | y | y | *N* | *N* | n/a | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| +v; -v; | y | y | y | y | *N* | -| !v; | y | y | y | y | y | -| ~v; | n/a | y | n/a | y | *N* | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| sv @ sv; but see below | y | y | y | y | *N* | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| s << v; s >> v; | n/a | *N* | n/a | *N* | *N* | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| v == v; v != v; | y | y | y | y | *N* [1] | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| v & v; v ^ v; v ¦ v; | n/a | y | n/a | y | y | -| sv && sv; sv ¦¦ sv; | y | y | *N* | *N* | *N* | -| v && v; v ¦¦ v; | y | y | *N* | *N* | y | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| v @= sv; but see below | y | y | y | y | *N* | -| v &= v; v ^= v; v ¦= v; | n/a | y | n/a | y | y | -|-------------------------+--------------+-----------+------------+-----------+-----------| -| v, v;[3] | *N* | *N* | y | y | y | -* \endcode -* -* Notes: -* -* - [1] The result of the mask-mask `==` and `!=` operation is a scalar. -* -* - [2] `++` (either kind) on bools is deprecated by the standard -* -* - [3] contrary to the other operators, the expected result for `(sv1, sv2)` -* is exactly `sv2`, no broadcasting applied. -* -* - [4] Checked with `Vector<int>::Mask` [SSE] and `SimdArray<int, 4>::Mask`, -* which behaved identical -* -* Support levels: -* -* - `y`: operation generally works; some instances of the operation may not -* apply -* -* - `*N*`: operation generally does not work; some instances of the operation -* may not apply -* -* - `n/a`: operation does not apply (i.e. bitwise operations to -* floating-point operands, `--` (and in the future possibly `++`) to -* boolean operands, assignment operators to scalar left hand sides) -* -* Each operation was tested with the full set of combinations of possible -* `const`/non-`const` lvalue/xvalue arguments. Each combination of constness -* and value category was applied to the scalar type and the operation tried -* in an SFINAE context; combinations that failed here were skipped for vector -* arguments too. -*/ + * @ingroup SIMDApp + * + * This implements the vectorization interface for Vc types, namely + * `Vc::Vector`, `Vc::Mask`, `Vc::SimdArray` and `Vc::SimdMask`. + * + * As an application developer, you need to `#include + * <dune/common/simd/vc.hh>`. You need to make sure that `HAVE_VC` is true + * before doing so: + * + * - If your program works both in the presence and the absence of Vc, wrap + * the include in `#if HAVE_VC` and `#endif` + * + * - If you write a unit test, in your `CMakeLists.txt` use + * `dune_add_test(... CMAKE_GUARD Vc_FOUND)` + * + * You also need to make sure that the compiler uses the correct flags and + * that the linker can find the library. (The compilation flags include one + * that ensures a name mangling scheme that can distiguish the + * compiler-intrinsic vector types from non-vector types is used.) + * + * - Either use `add_dune_vc_flags(your_application)` in `CMakeLists.txt`, + * + * - or use `dune_enable_all_packages()` in your module's toplevel + * `CMakeLists.txt`. + * + * There should be no need to explicitly call `find_package(Vc)` in your + * `CMakeLists.txt`, dune-common already does that. If your module can't live + * without Vc, you may however want to do something like this in your + * `cmake/modules/YourModuleMacros.cmake`: + * \code + * if(NOT Vc_FOUND) + * message(SEND_ERROR "This module requires Vc") + * endif(NOT Vc_FOUND) + * \endcode + * + * If you just want to compile dune, and have Vc installed to a location where + * cmake is not looking by default, you need to add that location to + * `CMAKE_PREFIX_PATH`. E.g. pass `-DCMAKE_PREFIX_PATH=$VCDIR` to cmake, for + * instance by including that in the variable `CMAKE_FLAGS` in the options + * file that you pass to dunecontrol. `$VCDIR` should be the same directory + * that you passed in `-DCMAKE_INSTALL_PREFIX=...` to cmake when you compiled + * Vc, i.e. Vc's main include file should be found under + * `$VCDIR/include/Vc/Vc`, and the library under `$VCDIR/lib/libVc.a` or + * similar. + * + * @section SIMDVcRestrictions Restrictions + * + * During thorough testing of the Vc abstraction implementation it turned out + * that certain operations were not supported, or were buggy. This meant that + * the tests had to be relaxed, and/or some restrictions had to made as to how + * things must be done through the SIMD abstraction, see @ref + * simd_abstraction_limit. + * + * For future reference, here is a detailed list of things that certain Vc + * types do or don't support. `s` denotes a scalar object/expression (i.e. of + * type `double` or in the case of masks `bool`). `v` denotes a vector/mask + * object/expression. `sv` means that both scalar and vector arguments are + * accepted. `V` denotes a vector/mask type. `@` means any applicable + * operator that is not otherwise listed. + * + * <!-- The following table is in orgtbl format -- If you are using emacs, you + * may want to enable the `orgtbl` minor mode. We substitute `|` with + * `¦` when describing or-operators so as to not confuse orgtbl. --> + * \code + | | Vector | Vector | SimdArray | SimdArray | Masks[4] | + | | <double> AVX | <int> SSE | <double,4> | <int,4> | | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | V v(s); | y | y | y | y | y | + | V v = s; | y | y | y | y | *N* | + | V v{s}; | *N* | y | *N* | *N* | y | + | V v = {s}; | *N* | y | *N* | *N* | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v = s; | y | y | y | y | *N* | + | v = {s}; | *N* | *N* | *N* | *N* | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v++; ++v; | y | y | *N* | *N* | y(n/a)[2] | + | v--; --v; | y | y | *N* | *N* | n/a | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | +v; -v; | y | y | y | y | *N* | + | !v; | y | y | y | y | y | + | ~v; | n/a | y | n/a | y | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | sv @ sv; but see below | y | y | y | y | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | s << v; s >> v; | n/a | *N* | n/a | *N* | *N* | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v == v; v != v; | y | y | y | y | *N* [1] | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v & v; v ^ v; v ¦ v; | n/a | y | n/a | y | y | + | sv && sv; sv ¦¦ sv; | y | y | *N* | *N* | *N* | + | v && v; v ¦¦ v; | y | y | *N* | *N* | y | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v @= sv; but see below | y | y | y | y | *N* | + | v &= v; v ^= v; v ¦= v; | n/a | y | n/a | y | y | + |-------------------------+--------------+-----------+------------+-----------+-----------| + | v, v;[3] | *N* | *N* | y | y | y | + * \endcode + * + * Notes: + * + * - [1] The result of the mask-mask `==` and `!=` operation is a scalar. + * + * - [2] `++` (either kind) on bools is deprecated by the standard + * + * - [3] contrary to the other operators, the expected result for `(sv1, sv2)` + * is exactly `sv2`, no broadcasting applied. + * + * - [4] Checked with `Vector<int>::Mask` [SSE] and `SimdArray<int, 4>::Mask`, + * which behaved identical + * + * Support levels: + * + * - `y`: operation generally works; some instances of the operation may not + * apply + * + * - `*N*`: operation generally does not work; some instances of the operation + * may not apply + * + * - `n/a`: operation does not apply (i.e. bitwise operations to + * floating-point operands, `--` (and in the future possibly `++`) to + * boolean operands, assignment operators to scalar left hand sides) + * + * Each operation was tested with the full set of combinations of possible + * `const`/non-`const` lvalue/xvalue arguments. Each combination of constness + * and value category was applied to the scalar type and the operation tried + * in an SFINAE context; combinations that failed here were skipped for vector + * arguments too. + */ namespace Dune { -namespace Simd { - -namespace VcImpl { - -//! specialized to true for Vc mask types -template<class V, class SFINAE = void> -struct IsMask : std::false_type {}; - -template<typename T, typename A> -struct IsMask<Vc::Mask<T, A> > : std::true_type {}; - -template<typename T, std::size_t n, typename V, std::size_t m> -struct IsMask<Vc::SimdMaskArray<T, n, V, m> > : std::true_type {}; - -//! specialized to true for Vc vector and mask types -template<class V, class SFINAE = void> -struct IsVector : IsMask<V> {}; - -template<typename T, typename A> -struct IsVector<Vc::Vector<T, A> > : std::true_type {}; - -template<typename T, std::size_t n, typename V, std::size_t m> -struct IsVector<Vc::SimdArray<T, n, V, m> > : std::true_type {}; - -template<typename T> struct IsVectorizable : std::false_type {}; -template<> struct IsVectorizable<double> : std::true_type {}; -template<> struct IsVectorizable<float> : std::true_type {}; -template<> struct IsVectorizable<std::int32_t> : std::true_type {}; -template<> struct IsVectorizable<std::uint32_t> : std::true_type {}; -template<> struct IsVectorizable<std::int16_t> : std::true_type {}; -template<> struct IsVectorizable<std::uint16_t> : std::true_type {}; - -//! A reference-like proxy for elements of random-access vectors. -/** -* This is necessary because Vc's lane-access operation return a proxy -* that cannot constructed by non-Vc code (i.e. code that isn't -* explicitly declared `friend`). This means in particular that there -* is no copy/move constructor, meaning we cannot return such proxies -* from our own functions, such as `lane()`. To work around this, we -* define our own proxy class which internally holds a reference to the -* vector and a lane index. -* -* Note: this should be unnecessary with C++17, as just returning a -* temporary object should not involve copying it. -*/ -template<class V> -class Proxy -{ -static_assert(std::is_same<V, std::decay_t<V> >::value, "Class Proxy " -"may only be instantiated with unqualified types"); -public: -using value_type = typename V::value_type; - -private: -static_assert(std::is_arithmetic<value_type>::value, -"Only artihmetic types are supported"); -V &vec_; -std::size_t idx_; - -public: -Proxy(std::size_t idx, V &vec) -: vec_(vec), idx_(idx) -{ } - -Proxy(const Proxy&) = delete; -// allow move construction so we can return proxies from functions -Proxy(Proxy&&) = default; - -operator value_type() const { return vec_[idx_]; } - -// assignment operators + namespace Simd { + + namespace VcImpl { + + //! specialized to true for Vc mask types + template<class V, class SFINAE = void> + struct IsMask : std::false_type {}; + + template<typename T, typename A> + struct IsMask<Vc::Mask<T, A> > : std::true_type {}; + + template<typename T, std::size_t n, typename V, std::size_t m> + struct IsMask<Vc::SimdMaskArray<T, n, V, m> > : std::true_type {}; + + //! specialized to true for Vc vector and mask types + template<class V, class SFINAE = void> + struct IsVector : IsMask<V> {}; + + template<typename T, typename A> + struct IsVector<Vc::Vector<T, A> > : std::true_type {}; + + template<typename T, std::size_t n, typename V, std::size_t m> + struct IsVector<Vc::SimdArray<T, n, V, m> > : std::true_type {}; + + template<typename T> struct IsVectorizable : std::false_type {}; + template<> struct IsVectorizable<double> : std::true_type {}; + template<> struct IsVectorizable<float> : std::true_type {}; + template<> struct IsVectorizable<std::int32_t> : std::true_type {}; + template<> struct IsVectorizable<std::uint32_t> : std::true_type {}; + template<> struct IsVectorizable<std::int16_t> : std::true_type {}; + template<> struct IsVectorizable<std::uint16_t> : std::true_type {}; + + //! A reference-like proxy for elements of random-access vectors. + /** + * This is necessary because Vc's lane-access operation return a proxy + * that cannot constructed by non-Vc code (i.e. code that isn't + * explicitly declared `friend`). This means in particular that there + * is no copy/move constructor, meaning we cannot return such proxies + * from our own functions, such as `lane()`. To work around this, we + * define our own proxy class which internally holds a reference to the + * vector and a lane index. + * + * Note: this should be unnecessary with C++17, as just returning a + * temporary object should not involve copying it. + */ + template<class V> + class Proxy + { + static_assert(std::is_same<V, std::decay_t<V> >::value, "Class Proxy " + "may only be instantiated with unqualified types"); + public: + using value_type = typename V::value_type; + + private: + static_assert(std::is_arithmetic<value_type>::value, + "Only artihmetic types are supported"); + V &vec_; + std::size_t idx_; + + public: + Proxy(std::size_t idx, V &vec) + : vec_(vec), idx_(idx) + { } + + Proxy(const Proxy&) = delete; + // allow move construction so we can return proxies from functions + Proxy(Proxy&&) = default; + + operator value_type() const { return vec_[idx_]; } + + // assignment operators #define DUNE_SIMD_VC_ASSIGNMENT(OP) \ -template<class T, \ -class = decltype(std::declval<value_type&>() OP \ -autoCopy(std::declval<T>()) )> \ -Proxy operator OP(T &&o) && \ -{ \ -vec_[idx_] OP autoCopy(std::forward<T>(o)); \ -return { idx_, vec_ }; \ -} -DUNE_SIMD_VC_ASSIGNMENT(=); -DUNE_SIMD_VC_ASSIGNMENT(*=); -DUNE_SIMD_VC_ASSIGNMENT(/=); -DUNE_SIMD_VC_ASSIGNMENT(%=); -DUNE_SIMD_VC_ASSIGNMENT(+=); -DUNE_SIMD_VC_ASSIGNMENT(-=); -DUNE_SIMD_VC_ASSIGNMENT(<<=); -DUNE_SIMD_VC_ASSIGNMENT(>>=); -DUNE_SIMD_VC_ASSIGNMENT(&=); -DUNE_SIMD_VC_ASSIGNMENT(^=); -DUNE_SIMD_VC_ASSIGNMENT(|=); + template<class T, \ + class = decltype(std::declval<value_type&>() OP \ + autoCopy(std::declval<T>()) )> \ + Proxy operator OP(T &&o) && \ + { \ + vec_[idx_] OP autoCopy(std::forward<T>(o)); \ + return { idx_, vec_ }; \ + } + DUNE_SIMD_VC_ASSIGNMENT(=); + DUNE_SIMD_VC_ASSIGNMENT(*=); + DUNE_SIMD_VC_ASSIGNMENT(/=); + DUNE_SIMD_VC_ASSIGNMENT(%=); + DUNE_SIMD_VC_ASSIGNMENT(+=); + DUNE_SIMD_VC_ASSIGNMENT(-=); + DUNE_SIMD_VC_ASSIGNMENT(<<=); + DUNE_SIMD_VC_ASSIGNMENT(>>=); + DUNE_SIMD_VC_ASSIGNMENT(&=); + DUNE_SIMD_VC_ASSIGNMENT(^=); + DUNE_SIMD_VC_ASSIGNMENT(|=); #undef DUNE_SIMD_VC_ASSIGNMENT -// unary (prefix) operators -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -Proxy operator++() { ++(vec_[idx_]); return *this; } -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -Proxy operator--() { --(vec_[idx_]); return *this; } - -// postfix operators -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -value_type operator++(int) { return vec_[idx_]++; } -template<class T = value_type, -class = std::enable_if_t<!std::is_same<T, bool>::value> > -value_type operator--(int) { return vec_[idx_]--; } - - -// swap on proxies swaps the proxied vector entries. As such, it -// applies to rvalues of proxies too, not just lvalues -friend void swap(const Proxy &a, const Proxy &b) { -// don't use swap() ourselves -- not supported by Vc 1.3.0 (but is -// supported by Vc 1.3.2) -value_type tmp = std::move(a.vec_[a.idx_]); -a.vec_[a.idx_] = std::move(b.vec_[b.idx_]); -b.vec_[b.idx_] = std::move(tmp); -} -friend void swap(value_type &a, const Proxy &b) { -// don't use swap() ourselves -- not supported by Vc 1.3.0 (but is -// supported by Vc 1.3.2) -value_type tmp = std::move(a); -a = std::move(b.vec_[b.idx_]); -b.vec_[b.idx_] = std::move(tmp); -} -friend void swap(const Proxy &a, value_type &b) { -// don't use swap() ourselves -- not supported by Vc 1.3.0 (but is -// supported by Vc 1.3.2) -value_type tmp = std::move(a.vec_[a.idx_]); -a.vec_[a.idx_] = std::move(b); -b = std::move(tmp); -} - -// binary operators -// -// Normally, these are provided by the conversion operator in -// combination with C++'s builtin binary operators. Other classes -// that need to provide the binary operators themselves should either -// 1. deduce the "foreign" operand type independently, i.e. use -// template<class... Args, class Foreign> -// auto operator@(MyClass<Args...>, Foreign); -// or -// 2. not deduce anything from the foreign argument, i.e. -// template<class... Args> -// auto operator@(MyClass<Args...>, -// typename MyClass<Args...>::value_type); -// or -// template<class T, class... Args> -// struct MyClass { -// auto operator@(T); -// } -// or -// template<class T, class... Args> -// struct MyClass { -// friend auto operator@(MyClass, T); -// } -// -// This allows either for an exact match (in the case of option 1.) or -// for conversions to be applied to the foreign argument (options 2.). -// In contrast, allowing some of the template parameters being deduced -// from the self argument also being deduced from the foreign argument -// will likely lead to ambigous deduction when the foreign argument is -// a proxy: -// template<class T, class... Args> -// auto operator@(MyClass<T, Args...>, T); -// One class that suffers from this problem ist std::complex. -// -// Note that option 1. is a bit dangerous, as the foreign argument is -// catch-all. This seems tempting in the case of a proxy class, as -// the operator could just be forwarded to the proxied object with the -// foreign argument unchanged, immediately creating interoperability -// with arbitrary foreign classes. However, if the foreign class also -// choses option 1., this will result in ambigous overloads, and there -// is no clear guide to decide which class should provide the overload -// and which should not. -// -// Fortunately, deferring to the conversion and the built-in operators -// mostly works in the case of this proxy class, because only built-in -// types can be proxied anyway. Unfortunately, the Vc vectors and -// arrays suffer from a slightly different problem. They chose option -// 1., but they can't just accept the argument type they are given, -// since they need to somehow implement the operation in terms of -// intrinsics. So they check the argument whether it is one of the -// expected types, and remove the operator from the overload set if it -// isn't via SFINAE. Of course, this proxy class is not one of the -// expected types, even though it would convert to them... -// -// So what we have to do here, unfortunately, is to provide operators -// for the Vc types explicitly, and hope that there won't be some Vc -// version that gets the operators right, thus creating ambigous -// overloads. Well, if guess it will be #ifdef time if it comes to -// that. + // unary (prefix) operators + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + Proxy operator++() { ++(vec_[idx_]); return *this; } + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + Proxy operator--() { --(vec_[idx_]); return *this; } + + // postfix operators + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + value_type operator++(int) { return vec_[idx_]++; } + template<class T = value_type, + class = std::enable_if_t<!std::is_same<T, bool>::value> > + value_type operator--(int) { return vec_[idx_]--; } + + + // swap on proxies swaps the proxied vector entries. As such, it + // applies to rvalues of proxies too, not just lvalues + friend void swap(const Proxy &a, const Proxy &b) { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + value_type tmp = std::move(a.vec_[a.idx_]); + a.vec_[a.idx_] = std::move(b.vec_[b.idx_]); + b.vec_[b.idx_] = std::move(tmp); + } + friend void swap(value_type &a, const Proxy &b) { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + value_type tmp = std::move(a); + a = std::move(b.vec_[b.idx_]); + b.vec_[b.idx_] = std::move(tmp); + } + friend void swap(const Proxy &a, value_type &b) { + // don't use swap() ourselves -- not supported by Vc 1.3.0 (but is + // supported by Vc 1.3.2) + value_type tmp = std::move(a.vec_[a.idx_]); + a.vec_[a.idx_] = std::move(b); + b = std::move(tmp); + } + + // binary operators + // + // Normally, these are provided by the conversion operator in + // combination with C++'s builtin binary operators. Other classes + // that need to provide the binary operators themselves should either + // 1. deduce the "foreign" operand type independently, i.e. use + // template<class... Args, class Foreign> + // auto operator@(MyClass<Args...>, Foreign); + // or + // 2. not deduce anything from the foreign argument, i.e. + // template<class... Args> + // auto operator@(MyClass<Args...>, + // typename MyClass<Args...>::value_type); + // or + // template<class T, class... Args> + // struct MyClass { + // auto operator@(T); + // } + // or + // template<class T, class... Args> + // struct MyClass { + // friend auto operator@(MyClass, T); + // } + // + // This allows either for an exact match (in the case of option 1.) or + // for conversions to be applied to the foreign argument (options 2.). + // In contrast, allowing some of the template parameters being deduced + // from the self argument also being deduced from the foreign argument + // will likely lead to ambigous deduction when the foreign argument is + // a proxy: + // template<class T, class... Args> + // auto operator@(MyClass<T, Args...>, T); + // One class that suffers from this problem ist std::complex. + // + // Note that option 1. is a bit dangerous, as the foreign argument is + // catch-all. This seems tempting in the case of a proxy class, as + // the operator could just be forwarded to the proxied object with the + // foreign argument unchanged, immediately creating interoperability + // with arbitrary foreign classes. However, if the foreign class also + // choses option 1., this will result in ambigous overloads, and there + // is no clear guide to decide which class should provide the overload + // and which should not. + // + // Fortunately, deferring to the conversion and the built-in operators + // mostly works in the case of this proxy class, because only built-in + // types can be proxied anyway. Unfortunately, the Vc vectors and + // arrays suffer from a slightly different problem. They chose option + // 1., but they can't just accept the argument type they are given, + // since they need to somehow implement the operation in terms of + // intrinsics. So they check the argument whether it is one of the + // expected types, and remove the operator from the overload set if it + // isn't via SFINAE. Of course, this proxy class is not one of the + // expected types, even though it would convert to them... + // + // So what we have to do here, unfortunately, is to provide operators + // for the Vc types explicitly, and hope that there won't be some Vc + // version that gets the operators right, thus creating ambigous + // overloads. Well, if guess it will be #ifdef time if it comes to + // that. #define DUNE_SIMD_VC_BINARY(OP) \ -template<class T, class Abi> \ -friend auto operator OP(const Vc::Vector<T, Abi> &l, Proxy&& r) \ --> decltype(l OP std::declval<value_type>()) \ -{ \ -return l OP value_type(r); \ -} \ -template<class T, class Abi> \ -auto operator OP(const Vc::Vector<T, Abi> &r) && \ --> decltype(std::declval<value_type>() OP r) \ -{ \ -return value_type(*this) OP r; \ -} \ -template<class T, std::size_t n, class Vec, std::size_t m> \ -friend auto \ -operator OP(const Vc::SimdArray<T, n, Vec, m> &l, Proxy&& r) \ --> decltype(l OP std::declval<value_type>()) \ -{ \ -return l OP value_type(r); \ -} \ -template<class T, std::size_t n, class Vec, std::size_t m> \ -auto operator OP(const Vc::SimdArray<T, n, Vec, m> &r) && \ --> decltype(std::declval<value_type>() OP r) \ -{ \ -return value_type(*this) OP r; \ -} - -DUNE_SIMD_VC_BINARY(*); -DUNE_SIMD_VC_BINARY(/); -DUNE_SIMD_VC_BINARY(%); -DUNE_SIMD_VC_BINARY(+); -DUNE_SIMD_VC_BINARY(-); -DUNE_SIMD_VC_BINARY(<<); -DUNE_SIMD_VC_BINARY(>>); -DUNE_SIMD_VC_BINARY(&); -DUNE_SIMD_VC_BINARY(^); -DUNE_SIMD_VC_BINARY(|); -DUNE_SIMD_VC_BINARY(<); -DUNE_SIMD_VC_BINARY(>); -DUNE_SIMD_VC_BINARY(<=); -DUNE_SIMD_VC_BINARY(>=); -DUNE_SIMD_VC_BINARY(==); -DUNE_SIMD_VC_BINARY(!=); + template<class T, class Abi> \ + friend auto operator OP(const Vc::Vector<T, Abi> &l, Proxy&& r) \ + -> decltype(l OP std::declval<value_type>()) \ + { \ + return l OP value_type(r); \ + } \ + template<class T, class Abi> \ + auto operator OP(const Vc::Vector<T, Abi> &r) && \ + -> decltype(std::declval<value_type>() OP r) \ + { \ + return value_type(*this) OP r; \ + } \ + template<class T, std::size_t n, class Vec, std::size_t m> \ + friend auto \ + operator OP(const Vc::SimdArray<T, n, Vec, m> &l, Proxy&& r) \ + -> decltype(l OP std::declval<value_type>()) \ + { \ + return l OP value_type(r); \ + } \ + template<class T, std::size_t n, class Vec, std::size_t m> \ + auto operator OP(const Vc::SimdArray<T, n, Vec, m> &r) && \ + -> decltype(std::declval<value_type>() OP r) \ + { \ + return value_type(*this) OP r; \ + } + + DUNE_SIMD_VC_BINARY(*); + DUNE_SIMD_VC_BINARY(/); + DUNE_SIMD_VC_BINARY(%); + DUNE_SIMD_VC_BINARY(+); + DUNE_SIMD_VC_BINARY(-); + DUNE_SIMD_VC_BINARY(<<); + DUNE_SIMD_VC_BINARY(>>); + DUNE_SIMD_VC_BINARY(&); + DUNE_SIMD_VC_BINARY(^); + DUNE_SIMD_VC_BINARY(|); + DUNE_SIMD_VC_BINARY(<); + DUNE_SIMD_VC_BINARY(>); + DUNE_SIMD_VC_BINARY(<=); + DUNE_SIMD_VC_BINARY(>=); + DUNE_SIMD_VC_BINARY(==); + DUNE_SIMD_VC_BINARY(!=); #undef DUNE_SIMD_VC_BINARY -// this is needed to implement broadcast construction from proxy as -// the unadorned assignment operator cannot be a non-member -template<class T, class Abi, -class = std::enable_if_t<std::is_convertible<value_type, -T>::value> > -operator Vc::Vector<T, Abi>() && -{ -return value_type(*this); -} -template<class T, std::size_t n, class Vec, std::size_t m, -class = std::enable_if_t<std::is_convertible<value_type, -T>::value> > -operator Vc::SimdArray<T, n, Vec, m>() && -{ -return value_type(*this); -} + // this is needed to implement broadcast construction from proxy as + // the unadorned assignment operator cannot be a non-member + template<class T, class Abi, + class = std::enable_if_t<std::is_convertible<value_type, + T>::value> > + operator Vc::Vector<T, Abi>() && + { + return value_type(*this); + } + template<class T, std::size_t n, class Vec, std::size_t m, + class = std::enable_if_t<std::is_convertible<value_type, + T>::value> > + operator Vc::SimdArray<T, n, Vec, m>() && + { + return value_type(*this); + } #define DUNE_SIMD_VC_ASSIGN(OP) \ -template<class T, class Abi> \ -friend auto operator OP(Vc::Vector<T, Abi> &l, Proxy&& r) \ --> decltype(l OP std::declval<value_type>()) \ -{ \ -return l OP value_type(r); \ -} - -DUNE_SIMD_VC_ASSIGN(*=); -DUNE_SIMD_VC_ASSIGN(/=); -DUNE_SIMD_VC_ASSIGN(%=); -DUNE_SIMD_VC_ASSIGN(+=); -DUNE_SIMD_VC_ASSIGN(-=); -DUNE_SIMD_VC_ASSIGN(&=); -DUNE_SIMD_VC_ASSIGN(^=); -DUNE_SIMD_VC_ASSIGN(|=); -// The shift assignment would not be needed for Vc::Vector since it -// has overloads for `int` rhs and the proxy can convert to that -- -// except that there is also overloads for Vector, and because of the -// conversion operator needed to support unadorned assignments, the -// proxy can convert to that, too. -DUNE_SIMD_VC_ASSIGN(<<=); -DUNE_SIMD_VC_ASSIGN(>>=); + template<class T, class Abi> \ + friend auto operator OP(Vc::Vector<T, Abi> &l, Proxy&& r) \ + -> decltype(l OP std::declval<value_type>()) \ + { \ + return l OP value_type(r); \ + } + + DUNE_SIMD_VC_ASSIGN(*=); + DUNE_SIMD_VC_ASSIGN(/=); + DUNE_SIMD_VC_ASSIGN(%=); + DUNE_SIMD_VC_ASSIGN(+=); + DUNE_SIMD_VC_ASSIGN(-=); + DUNE_SIMD_VC_ASSIGN(&=); + DUNE_SIMD_VC_ASSIGN(^=); + DUNE_SIMD_VC_ASSIGN(|=); + // The shift assignment would not be needed for Vc::Vector since it + // has overloads for `int` rhs and the proxy can convert to that -- + // except that there is also overloads for Vector, and because of the + // conversion operator needed to support unadorned assignments, the + // proxy can convert to that, too. + DUNE_SIMD_VC_ASSIGN(<<=); + DUNE_SIMD_VC_ASSIGN(>>=); #undef DUNE_SIMD_VC_ASSIGN -}; - -} // namespace VcImpl - -namespace Overloads { - -/** @name Specialized classes and overloaded functions -* @ingroup SIMDVc -* @{ -*/ - -//! should have a member type \c type -/** -* Implements Simd::Scalar -*/ -template<class V> -struct ScalarType<V, std::enable_if_t<VcImpl::IsVector<V>::value> > -{ -using type = typename V::value_type; -}; - -//! should have a member type \c type -/** -* Implements Simd::Rebind -* -* This specialization covers -* - Mask -> bool -* - Vector -> Scalar<Vector> -*/ -template<class V> -struct RebindType<Simd::Scalar<V>, V, -std::enable_if_t<VcImpl::IsVector<V>::value> > -{ -using type = V; -}; - -//! should have a member type \c type -/** -* Implements Simd::Rebind -* -* This specialization covers -* - Vector -> bool -*/ -template<class V> -struct RebindType<bool, V, std::enable_if_t<VcImpl::IsVector<V>::value && -!VcImpl::IsMask<V>::value>> -{ -using type = typename V::mask_type; -}; - -//! should have a member type \c type -/** -* Implements Simd::Rebind -* -* This specialization covers -* - Mask -> Scalar<Mask::Vector> -*/ -template<class M> -struct RebindType<Scalar<typename M::Vector>, M, -std::enable_if_t<VcImpl::IsMask<M>::value>> -{ -using type = typename M::Vector; -}; - -//! should have a member type \c type -/** -* Implements Simd::Rebind -* -* This specialization covers -* - Mask -> Vc-vectorizable type except bool, Scalar<Mask::Vector> -*/ -template<class S, class M> -struct RebindType<S, M, -std::enable_if_t< -VcImpl::IsMask<M>::value && -VcImpl::IsVectorizable<S>::value && -!std::is_same<S, Scalar<typename M::Vector> >::value -> > -{ -using type = Vc::SimdArray<S, Simd::lanes<M>()>; -}; - -//! should have a member type \c type -/** -* Implements Simd::Rebind -* -* This specialization covers -* - Vector -> Vc-vectorizable type except bool, Scalar<Vector> -*/ -template<class S, class V> -struct RebindType<S, V, -std::enable_if_t<VcImpl::IsVector<V>::value && -!VcImpl::IsMask<V>::value && -VcImpl::IsVectorizable<S>::value && -!std::is_same<S, Scalar<V> >::value> > -{ -using type = Vc::SimdArray<S, Simd::lanes<V>()>; -}; - -//! should have a member type \c type -/** -* Implements Simd::Rebind -* -* This specialization covers -* - Mask -> non-Vc-vectorizable type except bool -* - Vector -> non-Vc-vectorizable type except bool -*/ -template<class S, class V> -struct RebindType<S, V, -std::enable_if_t<VcImpl::IsVector<V>::value && -!VcImpl::IsVectorizable<S>::value && -!std::is_same<S, bool>::value && -!std::is_same<S, Scalar<V> >::value> > -{ -using type = LoopSIMD<S, Simd::lanes<V>()>; -}; - -//! should be derived from an Dune::index_constant -/** -* Implements Simd::lanes() -*/ -template<class V> -struct LaneCount<V, std::enable_if_t<VcImpl::IsVector<V>::value> > -: public index_constant<V::size()> -{ }; - -//! implements Simd::lane() -template<class V> -VcImpl::Proxy<V> lane(ADLTag<5, VcImpl::IsVector<V>::value>, -std::size_t l, V &v) -{ -return { l, v }; -} - -//! implements Simd::lane() -template<class V> -Scalar<V> lane(ADLTag<5, VcImpl::IsVector<V>::value>, -std::size_t l, const V &v) -{ -return v[l]; -} - -//! implements Simd::lane() -/* -* The hack with the SFINAE is necessary, because if I use just -* Scalar<V> as the return type, the compiler still errors out if V is -* an lvalue-reference T&. You'd think he'd notice that he can't -* instantiate this declaration for this template parameter, and would -* simply remove it from the overload set, but no... -*/ -template<class V, -class = std::enable_if_t<!std::is_reference<V>::value> > -Scalar<V> lane(ADLTag<5, VcImpl::IsVector<V>::value>, -std::size_t l, V &&v) -{ -return std::forward<V>(v)[l]; -} - -//! implements Simd::cond() -template<class V> -V cond(ADLTag<5, VcImpl::IsVector<V>::value && -!VcImpl::IsMask<V>::value>, -const Mask<V> &mask, const V &ifTrue, const V &ifFalse) -{ -return Vc::iif(mask, ifTrue, ifFalse); -} - -//! implements Simd::cond() -/* -* Kludge because iif seems to be unimplemented for masks -*/ -template<class V> -V cond(ADLTag<5, VcImpl::IsMask<V>::value>, -const V &mask, const V &ifTrue, const V &ifFalse) -{ -return (mask && ifTrue) || (!mask && ifFalse); -} - -//! implements binary Simd::max() -template<class V> -auto max(ADLTag<5, VcImpl::IsVector<V>::value && -!VcImpl::IsMask<V>::value>, -const V &v1, const V &v2) -{ -return Simd::cond(v1 < v2, v2, v1); -} - -//! implements binary Simd::max() -template<class M> -auto max(ADLTag<5, VcImpl::IsMask<M>::value>, -const M &m1, const M &m2) -{ -return m1 || m2; -} - -//! implements binary Simd::min() -template<class V> -auto min(ADLTag<5, VcImpl::IsVector<V>::value && -!VcImpl::IsMask<V>::value>, -const V &v1, const V &v2) -{ -return Simd::cond(v1 < v2, v1, v2); -} - -//! implements binary Simd::min() -template<class M> -auto min(ADLTag<5, VcImpl::IsMask<M>::value>, -const M &m1, const M &m2) -{ -return m1 && m2; -} - -//! implements Simd::anyTrue() -template<class M> -bool anyTrue (ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) -{ -return Vc::any_of(mask); -} - -//! implements Simd::allTrue() -template<class M> -bool allTrue (ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) -{ -return Vc::all_of(mask); -} - -// nothing like anyFalse() in Vc, so let defaults.hh handle it - -//! implements Simd::allFalse() -template<class M> -bool allFalse(ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) -{ -return Vc::none_of(mask); -} - -//! implements Simd::maxValue() -template<class V> -auto max(ADLTag<5, VcImpl::IsVector<V>::value && -!VcImpl::IsMask<V>::value>, -const V &v) -{ -return v.max(); -} - -//! implements Simd::maxValue() -template<class M> -bool max(ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) -{ -return Vc::any_of(mask); -} - -//! implements Simd::minValue() -template<class V> -auto min(ADLTag<5, VcImpl::IsVector<V>::value && -!VcImpl::IsMask<V>::value>, -const V &v) -{ -return v.min(); -} - -//! implements Simd::minValue() -template<class M> -bool min(ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) -{ -return !Vc::any_of(!mask); -} - -//! implements Simd::maskAnd() -template<class S1, class V2> -auto maskAnd(ADLTag<5, std::is_same<Mask<S1>, bool>::value && -VcImpl::IsVector<V2>::value>, -const S1 &s1, const V2 &v2) -{ -return Simd::Mask<V2>(Simd::mask(s1)) && Simd::mask(v2); -} - -//! implements Simd::maskAnd() -template<class V1, class S2> -auto maskAnd(ADLTag<5, VcImpl::IsVector<V1>::value && -std::is_same<Mask<S2>, bool>::value>, -const V1 &v1, const S2 &s2) -{ -return Simd::mask(v1) && Simd::Mask<V1>(Simd::mask(s2)); -} - -//! implements Simd::maskOr() -template<class S1, class V2> -auto maskOr(ADLTag<5, std::is_same<Mask<S1>, bool>::value && -VcImpl::IsVector<V2>::value>, -const S1 &s1, const V2 &v2) -{ -return Simd::Mask<V2>(Simd::mask(s1)) || Simd::mask(v2); -} - -//! implements Simd::maskOr() -template<class V1, class S2> -auto maskOr(ADLTag<5, VcImpl::IsVector<V1>::value && -std::is_same<Mask<S2>, bool>::value>, -const V1 &v1, const S2 &s2) -{ -return Simd::mask(v1) || Simd::Mask<V1>(Simd::mask(s2)); -} - -//! @} group SIMDVc - -} // namespace Overloads - -} // namespace Simd - -/* -* Specialize IsNumber for Vc::SimdArray and Vc::Vector to be able to use -* it as a scalar in DenseMatrix etc. -*/ -template <typename T, std::size_t N> -struct IsNumber<Vc::SimdArray<T, N>> -: public std::integral_constant<bool, IsNumber<T>::value> { -}; - -template <typename T, typename Abi> -struct IsNumber<Vc::Vector<T, Abi>> -: public std::integral_constant<bool, IsNumber<T>::value> { -}; - -//! Specialization of AutonomousValue for Vc proxies -template<class V> -struct AutonomousValueType<Simd::VcImpl::Proxy<V> > : -AutonomousValueType<typename Simd::VcImpl::Proxy<V>::value_type> {}; + }; + + } // namespace VcImpl + + namespace Overloads { + + /** @name Specialized classes and overloaded functions + * @ingroup SIMDVc + * @{ + */ + + //! should have a member type \c type + /** + * Implements Simd::Scalar + */ + template<class V> + struct ScalarType<V, std::enable_if_t<VcImpl::IsVector<V>::value> > + { + using type = typename V::value_type; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> bool + * - Vector -> Scalar<Vector> + */ + template<class V> + struct RebindType<Simd::Scalar<V>, V, + std::enable_if_t<VcImpl::IsVector<V>::value> > + { + using type = V; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Vector -> bool + */ + template<class V> + struct RebindType<bool, V, std::enable_if_t<VcImpl::IsVector<V>::value && + !VcImpl::IsMask<V>::value>> + { + using type = typename V::mask_type; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> Scalar<Mask::Vector> + */ + template<class M> + struct RebindType<Scalar<typename M::Vector>, M, + std::enable_if_t<VcImpl::IsMask<M>::value>> + { + using type = typename M::Vector; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> Vc-vectorizable type except bool, Scalar<Mask::Vector> + */ + template<class S, class M> + struct RebindType<S, M, + std::enable_if_t< + VcImpl::IsMask<M>::value && + VcImpl::IsVectorizable<S>::value && + !std::is_same<S, Scalar<typename M::Vector> >::value + > > + { + using type = Vc::SimdArray<S, Simd::lanes<M>()>; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Vector -> Vc-vectorizable type except bool, Scalar<Vector> + */ + template<class S, class V> + struct RebindType<S, V, + std::enable_if_t<VcImpl::IsVector<V>::value && + !VcImpl::IsMask<V>::value && + VcImpl::IsVectorizable<S>::value && + !std::is_same<S, Scalar<V> >::value> > + { + using type = Vc::SimdArray<S, Simd::lanes<V>()>; + }; + + //! should have a member type \c type + /** + * Implements Simd::Rebind + * + * This specialization covers + * - Mask -> non-Vc-vectorizable type except bool + * - Vector -> non-Vc-vectorizable type except bool + */ + template<class S, class V> + struct RebindType<S, V, + std::enable_if_t<VcImpl::IsVector<V>::value && + !VcImpl::IsVectorizable<S>::value && + !std::is_same<S, bool>::value && + !std::is_same<S, Scalar<V> >::value> > + { + using type = LoopSIMD<S, Simd::lanes<V>()>; + }; + + //! should be derived from an Dune::index_constant + /** + * Implements Simd::lanes() + */ + template<class V> + struct LaneCount<V, std::enable_if_t<VcImpl::IsVector<V>::value> > + : public index_constant<V::size()> + { }; + + //! implements Simd::lane() + template<class V> + VcImpl::Proxy<V> lane(ADLTag<5, VcImpl::IsVector<V>::value>, + std::size_t l, V &v) + { + return { l, v }; + } + + //! implements Simd::lane() + template<class V> + Scalar<V> lane(ADLTag<5, VcImpl::IsVector<V>::value>, + std::size_t l, const V &v) + { + return v[l]; + } + + //! implements Simd::lane() + /* + * The hack with the SFINAE is necessary, because if I use just + * Scalar<V> as the return type, the compiler still errors out if V is + * an lvalue-reference T&. You'd think he'd notice that he can't + * instantiate this declaration for this template parameter, and would + * simply remove it from the overload set, but no... + */ + template<class V, + class = std::enable_if_t<!std::is_reference<V>::value> > + Scalar<V> lane(ADLTag<5, VcImpl::IsVector<V>::value>, + std::size_t l, V &&v) + { + return std::forward<V>(v)[l]; + } + + //! implements Simd::cond() + template<class V> + V cond(ADLTag<5, VcImpl::IsVector<V>::value && + !VcImpl::IsMask<V>::value>, + const Mask<V> &mask, const V &ifTrue, const V &ifFalse) + { + return Vc::iif(mask, ifTrue, ifFalse); + } + + //! implements Simd::cond() + /* + * Kludge because iif seems to be unimplemented for masks + */ + template<class V> + V cond(ADLTag<5, VcImpl::IsMask<V>::value>, + const V &mask, const V &ifTrue, const V &ifFalse) + { + return (mask && ifTrue) || (!mask && ifFalse); + } + + //! implements binary Simd::max() + template<class V> + auto max(ADLTag<5, VcImpl::IsVector<V>::value && + !VcImpl::IsMask<V>::value>, + const V &v1, const V &v2) + { + return Simd::cond(v1 < v2, v2, v1); + } + + //! implements binary Simd::max() + template<class M> + auto max(ADLTag<5, VcImpl::IsMask<M>::value>, + const M &m1, const M &m2) + { + return m1 || m2; + } + + //! implements binary Simd::min() + template<class V> + auto min(ADLTag<5, VcImpl::IsVector<V>::value && + !VcImpl::IsMask<V>::value>, + const V &v1, const V &v2) + { + return Simd::cond(v1 < v2, v1, v2); + } + + //! implements binary Simd::min() + template<class M> + auto min(ADLTag<5, VcImpl::IsMask<M>::value>, + const M &m1, const M &m2) + { + return m1 && m2; + } + + //! implements Simd::anyTrue() + template<class M> + bool anyTrue (ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) + { + return Vc::any_of(mask); + } + + //! implements Simd::allTrue() + template<class M> + bool allTrue (ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) + { + return Vc::all_of(mask); + } + + // nothing like anyFalse() in Vc, so let defaults.hh handle it + + //! implements Simd::allFalse() + template<class M> + bool allFalse(ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) + { + return Vc::none_of(mask); + } + + //! implements Simd::maxValue() + template<class V> + auto max(ADLTag<5, VcImpl::IsVector<V>::value && + !VcImpl::IsMask<V>::value>, + const V &v) + { + return v.max(); + } + + //! implements Simd::maxValue() + template<class M> + bool max(ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) + { + return Vc::any_of(mask); + } + + //! implements Simd::minValue() + template<class V> + auto min(ADLTag<5, VcImpl::IsVector<V>::value && + !VcImpl::IsMask<V>::value>, + const V &v) + { + return v.min(); + } + + //! implements Simd::minValue() + template<class M> + bool min(ADLTag<5, VcImpl::IsMask<M>::value>, const M &mask) + { + return !Vc::any_of(!mask); + } + + //! implements Simd::maskAnd() + template<class S1, class V2> + auto maskAnd(ADLTag<5, std::is_same<Mask<S1>, bool>::value && + VcImpl::IsVector<V2>::value>, + const S1 &s1, const V2 &v2) + { + return Simd::Mask<V2>(Simd::mask(s1)) && Simd::mask(v2); + } + + //! implements Simd::maskAnd() + template<class V1, class S2> + auto maskAnd(ADLTag<5, VcImpl::IsVector<V1>::value && + std::is_same<Mask<S2>, bool>::value>, + const V1 &v1, const S2 &s2) + { + return Simd::mask(v1) && Simd::Mask<V1>(Simd::mask(s2)); + } + + //! implements Simd::maskOr() + template<class S1, class V2> + auto maskOr(ADLTag<5, std::is_same<Mask<S1>, bool>::value && + VcImpl::IsVector<V2>::value>, + const S1 &s1, const V2 &v2) + { + return Simd::Mask<V2>(Simd::mask(s1)) || Simd::mask(v2); + } + + //! implements Simd::maskOr() + template<class V1, class S2> + auto maskOr(ADLTag<5, VcImpl::IsVector<V1>::value && + std::is_same<Mask<S2>, bool>::value>, + const V1 &v1, const S2 &s2) + { + return Simd::mask(v1) || Simd::Mask<V1>(Simd::mask(s2)); + } + + //! @} group SIMDVc + + } // namespace Overloads + + } // namespace Simd + + /* + * Specialize IsNumber for Vc::SimdArray and Vc::Vector to be able to use + * it as a scalar in DenseMatrix etc. + */ + template <typename T, std::size_t N> + struct IsNumber<Vc::SimdArray<T, N>> + : public std::integral_constant<bool, IsNumber<T>::value> { + }; + + template <typename T, typename Abi> + struct IsNumber<Vc::Vector<T, Abi>> + : public std::integral_constant<bool, IsNumber<T>::value> { + }; + + //! Specialization of AutonomousValue for Vc proxies + template<class V> + struct AutonomousValueType<Simd::VcImpl::Proxy<V> > : + AutonomousValueType<typename Simd::VcImpl::Proxy<V>::value_type> {}; } // namespace Dune diff --git a/dune/common/singleton.hh b/dune/common/singleton.hh index 812982ea0e6d15b6b91c750de23181131418d68e..1df68e7ccd2035e0ada67d8c1758e0d4601020ba 100644 --- a/dune/common/singleton.hh +++ b/dune/common/singleton.hh @@ -7,71 +7,71 @@ #include <dune/common/visibility.hh> /** -* @file -* @brief Useful wrapper for creating singletons. -* -* Inspired by the article -* <a href="http://www.codeguru.com/cpp/cpp/cpp_mfc/singletons/article.php/c755/">CodeGuru: A Leak-Free Singleton class</a> -*/ + * @file + * @brief Useful wrapper for creating singletons. + * + * Inspired by the article + * <a href="http://www.codeguru.com/cpp/cpp/cpp_mfc/singletons/article.php/c755/">CodeGuru: A Leak-Free Singleton class</a> + */ namespace Dune { -/** -* @brief An adapter to turn a class into a singleton. -* -* The class represented by the template parameter T must -* have a parameterless constructor. -* -* Class T can be publicly derived from Singleton<T>: -* -* \code -* #include<dune/common/singleton.hh> -* class Foo : public Dune::Singleton<Foo> -* { -* public: -* Foo() -* { -* bytes = new char[1000]; -* } -* -* ~Foo() -* { -* delete[] bytes; -* } -* private: -* char* bytes; -* }; -* \endcode -* -* Or one can construct a Singleton of an existing class. Say Foo1 is a class -* with parameterless constructor then -* \code -* typedef Dune::Singleton<Foo1> FooSingleton; -* Foo1 instance& = FooSingleton::instance(); -* \endcode -* Creates a singleton of that class and accesses its instance. -*/ -template<class T> -class Singleton -{ -protected: -/* @brief Protected constructor. */ -Singleton() = default; + /** + * @brief An adapter to turn a class into a singleton. + * + * The class represented by the template parameter T must + * have a parameterless constructor. + * + * Class T can be publicly derived from Singleton<T>: + * + * \code + * #include<dune/common/singleton.hh> + * class Foo : public Dune::Singleton<Foo> + * { + * public: + * Foo() + * { + * bytes = new char[1000]; + * } + * + * ~Foo() + * { + * delete[] bytes; + * } + * private: + * char* bytes; + * }; + * \endcode + * + * Or one can construct a Singleton of an existing class. Say Foo1 is a class + * with parameterless constructor then + * \code + * typedef Dune::Singleton<Foo1> FooSingleton; + * Foo1 instance& = FooSingleton::instance(); + * \endcode + * Creates a singleton of that class and accesses its instance. + */ + template<class T> + class Singleton + { + protected: + /* @brief Protected constructor. */ + Singleton() = default; -public: + public: -Singleton(const Singleton&) = delete; -void operator=(const Singleton&) = delete; + Singleton(const Singleton&) = delete; + void operator=(const Singleton&) = delete; -/** -* @brief Get the instance of the singleton. -* @return The instance of the singleton. -*/ -DUNE_EXPORT static T& instance() -{ -static T instance_; -return instance_; -} -}; + /** + * @brief Get the instance of the singleton. + * @return The instance of the singleton. + */ + DUNE_EXPORT static T& instance() + { + static T instance_; + return instance_; + } + }; } // namespace Dune diff --git a/dune/common/sllist.hh b/dune/common/sllist.hh index 8ee9e4148e27c1387d025772c7ab1a2c52beaeaf..61665e16a39df18c7cf2307f54e0990fd7f1bf54 100644 --- a/dune/common/sllist.hh +++ b/dune/common/sllist.hh @@ -11,798 +11,798 @@ namespace Dune { -/** -* @addtogroup Common -* -* @{ -*/ -/** -* @file -* \brief Implements a singly linked list together with -* the necessary iterators. -* @author Markus Blatt -*/ -template<typename T, class A> -class SLListIterator; - -template<typename T, class A> -class SLListConstIterator; - -template<typename T, class A> -class SLListModifyIterator; - -/** -* @brief A single linked list. -* -* The list is capable of insertions at the front and at -* the end and of removing elements at the front. Those -* operations require constant time. -*/ -template<typename T, class A=std::allocator<T> > -class SLList -{ -struct Element; -friend class SLListIterator<T,A>; -friend class SLListConstIterator<T,A>; - -public: - -/** -* @brief The size type. -*/ -typedef typename A::size_type size_type; - -/** -* @brief The type we store. -*/ -typedef T MemberType; - -/** -* @brief The allocator to use. -*/ -using Allocator = typename std::allocator_traits<A>::template rebind_alloc<Element>; - -/** -* @brief The mutable iterator of the list. -*/ -typedef SLListIterator<T,A> iterator; - -/** -* @brief The constant iterator of the list. -*/ -typedef SLListConstIterator<T,A> const_iterator; - -/** -* @brief Constructor. -*/ -SLList(); - -/** -* @brief Copy constructor with type conversion. -*/ -template<typename T1, typename A1> -SLList(const SLList<T1,A1>& other); - -/** -* @brief Copy constructor. -*/ -SLList(const SLList<T,A>& other); - -/** -* @brief Destructor. -* -* Deallocates all elements in the list. -*/ -~SLList(); - -/** -* @brief The type of the iterator capable of deletion -* and insertion. -*/ -typedef SLListModifyIterator<T,A> ModifyIterator; - -/** -* @brief Assignment operator. -*/ -SLList<T,A>& operator=(const SLList<T,A>& other); - - -/** -* @brief Add a new entry to the end of the list. -* @param item The item to add. -*/ -inline void push_back(const MemberType& item); - -/** -* @brief Add a new entry to the beginning of the list. -* @param item The item to add. -*/ -inline void push_front(const MemberType& item); - -/** -* @brief Remove the first item in the list. -*/ -inline void pop_front(); - -/** @brief Remove all elements from the list. */ -inline void clear(); - -/** -* @brief Get an iterator pointing to the first -* element in the list. -* -* @return An iterator pointing to the first -* element or the end if the list is empty. -*/ -inline iterator begin(); - -/** -* @brief Get an iterator pointing to the first -* element in the list. -* -* @return An iterator pointing to the first -* element or the end if the list is empty. -*/ -inline const_iterator begin() const; - -/** -* @brief Get an iterator capable of deleting and -* inserting elements. -* -* @return Modifying iterator positioned at the beginning -* of the list. -*/ -inline ModifyIterator beginModify(); - -/** -* @brief Get an iterator capable of deleting and -* inserting elements. -* -* @return Modifying iterator positioned after the end -* of the list. -*/ -inline ModifyIterator endModify(); - -/** -* @brief Get an iterator pointing to the -* end of the list. -* -* @return An iterator pointing to the end. -*/ -inline iterator end(); - -/** -* @brief Get an iterator pointing to the -* end of the list. -* -* @return An iterator pointing to the end. -*/ -inline const_iterator end() const; - -/** -* @brief Check whether the list is empty. -* -* @return True if the list is empty; -*/ -inline bool empty() const; - -/** -* @brief Get the number of elements the list -* contains. -*/ -inline int size() const; - -bool operator==(const SLList& sl) const; - - -bool operator!=(const SLList& sl) const; - -private: -/** \todo Please doc me! */ -struct Element -{ -/** -* @brief The next element in the list. -*/ -Element* next_; -/** -* @brief The element we hold. -*/ -MemberType item_; - -Element(const MemberType& item, Element* next_=0); - -Element(); - -~Element(); -}; - -/** -* @brief Delete the next element in the list. -* @param current Element whose next element should be deleted. -*/ -void deleteNext(Element* current); - -/** -* @brief Copy the elements from another list. -* @param other The other list. -*/ -void copyElements(const SLList<T,A>& other); - -/** -* @brief Delete the next element in the list. -* -* If the template parameter watchForTail is true, it is checked whether -* the deleted element is the tail and therefore the tail must be updated. -* @param current Element whose next element should be deleted. -*/ -template<bool watchForTail> -void deleteNext(Element* current); -/** -* @brief Insert an element after another one in the list. -* @param current The element after which we insert. -* @param item The item to insert. -*/ -void insertAfter(Element* current, const T& item); - -/** @brief Pseudo element before the first entry. */ -Element beforeHead_; - -/** -* @brief Pointer to he last element in the list. -* -* If list is empty this will point to beforeHead_ -*/ -Element* tail_; - -/** @brief The allocator we use. */ -Allocator allocator_; - -/** brief The number of elements the list holds. */ -int size_; -}; - -/** -* @brief A mutable iterator for the SLList. -*/ -template<typename T, class A> -class SLListIterator : public Dune::ForwardIteratorFacade<SLListIterator<T,A>, T, T&, std::size_t> -{ -friend class SLListConstIterator<T,A>; -friend class SLListModifyIterator<T,A>; -friend class SLList<T,A>; - -public: -inline SLListIterator(typename SLList<T,A>::Element* item, -SLList<T,A>* sllist) -: current_(item), list_(sllist) -{} - -inline SLListIterator() -: current_(0), list_(0) -{} - -inline SLListIterator(const SLListModifyIterator<T,A>& other) -: current_(other.iterator_.current_), list_(other.iterator_.list_) -{} - -/** -* @brief Dereferencing function for the iterator facade. -* @return A reference to the element at the current position. -*/ -inline T& dereference() const -{ -return current_->item_; -} - -/** -* @brief Equality test for the iterator facade. -* @param other The other iterator to check. -* @return true If the other iterator is at the same position. -*/ -inline bool equals(const SLListConstIterator<T,A>& other) const -{ -return current_==other.current_; -} - -/** -* @brief Equality test for the iterator facade. -* @param other The other iterator to check. -* @return true If the other iterator is at the same position. -*/ -inline bool equals(const SLListIterator<T,A>& other) const -{ -return current_==other.current_; -} - -/** -* @brief Equality test for the iterator facade. -* @param other The other iterator to check. -* @return true If the other iterator is at the same position. -*/ -inline bool equals(const SLListModifyIterator<T,A>& other) const -{ -return current_==other.iterator_.current_; -} - -/** -* @brief Increment function for the iterator facade. -*/ -inline void increment() -{ -current_ = current_->next_; -} - -/** -* @brief Insert an element in the underlying list after -* the current position. -* @param v The value to insert. -*/ -inline void insertAfter(const T& v) const -{ -assert(list_ ); -list_->insertAfter(current_, v); -} - -/** -* @brief Delete the entry after the current position. -* -* @warning This will invalidate all iterators positioned at the delete position! Use with care! -*/ -inline void deleteNext() const -{ -assert(list_); -list_->deleteNext(current_); -} - -private: -/** @brief The current element. */ -typename SLList<T,A>::Element* current_; -/** @brief The list we iterate over. */ -SLList<T,A>* list_; -}; - -/** -* @brief A constant iterator for the SLList. -*/ -template<class T, class A> -class SLListConstIterator : public Dune::ForwardIteratorFacade<SLListConstIterator<T,A>, const T, const T&, std::size_t> -{ -friend class SLListIterator<T,A>; -friend class SLList<T,A>; - -public: -inline SLListConstIterator() -: current_(0) -{} - -inline SLListConstIterator(typename SLList<T,A>::Element* item) -: current_(item) -{} - -inline SLListConstIterator(const SLListIterator<T,A>& other) -: current_(other.current_) -{} - -inline SLListConstIterator(const SLListConstIterator<T,A>& other) -: current_(other.current_) -{} - -inline SLListConstIterator(const SLListModifyIterator<T,A>& other) -: current_(other.iterator_.current_) -{} - -/** -* @brief Dereferencing function for the facade. -* @return A reference to the element at the current position. -*/ -inline const T& dereference() const -{ -return current_->item_; -} - -/** -* @brief Equality test for the iterator facade. -* @param other The other iterator to check. -* @return true If the other iterator is at the same position. -*/ -inline bool equals(const SLListConstIterator<T,A>& other) const -{ -return current_==other.current_; -} - -/** -* @brief Increment function for the iterator facade. -*/ -inline void increment() -{ -current_ = current_->next_; -} - -private: -/** @brief The current element. */ -typename SLList<T,A>::Element* current_; -}; - -/** -* @brief A mutable iterator for the SLList. -*/ -template<typename T, class A> -class SLListModifyIterator : public Dune::ForwardIteratorFacade<SLListModifyIterator<T,A>, T, T&, std::size_t> -{ -friend class SLListConstIterator<T,A>; -friend class SLListIterator<T,A>; -public: -inline SLListModifyIterator(SLListIterator<T,A> beforeIterator, -SLListIterator<T,A> _iterator) -: beforeIterator_(beforeIterator), iterator_(_iterator) -{} - -inline SLListModifyIterator(const SLListModifyIterator<T,A>& other) -: beforeIterator_(other.beforeIterator_), iterator_(other.iterator_) -{} - -inline SLListModifyIterator() -: beforeIterator_(), iterator_() -{} - -/** -* @brief Dereferencing function for the iterator facade. -* @return A reference to the element at the current position. -*/ -inline T& dereference() const -{ -return *iterator_; -} - -/** -* @brief Test whether another iterator is equal. -* @return true if the other iterator is at the same position as -* this one. -*/ -inline bool equals(const SLListConstIterator<T,A>& other) const -{ -return iterator_== other; -} - - -/** -* @brief Test whether another iterator is equal. -* @return true if the other iterator is at the same position as -* this one. -*/ -inline bool equals(const SLListIterator<T,A>& other) const -{ -return iterator_== other; -} - - -/** -* @brief Test whether another iterator is equal. -* @return true if the other iterator is at the same position as -* this one. -*/ -inline bool equals(const SLListModifyIterator<T,A>& other) const -{ -return iterator_== other.iterator_; -} - -/** -* @brief Increment function for the iterator facade. -*/ -inline void increment() -{ -++iterator_; -++beforeIterator_; -} - -/** -* @brief Insert an element at the current position. -* -* Starting from the element at the current position all -* elements will be shifted by one position to the back. -* The iterator will point to the same element as before -* after the insertion, i.e the number of increments to -* reach the same position from a begin iterator increases -* by one. -* This means the inserted element is the one before the one -* the iterator points to. -* @param v The value to insert. -*/ -inline void insert(const T& v) -{ -beforeIterator_.insertAfter(v); -++beforeIterator_; -} - -/** -* @brief Delete the entry at the current position. -* -* The iterator will be positioned at the next position after the -* deletion -* @warning This will invalidate all iterators positioned at the delete position! Use with care! -*/ -inline void remove() -{ -++iterator_; -beforeIterator_.deleteNext(); -} - -private: -/** @brief Iterator positioned at the position before the current. */ -SLListIterator<T,A> beforeIterator_; -/** @brief Iterator positioned at the current position. */ -SLListIterator<T,A> iterator_; -}; - -template<typename T, typename A> -std::ostream& operator<<(std::ostream& os, const SLList<T,A>& sllist) -{ -typedef typename SLList<T,A>::const_iterator Iterator; -Iterator end = sllist.end(); -Iterator current= sllist.begin(); - -os << "{ "; - -if(current!=end) { -os<<*current<<" ("<<static_cast<const void*>(&(*current))<<")"; -++current; - -for(; current != end; ++current) -os<<", "<<*current<<" ("<<static_cast<const void*>(&(*current))<<")"; -} -os<<"} "; -return os; -} - -template<typename T, class A> -SLList<T,A>::Element::Element(const MemberType& item, Element* next) -: next_(next), item_(item) -{} - -template<typename T, class A> -SLList<T,A>::Element::Element() -: next_(0), item_() -{} - -template<typename T, class A> -SLList<T,A>::Element::~Element() -{ -next_=0; -} - -template<typename T, class A> -SLList<T,A>::SLList() -: beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) -{ -beforeHead_.next_=0; -assert(&beforeHead_==tail_); -assert(tail_->next_==0); -} - -template<typename T, class A> -SLList<T,A>::SLList(const SLList<T,A>& other) -: beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) -{ -copyElements(other); -} - -template<typename T, class A> -template<typename T1, class A1> -SLList<T,A>::SLList(const SLList<T1,A1>& other) -: beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) -{ -copyElements(other); -} - -template<typename T, typename A> -void SLList<T,A>::copyElements(const SLList<T,A>& other) -{ -assert(tail_==&beforeHead_); -assert(size_==0); -typedef typename SLList<T,A>::const_iterator Iterator; -Iterator iend = other.end(); -for(Iterator element=other.begin(); element != iend; ++element) -push_back(*element); - -assert(other.size()==size()); -} - -template<typename T, class A> -SLList<T,A>::~SLList() -{ -clear(); -} - -template<typename T, class A> -bool SLList<T,A>::operator==(const SLList& other) const -{ -if(size()!=other.size()) -return false; -for(const_iterator iter=begin(), oiter=other.begin(); -iter != end(); ++iter, ++oiter) -if(*iter!=*oiter) -return false; -return true; -} - -template<typename T, class A> -bool SLList<T,A>::operator!=(const SLList& other) const -{ -if(size()==other.size()) { -for(const_iterator iter=begin(), oiter=other.begin(); -iter != end(); ++iter, ++oiter) -if(*iter!=*oiter) -return true; -return false; -}else -return true; -} -template<typename T, class A> -SLList<T,A>& SLList<T,A>::operator=(const SLList<T,A>& other) -{ -clear(); -copyElements(other); -return *this; -} - -template<typename T, class A> -inline void SLList<T,A>::push_back(const MemberType& item) -{ -assert(size_>0 || tail_==&beforeHead_); -tail_->next_ = allocator_.allocate(1); -assert(size_>0 || tail_==&beforeHead_); -tail_ = tail_->next_; -::new (static_cast<void*>(&(tail_->item_)))T(item); -tail_->next_=0; -assert(tail_->next_==0); -++size_; -} - -template<typename T, class A> -inline void SLList<T,A>::insertAfter(Element* current, const T& item) -{ -assert(current); + /** + * @addtogroup Common + * + * @{ + */ + /** + * @file + * \brief Implements a singly linked list together with + * the necessary iterators. + * @author Markus Blatt + */ + template<typename T, class A> + class SLListIterator; + + template<typename T, class A> + class SLListConstIterator; + + template<typename T, class A> + class SLListModifyIterator; + + /** + * @brief A single linked list. + * + * The list is capable of insertions at the front and at + * the end and of removing elements at the front. Those + * operations require constant time. + */ + template<typename T, class A=std::allocator<T> > + class SLList + { + struct Element; + friend class SLListIterator<T,A>; + friend class SLListConstIterator<T,A>; + + public: + + /** + * @brief The size type. + */ + typedef typename A::size_type size_type; + + /** + * @brief The type we store. + */ + typedef T MemberType; + + /** + * @brief The allocator to use. + */ + using Allocator = typename std::allocator_traits<A>::template rebind_alloc<Element>; + + /** + * @brief The mutable iterator of the list. + */ + typedef SLListIterator<T,A> iterator; + + /** + * @brief The constant iterator of the list. + */ + typedef SLListConstIterator<T,A> const_iterator; + + /** + * @brief Constructor. + */ + SLList(); + + /** + * @brief Copy constructor with type conversion. + */ + template<typename T1, typename A1> + SLList(const SLList<T1,A1>& other); + + /** + * @brief Copy constructor. + */ + SLList(const SLList<T,A>& other); + + /** + * @brief Destructor. + * + * Deallocates all elements in the list. + */ + ~SLList(); + + /** + * @brief The type of the iterator capable of deletion + * and insertion. + */ + typedef SLListModifyIterator<T,A> ModifyIterator; + + /** + * @brief Assignment operator. + */ + SLList<T,A>& operator=(const SLList<T,A>& other); + + + /** + * @brief Add a new entry to the end of the list. + * @param item The item to add. + */ + inline void push_back(const MemberType& item); + + /** + * @brief Add a new entry to the beginning of the list. + * @param item The item to add. + */ + inline void push_front(const MemberType& item); + + /** + * @brief Remove the first item in the list. + */ + inline void pop_front(); + + /** @brief Remove all elements from the list. */ + inline void clear(); + + /** + * @brief Get an iterator pointing to the first + * element in the list. + * + * @return An iterator pointing to the first + * element or the end if the list is empty. + */ + inline iterator begin(); + + /** + * @brief Get an iterator pointing to the first + * element in the list. + * + * @return An iterator pointing to the first + * element or the end if the list is empty. + */ + inline const_iterator begin() const; + + /** + * @brief Get an iterator capable of deleting and + * inserting elements. + * + * @return Modifying iterator positioned at the beginning + * of the list. + */ + inline ModifyIterator beginModify(); + + /** + * @brief Get an iterator capable of deleting and + * inserting elements. + * + * @return Modifying iterator positioned after the end + * of the list. + */ + inline ModifyIterator endModify(); + + /** + * @brief Get an iterator pointing to the + * end of the list. + * + * @return An iterator pointing to the end. + */ + inline iterator end(); + + /** + * @brief Get an iterator pointing to the + * end of the list. + * + * @return An iterator pointing to the end. + */ + inline const_iterator end() const; + + /** + * @brief Check whether the list is empty. + * + * @return True if the list is empty; + */ + inline bool empty() const; + + /** + * @brief Get the number of elements the list + * contains. + */ + inline int size() const; + + bool operator==(const SLList& sl) const; + + + bool operator!=(const SLList& sl) const; + + private: + /** \todo Please doc me! */ + struct Element + { + /** + * @brief The next element in the list. + */ + Element* next_; + /** + * @brief The element we hold. + */ + MemberType item_; + + Element(const MemberType& item, Element* next_=0); + + Element(); + + ~Element(); + }; + + /** + * @brief Delete the next element in the list. + * @param current Element whose next element should be deleted. + */ + void deleteNext(Element* current); + + /** + * @brief Copy the elements from another list. + * @param other The other list. + */ + void copyElements(const SLList<T,A>& other); + + /** + * @brief Delete the next element in the list. + * + * If the template parameter watchForTail is true, it is checked whether + * the deleted element is the tail and therefore the tail must be updated. + * @param current Element whose next element should be deleted. + */ + template<bool watchForTail> + void deleteNext(Element* current); + /** + * @brief Insert an element after another one in the list. + * @param current The element after which we insert. + * @param item The item to insert. + */ + void insertAfter(Element* current, const T& item); + + /** @brief Pseudo element before the first entry. */ + Element beforeHead_; + + /** + * @brief Pointer to he last element in the list. + * + * If list is empty this will point to beforeHead_ + */ + Element* tail_; + + /** @brief The allocator we use. */ + Allocator allocator_; + + /** brief The number of elements the list holds. */ + int size_; + }; + + /** + * @brief A mutable iterator for the SLList. + */ + template<typename T, class A> + class SLListIterator : public Dune::ForwardIteratorFacade<SLListIterator<T,A>, T, T&, std::size_t> + { + friend class SLListConstIterator<T,A>; + friend class SLListModifyIterator<T,A>; + friend class SLList<T,A>; + + public: + inline SLListIterator(typename SLList<T,A>::Element* item, + SLList<T,A>* sllist) + : current_(item), list_(sllist) + {} + + inline SLListIterator() + : current_(0), list_(0) + {} + + inline SLListIterator(const SLListModifyIterator<T,A>& other) + : current_(other.iterator_.current_), list_(other.iterator_.list_) + {} + + /** + * @brief Dereferencing function for the iterator facade. + * @return A reference to the element at the current position. + */ + inline T& dereference() const + { + return current_->item_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListConstIterator<T,A>& other) const + { + return current_==other.current_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListIterator<T,A>& other) const + { + return current_==other.current_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListModifyIterator<T,A>& other) const + { + return current_==other.iterator_.current_; + } + + /** + * @brief Increment function for the iterator facade. + */ + inline void increment() + { + current_ = current_->next_; + } + + /** + * @brief Insert an element in the underlying list after + * the current position. + * @param v The value to insert. + */ + inline void insertAfter(const T& v) const + { + assert(list_ ); + list_->insertAfter(current_, v); + } + + /** + * @brief Delete the entry after the current position. + * + * @warning This will invalidate all iterators positioned at the delete position! Use with care! + */ + inline void deleteNext() const + { + assert(list_); + list_->deleteNext(current_); + } + + private: + /** @brief The current element. */ + typename SLList<T,A>::Element* current_; + /** @brief The list we iterate over. */ + SLList<T,A>* list_; + }; + + /** + * @brief A constant iterator for the SLList. + */ + template<class T, class A> + class SLListConstIterator : public Dune::ForwardIteratorFacade<SLListConstIterator<T,A>, const T, const T&, std::size_t> + { + friend class SLListIterator<T,A>; + friend class SLList<T,A>; + + public: + inline SLListConstIterator() + : current_(0) + {} + + inline SLListConstIterator(typename SLList<T,A>::Element* item) + : current_(item) + {} + + inline SLListConstIterator(const SLListIterator<T,A>& other) + : current_(other.current_) + {} + + inline SLListConstIterator(const SLListConstIterator<T,A>& other) + : current_(other.current_) + {} + + inline SLListConstIterator(const SLListModifyIterator<T,A>& other) + : current_(other.iterator_.current_) + {} + + /** + * @brief Dereferencing function for the facade. + * @return A reference to the element at the current position. + */ + inline const T& dereference() const + { + return current_->item_; + } + + /** + * @brief Equality test for the iterator facade. + * @param other The other iterator to check. + * @return true If the other iterator is at the same position. + */ + inline bool equals(const SLListConstIterator<T,A>& other) const + { + return current_==other.current_; + } + + /** + * @brief Increment function for the iterator facade. + */ + inline void increment() + { + current_ = current_->next_; + } + + private: + /** @brief The current element. */ + typename SLList<T,A>::Element* current_; + }; + + /** + * @brief A mutable iterator for the SLList. + */ + template<typename T, class A> + class SLListModifyIterator : public Dune::ForwardIteratorFacade<SLListModifyIterator<T,A>, T, T&, std::size_t> + { + friend class SLListConstIterator<T,A>; + friend class SLListIterator<T,A>; + public: + inline SLListModifyIterator(SLListIterator<T,A> beforeIterator, + SLListIterator<T,A> _iterator) + : beforeIterator_(beforeIterator), iterator_(_iterator) + {} + + inline SLListModifyIterator(const SLListModifyIterator<T,A>& other) + : beforeIterator_(other.beforeIterator_), iterator_(other.iterator_) + {} + + inline SLListModifyIterator() + : beforeIterator_(), iterator_() + {} + + /** + * @brief Dereferencing function for the iterator facade. + * @return A reference to the element at the current position. + */ + inline T& dereference() const + { + return *iterator_; + } + + /** + * @brief Test whether another iterator is equal. + * @return true if the other iterator is at the same position as + * this one. + */ + inline bool equals(const SLListConstIterator<T,A>& other) const + { + return iterator_== other; + } + + + /** + * @brief Test whether another iterator is equal. + * @return true if the other iterator is at the same position as + * this one. + */ + inline bool equals(const SLListIterator<T,A>& other) const + { + return iterator_== other; + } + + + /** + * @brief Test whether another iterator is equal. + * @return true if the other iterator is at the same position as + * this one. + */ + inline bool equals(const SLListModifyIterator<T,A>& other) const + { + return iterator_== other.iterator_; + } + + /** + * @brief Increment function for the iterator facade. + */ + inline void increment() + { + ++iterator_; + ++beforeIterator_; + } + + /** + * @brief Insert an element at the current position. + * + * Starting from the element at the current position all + * elements will be shifted by one position to the back. + * The iterator will point to the same element as before + * after the insertion, i.e the number of increments to + * reach the same position from a begin iterator increases + * by one. + * This means the inserted element is the one before the one + * the iterator points to. + * @param v The value to insert. + */ + inline void insert(const T& v) + { + beforeIterator_.insertAfter(v); + ++beforeIterator_; + } + + /** + * @brief Delete the entry at the current position. + * + * The iterator will be positioned at the next position after the + * deletion + * @warning This will invalidate all iterators positioned at the delete position! Use with care! + */ + inline void remove() + { + ++iterator_; + beforeIterator_.deleteNext(); + } + + private: + /** @brief Iterator positioned at the position before the current. */ + SLListIterator<T,A> beforeIterator_; + /** @brief Iterator positioned at the current position. */ + SLListIterator<T,A> iterator_; + }; + + template<typename T, typename A> + std::ostream& operator<<(std::ostream& os, const SLList<T,A>& sllist) + { + typedef typename SLList<T,A>::const_iterator Iterator; + Iterator end = sllist.end(); + Iterator current= sllist.begin(); + + os << "{ "; + + if(current!=end) { + os<<*current<<" ("<<static_cast<const void*>(&(*current))<<")"; + ++current; + + for(; current != end; ++current) + os<<", "<<*current<<" ("<<static_cast<const void*>(&(*current))<<")"; + } + os<<"} "; + return os; + } + + template<typename T, class A> + SLList<T,A>::Element::Element(const MemberType& item, Element* next) + : next_(next), item_(item) + {} + + template<typename T, class A> + SLList<T,A>::Element::Element() + : next_(0), item_() + {} + + template<typename T, class A> + SLList<T,A>::Element::~Element() + { + next_=0; + } + + template<typename T, class A> + SLList<T,A>::SLList() + : beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) + { + beforeHead_.next_=0; + assert(&beforeHead_==tail_); + assert(tail_->next_==0); + } + + template<typename T, class A> + SLList<T,A>::SLList(const SLList<T,A>& other) + : beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) + { + copyElements(other); + } + + template<typename T, class A> + template<typename T1, class A1> + SLList<T,A>::SLList(const SLList<T1,A1>& other) + : beforeHead_(), tail_(&beforeHead_), allocator_(), size_(0) + { + copyElements(other); + } + + template<typename T, typename A> + void SLList<T,A>::copyElements(const SLList<T,A>& other) + { + assert(tail_==&beforeHead_); + assert(size_==0); + typedef typename SLList<T,A>::const_iterator Iterator; + Iterator iend = other.end(); + for(Iterator element=other.begin(); element != iend; ++element) + push_back(*element); + + assert(other.size()==size()); + } + + template<typename T, class A> + SLList<T,A>::~SLList() + { + clear(); + } + + template<typename T, class A> + bool SLList<T,A>::operator==(const SLList& other) const + { + if(size()!=other.size()) + return false; + for(const_iterator iter=begin(), oiter=other.begin(); + iter != end(); ++iter, ++oiter) + if(*iter!=*oiter) + return false; + return true; + } + + template<typename T, class A> + bool SLList<T,A>::operator!=(const SLList& other) const + { + if(size()==other.size()) { + for(const_iterator iter=begin(), oiter=other.begin(); + iter != end(); ++iter, ++oiter) + if(*iter!=*oiter) + return true; + return false; + }else + return true; + } + template<typename T, class A> + SLList<T,A>& SLList<T,A>::operator=(const SLList<T,A>& other) + { + clear(); + copyElements(other); + return *this; + } + + template<typename T, class A> + inline void SLList<T,A>::push_back(const MemberType& item) + { + assert(size_>0 || tail_==&beforeHead_); + tail_->next_ = allocator_.allocate(1); + assert(size_>0 || tail_==&beforeHead_); + tail_ = tail_->next_; + ::new (static_cast<void*>(&(tail_->item_)))T(item); + tail_->next_=0; + assert(tail_->next_==0); + ++size_; + } + + template<typename T, class A> + inline void SLList<T,A>::insertAfter(Element* current, const T& item) + { + assert(current); #ifndef NDEBUG -bool changeTail = (current == tail_); + bool changeTail = (current == tail_); #endif -// Save old next element -Element* tmp = current->next_; - -assert(!changeTail || !tmp); - -// Allocate space -current->next_ = allocator_.allocate(1); - -// Use copy constructor to initialize memory -std::allocator_traits<Allocator>::construct(allocator_, current->next_, Element(item,tmp)); - -//::new(static_cast<void*>(&(current->next_->item_))) T(item); - -if(!current->next_->next_) { -// Update tail -assert(changeTail); -tail_ = current->next_; -} -++size_; -assert(!tail_->next_); -} - -template<typename T, class A> -inline void SLList<T,A>::push_front(const MemberType& item) -{ -if(tail_ == &beforeHead_) { -// list was empty -beforeHead_.next_ = tail_ = allocator_.allocate(1, 0); -::new(static_cast<void*>(&beforeHead_.next_->item_))T(item); -beforeHead_.next_->next_=0; -}else{ -Element* added = allocator_.allocate(1, 0); -::new(static_cast<void*>(&added->item_))T(item); -added->next_=beforeHead_.next_; -beforeHead_.next_=added; -} -assert(tail_->next_==0); -++size_; -} - - -template<typename T, class A> -inline void SLList<T,A>::deleteNext(Element* current) -{ -this->template deleteNext<true>(current); -} - -template<typename T, class A> -template<bool watchForTail> -inline void SLList<T,A>::deleteNext(Element* current) -{ -assert(current->next_); -Element* next = current->next_; - -if(watchForTail) -if(next == tail_) { -// deleting last element changes tail! -tail_ = current; -} - -current->next_ = next->next_; -std::allocator_traits<Allocator>::destroy(allocator_, next); -allocator_.deallocate(next, 1); ---size_; -assert(!watchForTail || &beforeHead_ != tail_ || size_==0); -} - -template<typename T, class A> -inline void SLList<T,A>::pop_front() -{ -deleteNext(&beforeHead_); -} - -template<typename T, class A> -inline void SLList<T,A>::clear() -{ -while(beforeHead_.next_ ) { -this->template deleteNext<false>(&beforeHead_); -} - -assert(size_==0); -// update the tail! -tail_ = &beforeHead_; -} - -template<typename T, class A> -inline bool SLList<T,A>::empty() const -{ -return (&beforeHead_ == tail_); -} - -template<typename T, class A> -inline int SLList<T,A>::size() const -{ -return size_; -} - -template<typename T, class A> -inline SLListIterator<T,A> SLList<T,A>::begin() -{ -return iterator(beforeHead_.next_, this); -} - -template<typename T, class A> -inline SLListConstIterator<T,A> SLList<T,A>::begin() const -{ -return const_iterator(beforeHead_.next_); -} - -template<typename T, class A> -inline SLListIterator<T,A> SLList<T,A>::end() -{ -return iterator(); -} - -template<typename T, class A> -inline SLListModifyIterator<T,A> SLList<T,A>::endModify() -{ -return SLListModifyIterator<T,A>(iterator(tail_, this),iterator()); -} - - -template<typename T, class A> -inline SLListModifyIterator<T,A> SLList<T,A>::beginModify() -{ -return SLListModifyIterator<T,A>(iterator(&beforeHead_, this), -iterator(beforeHead_.next_, this)); -} - -template<typename T, class A> -inline SLListConstIterator<T,A> SLList<T,A>::end() const -{ -return const_iterator(); -} - -/** }@ */ + // Save old next element + Element* tmp = current->next_; + + assert(!changeTail || !tmp); + + // Allocate space + current->next_ = allocator_.allocate(1); + + // Use copy constructor to initialize memory + std::allocator_traits<Allocator>::construct(allocator_, current->next_, Element(item,tmp)); + + //::new(static_cast<void*>(&(current->next_->item_))) T(item); + + if(!current->next_->next_) { + // Update tail + assert(changeTail); + tail_ = current->next_; + } + ++size_; + assert(!tail_->next_); + } + + template<typename T, class A> + inline void SLList<T,A>::push_front(const MemberType& item) + { + if(tail_ == &beforeHead_) { + // list was empty + beforeHead_.next_ = tail_ = allocator_.allocate(1, 0); + ::new(static_cast<void*>(&beforeHead_.next_->item_))T(item); + beforeHead_.next_->next_=0; + }else{ + Element* added = allocator_.allocate(1, 0); + ::new(static_cast<void*>(&added->item_))T(item); + added->next_=beforeHead_.next_; + beforeHead_.next_=added; + } + assert(tail_->next_==0); + ++size_; + } + + + template<typename T, class A> + inline void SLList<T,A>::deleteNext(Element* current) + { + this->template deleteNext<true>(current); + } + + template<typename T, class A> + template<bool watchForTail> + inline void SLList<T,A>::deleteNext(Element* current) + { + assert(current->next_); + Element* next = current->next_; + + if(watchForTail) + if(next == tail_) { + // deleting last element changes tail! + tail_ = current; + } + + current->next_ = next->next_; + std::allocator_traits<Allocator>::destroy(allocator_, next); + allocator_.deallocate(next, 1); + --size_; + assert(!watchForTail || &beforeHead_ != tail_ || size_==0); + } + + template<typename T, class A> + inline void SLList<T,A>::pop_front() + { + deleteNext(&beforeHead_); + } + + template<typename T, class A> + inline void SLList<T,A>::clear() + { + while(beforeHead_.next_ ) { + this->template deleteNext<false>(&beforeHead_); + } + + assert(size_==0); + // update the tail! + tail_ = &beforeHead_; + } + + template<typename T, class A> + inline bool SLList<T,A>::empty() const + { + return (&beforeHead_ == tail_); + } + + template<typename T, class A> + inline int SLList<T,A>::size() const + { + return size_; + } + + template<typename T, class A> + inline SLListIterator<T,A> SLList<T,A>::begin() + { + return iterator(beforeHead_.next_, this); + } + + template<typename T, class A> + inline SLListConstIterator<T,A> SLList<T,A>::begin() const + { + return const_iterator(beforeHead_.next_); + } + + template<typename T, class A> + inline SLListIterator<T,A> SLList<T,A>::end() + { + return iterator(); + } + + template<typename T, class A> + inline SLListModifyIterator<T,A> SLList<T,A>::endModify() + { + return SLListModifyIterator<T,A>(iterator(tail_, this),iterator()); + } + + + template<typename T, class A> + inline SLListModifyIterator<T,A> SLList<T,A>::beginModify() + { + return SLListModifyIterator<T,A>(iterator(&beforeHead_, this), + iterator(beforeHead_.next_, this)); + } + + template<typename T, class A> + inline SLListConstIterator<T,A> SLList<T,A>::end() const + { + return const_iterator(); + } + + /** }@ */ } #endif diff --git a/dune/common/std/apply.hh b/dune/common/std/apply.hh index e75ab25f555d2fe0eafa15fd2f89c91636c4ce8c..c3101004f53bdc0cc7c3252cc4fc17e08026fea4 100644 --- a/dune/common/std/apply.hh +++ b/dune/common/std/apply.hh @@ -5,13 +5,13 @@ #include <dune/internal/dune-common.hh> #if DUNE_HAVE_CXX_APPLY -#include <tuple> + #include <tuple> #elif DUNE_HAVE_CXX_EXPERIMENTAL_APPLY -#include <experimental/tuple> + #include <experimental/tuple> #else -#include <cstddef> -#include <utility> -#include <tuple> + #include <cstddef> + #include <utility> + #include <tuple> #endif #include <dune/common/tupleutility.hh> @@ -20,39 +20,39 @@ namespace Dune { -namespace Std -{ + namespace Std + { #if DUNE_HAVE_CXX_APPLY -using std::apply; + using std::apply; #elif DUNE_HAVE_CXX_EXPERIMENTAL_APPLY -using std::experimental::apply; + using std::experimental::apply; #else -/** -* \brief Apply function with arguments given as tuple -* -* \param f A callable object -* \param args Tuple of arguments -* -* This will call the function with arguments generated by unpacking the tuple. -* -* \ingroup CxxUtilities -*/ -template<class F, class ArgTuple> -decltype(auto) apply(F&& f, ArgTuple&& args) -{ -auto indices = std::make_index_sequence<std::tuple_size<std::decay_t<ArgTuple>>::value>(); -return applyPartial(std::forward<F>(f), std::forward<ArgTuple>(args), indices); -} + /** + * \brief Apply function with arguments given as tuple + * + * \param f A callable object + * \param args Tuple of arguments + * + * This will call the function with arguments generated by unpacking the tuple. + * + * \ingroup CxxUtilities + */ + template<class F, class ArgTuple> + decltype(auto) apply(F&& f, ArgTuple&& args) + { + auto indices = std::make_index_sequence<std::tuple_size<std::decay_t<ArgTuple>>::value>(); + return applyPartial(std::forward<F>(f), std::forward<ArgTuple>(args), indices); + } #endif -} // namespace Std + } // namespace Std } // namespace Dune #endif // #ifndef DUNE_COMMON_STD_APPLY_HH diff --git a/dune/common/std/functional.hh b/dune/common/std/functional.hh index 00f5e90240cfe1afa1ff9dda66e7052be4491b94..2fe1c4ef42f22ee5898354a03474a5ae6f74fa90 100644 --- a/dune/common/std/functional.hh +++ b/dune/common/std/functional.hh @@ -9,24 +9,24 @@ namespace Dune { -namespace Std -{ + namespace Std + { -/** -* @brief A function object type whose operator() returns its argument unchanged -* @note Equivalent to: `return std::forward(t);` -* @warning When passing `r-values`, the result must be, at most, used for direct -* consumption in an outer function call -*/ + /** + * @brief A function object type whose operator() returns its argument unchanged + * @note Equivalent to: `return std::forward(t);` + * @warning When passing `r-values`, the result must be, at most, used for direct + * consumption in an outer function call + */ #if DUNE_HAVE_CXX_STD_IDENTITY -using std::identity; + using std::identity; #else //DUNE_HAVE_CXX_STD_IDENTITY -struct identity { -template<class T> -constexpr T&& operator()(T&& t ) const noexcept {return std::forward<T>(t);} -}; + struct identity { + template<class T> + constexpr T&& operator()(T&& t ) const noexcept {return std::forward<T>(t);} + }; #endif -} // namespace Std + } // namespace Std } // namespace Dune diff --git a/dune/common/std/make_array.hh b/dune/common/std/make_array.hh index 49155fcf56fc0aeabfea37c4a6e9afb43b46024b..6ab1a42159fd70f2210a4cb3fe584e6afe04ebb2 100644 --- a/dune/common/std/make_array.hh +++ b/dune/common/std/make_array.hh @@ -14,31 +14,31 @@ namespace Std { #if DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY -using std::experimental::make_array; + using std::experimental::make_array; #else // DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY -//! Create and initialize an array -/** -* \note This method is a somewhat limited dune-specific version of -* make_array() as proposed for C++17 (see <a -* href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4391.html">N4391</a>, -* accepted <a -* href="https://botondballo.wordpress.com/2015/06/05/trip-report-c-standards-meeting-in-lenexa-may-2015/">May -* 2015</a>). The differences are that this version should -* never be used with expliclitly given template arguments, or -* with std::reference_wrapper<...> arguments, and we do not -* give a diagnostic when anyone happens to do that. -* -* \ingroup CxxUtilities -*/ -template <typename... Args> -std::array<typename std::common_type<Args...>::type, sizeof...(Args)> -make_array(const Args&... args) { -std::array<typename std::common_type<Args...>::type, sizeof...(Args)> -result = {{args...}}; -return result; -} + //! Create and initialize an array + /** + * \note This method is a somewhat limited dune-specific version of + * make_array() as proposed for C++17 (see <a + * href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4391.html">N4391</a>, + * accepted <a + * href="https://botondballo.wordpress.com/2015/06/05/trip-report-c-standards-meeting-in-lenexa-may-2015/">May + * 2015</a>). The differences are that this version should + * never be used with expliclitly given template arguments, or + * with std::reference_wrapper<...> arguments, and we do not + * give a diagnostic when anyone happens to do that. + * + * \ingroup CxxUtilities + */ + template <typename... Args> + std::array<typename std::common_type<Args...>::type, sizeof...(Args)> + make_array(const Args&... args) { + std::array<typename std::common_type<Args...>::type, sizeof...(Args)> + result = {{args...}}; + return result; + } #endif // DUNE_HAVE_CXX_EXPERIMENTAL_MAKE_ARRAY diff --git a/dune/common/std/optional.hh b/dune/common/std/optional.hh index 9a9032250203f8bd3949523e774cdd4dea1214ce..3e5f3cc7300582f07a81c2f41ec8ae3ac36396ef 100644 --- a/dune/common/std/optional.hh +++ b/dune/common/std/optional.hh @@ -11,29 +11,29 @@ #include <optional> #warning dune/common/std/optional.hh is deprecated and will be removed after Dune 2.8.\ -Include <optional> instead + Include <optional> instead namespace Dune { -namespace Std -{ -// In case of C++ standard >= 17 we forward optionals into our namespace -template< class T > -using optional = std::optional< T >; + namespace Std + { + // In case of C++ standard >= 17 we forward optionals into our namespace + template< class T > + using optional = std::optional< T >; -using nullopt_t = std::nullopt_t; -using in_place_t = std::in_place_t; + using nullopt_t = std::nullopt_t; + using in_place_t = std::in_place_t; -namespace -{ -const std::nullopt_t nullopt = std::nullopt; -const std::in_place_t in_place = std::in_place; -} // anonymous namespace + namespace + { + const std::nullopt_t nullopt = std::nullopt; + const std::in_place_t in_place = std::in_place; + } // anonymous namespace -using bad_optional_access = std::bad_optional_access; + using bad_optional_access = std::bad_optional_access; -} // namespace Std + } // namespace Std } // namespace Dune diff --git a/dune/common/std/type_traits.hh b/dune/common/std/type_traits.hh index 681a74a80add72342182ce57f41d03bfdfe17285..3c876a1a9fcd26a8194545c6826d7e5faa19d88d 100644 --- a/dune/common/std/type_traits.hh +++ b/dune/common/std/type_traits.hh @@ -17,492 +17,492 @@ namespace Dune //! Namespace for features backported from new C++ standards /** -* The namespace Dune::Std contains library features of new C++ standards and -* technical specifications backported to older compilers. Most features are -* detected and pulled into this namespace from the standard library if your -* compiler has native support. If it doesn't, we provide a fallback implementation -* on a best-effort basis. -* -* \ingroup CxxUtilities -*/ + * The namespace Dune::Std contains library features of new C++ standards and + * technical specifications backported to older compilers. Most features are + * detected and pulled into this namespace from the standard library if your + * compiler has native support. If it doesn't, we provide a fallback implementation + * on a best-effort basis. + * + * \ingroup CxxUtilities + */ namespace Std { -// to_false_type -// ------------- - -/** \class to_false_type -* -* \brief template mapping a type to <tt>std::false_type</tt> -* -* \tparam T Some type -* -* Suppose you have a template class. You want to document the required -* members of this class in the non-specialized template, but you know that -* actually instantiating the non-specialized template is an error. You can -* try something like this: -* \code -* template<typename T> -* struct Traits -* { -* static_assert(false, -* "Instanciating this non-specialized template is an " -* "error. You should use one of the specializations " -* "instead."); -* //! The type used to frobnicate T -* typedef void FrobnicateType; -* }; -* \endcode -* This will trigger static_assert() as soon as the compiler reads the -* definition for the Traits template, since it knows that "false" can never -* become true, no matter what the template parameters of Traits are. As a -* workaround you can use to_false_type: replace <tt>false</tt> by -* <tt>to_false_type<T>::value</tt>, like this: -* \code -* template<typename T> -* struct Traits -* { -* static_assert(Std::to_false_type<T>::value, -* "Instanciating this non-specialized template is an " -* "error. You should use one of the specializations " -* "instead."); -* //! The type used to frobnicate T -* typedef void FrobnicateType; -* }; -* \endcode -* Since there might be an specialization of to_false_type for template -* parameter T, the compiler cannot trigger static_assert() until the type -* of T is known, that is, until Traits<T> is instantiated. -* -* \ingroup CxxUtilities -*/ -template< typename T > -struct to_false_type : public std::false_type {}; - - - -// to_true_type -// ------------ - -/** \class to_true_type -* -* \brief template mapping a type to <tt>std::true_type</tt> -* -* \tparam T Some type -* -* \note This class exists mostly for consistency with to_false_type. -* -* \ingroup CxxUtilities -*/ -template< typename T > -struct to_true_type : public std::true_type {}; + // to_false_type + // ------------- + + /** \class to_false_type + * + * \brief template mapping a type to <tt>std::false_type</tt> + * + * \tparam T Some type + * + * Suppose you have a template class. You want to document the required + * members of this class in the non-specialized template, but you know that + * actually instantiating the non-specialized template is an error. You can + * try something like this: + * \code + * template<typename T> + * struct Traits + * { + * static_assert(false, + * "Instanciating this non-specialized template is an " + * "error. You should use one of the specializations " + * "instead."); + * //! The type used to frobnicate T + * typedef void FrobnicateType; + * }; + * \endcode + * This will trigger static_assert() as soon as the compiler reads the + * definition for the Traits template, since it knows that "false" can never + * become true, no matter what the template parameters of Traits are. As a + * workaround you can use to_false_type: replace <tt>false</tt> by + * <tt>to_false_type<T>::value</tt>, like this: + * \code + * template<typename T> + * struct Traits + * { + * static_assert(Std::to_false_type<T>::value, + * "Instanciating this non-specialized template is an " + * "error. You should use one of the specializations " + * "instead."); + * //! The type used to frobnicate T + * typedef void FrobnicateType; + * }; + * \endcode + * Since there might be an specialization of to_false_type for template + * parameter T, the compiler cannot trigger static_assert() until the type + * of T is known, that is, until Traits<T> is instantiated. + * + * \ingroup CxxUtilities + */ + template< typename T > + struct to_false_type : public std::false_type {}; + + + + // to_true_type + // ------------ + + /** \class to_true_type + * + * \brief template mapping a type to <tt>std::true_type</tt> + * + * \tparam T Some type + * + * \note This class exists mostly for consistency with to_false_type. + * + * \ingroup CxxUtilities + */ + template< typename T > + struct to_true_type : public std::true_type {}; #if DUNE_HAVE_CXX_BOOL_CONSTANT -using std::bool_constant; + using std::bool_constant; #elif DUNE_HAVE_CXX_EXPERIMENTAL_BOOL_CONSTANT -using std::experimental::bool_constant; + using std::experimental::bool_constant; #else -/** -* \brief A template alias for std::integral_constant<bool, value> -* -* \tparam value Boolean value to encode as std::integral_constant<bool, value> -* -* \ingroup CxxUtilities -*/ -template <bool value> -using bool_constant = std::integral_constant<bool, value>; + /** + * \brief A template alias for std::integral_constant<bool, value> + * + * \tparam value Boolean value to encode as std::integral_constant<bool, value> + * + * \ingroup CxxUtilities + */ + template <bool value> + using bool_constant = std::integral_constant<bool, value>; #endif -namespace Impl { - -// If R is void we only need to check if F can be called -// with given Args... list. If this is not possible -// result_of_t is not defined and this overload is disabled. -template<class R, class F, class... Args, -std::enable_if_t< -std::is_same<void_t<std::result_of_t<F(Args...)>>, R>::value -, int> = 0> -std::true_type is_callable_helper(PriorityTag<2>) -{ return {}; } - -// Check if result of F(Args...) can be converted to R. -// If F cannot even be called with given Args... then -// result_of_t is not defined and this overload is disabled. -template<class R, class F, class... Args, -std::enable_if_t< -std::is_convertible<std::result_of_t<F(Args...)>, R>::value -, int> = 0> -std::true_type is_callable_helper(PriorityTag<1>) -{ return {}; } - -// If none of the above matches, F can either not be called -// with given Args..., or the result cannot be converted to -// void, or R is not void. -template<class R, class F, class... Args> -std::false_type is_callable_helper(PriorityTag<0>) -{ return {}; } -} - -/** -* \brief Traits class to check if function is callable -* -* \tparam D Function descriptor -* \tparam R Return value -* -* If D = F(Args...) this checks if F can be called with an -* argument list of type Args..., and if the return value can -* be converted to R. If R is void, any return type is accepted. -* The result is encoded by deriving from std::integral_constant<bool, result>. -* -* If D is not of the form D = F(Args...) this class is not defined. -* -* This implements std::is_callable as proposed in N4446 for C++17. -* -* \ingroup CxxUtilities -*/ -template <class D, class R= void> -struct is_callable; - -/** -* \brief Traits class to check if function is callable -* -* \tparam D Function descriptor -* \tparam R Return value -* -* If D = F(Args...) this checks if F can be called with an -* argument list of type Args..., and if the return value can -* be converted to R. If R is void, any return type is accepted. -* The result is encoded by deriving from std::integral_constant<bool, result>. -* -* If D is not of the form D = F(Args...) this class is not defined. -* -* This implements std::is_callable as proposed in N4446 for C++17. -* -* \ingroup CxxUtilities -*/ -template <class F, class... Args, class R> -struct is_callable< F(Args...), R> : -decltype(Impl::is_callable_helper<R, F, Args...>(PriorityTag<42>())) -{}; - - -/** -* \brief Traits class to check if function is invocable -* -* \tparam F Function to check -* \tparam Args Function arguments to check -* -* This checks if F can be called with an arguments list of type Args.... -* The result is encoded by deriving from std::integral_constant<bool, result>. -* -* This implements std::is_invocable from C++17. -* -* \ingroup CxxUtilities -*/ -template <class F, class... Args> -struct is_invocable : -decltype(Impl::is_callable_helper<void, F, Args...>(PriorityTag<42>())) -{}; - -/** -* \brief Traits class to check if function is invocable and the return type is compatible -* -* \tparam R Desired result type -* \tparam F Function to check -* \tparam Args Function arguments to check -* -* This checks if F can be called with an arguments list of type Args..., and -* if the return value can be converted to R. -* The result is encoded by deriving from std::integral_constant<bool, result>. -* -* This implements std::is_invocable_r from C++17. -* -* \ingroup CxxUtilities -*/ -template <class R, class F, class... Args> -struct is_invocable_r : -decltype(Impl::is_callable_helper<R, F, Args...>(PriorityTag<42>())) -{}; + namespace Impl { + + // If R is void we only need to check if F can be called + // with given Args... list. If this is not possible + // result_of_t is not defined and this overload is disabled. + template<class R, class F, class... Args, + std::enable_if_t< + std::is_same<void_t<std::result_of_t<F(Args...)>>, R>::value + , int> = 0> + std::true_type is_callable_helper(PriorityTag<2>) + { return {}; } + + // Check if result of F(Args...) can be converted to R. + // If F cannot even be called with given Args... then + // result_of_t is not defined and this overload is disabled. + template<class R, class F, class... Args, + std::enable_if_t< + std::is_convertible<std::result_of_t<F(Args...)>, R>::value + , int> = 0> + std::true_type is_callable_helper(PriorityTag<1>) + { return {}; } + + // If none of the above matches, F can either not be called + // with given Args..., or the result cannot be converted to + // void, or R is not void. + template<class R, class F, class... Args> + std::false_type is_callable_helper(PriorityTag<0>) + { return {}; } + } + + /** + * \brief Traits class to check if function is callable + * + * \tparam D Function descriptor + * \tparam R Return value + * + * If D = F(Args...) this checks if F can be called with an + * argument list of type Args..., and if the return value can + * be converted to R. If R is void, any return type is accepted. + * The result is encoded by deriving from std::integral_constant<bool, result>. + * + * If D is not of the form D = F(Args...) this class is not defined. + * + * This implements std::is_callable as proposed in N4446 for C++17. + * + * \ingroup CxxUtilities + */ + template <class D, class R= void> + struct is_callable; + + /** + * \brief Traits class to check if function is callable + * + * \tparam D Function descriptor + * \tparam R Return value + * + * If D = F(Args...) this checks if F can be called with an + * argument list of type Args..., and if the return value can + * be converted to R. If R is void, any return type is accepted. + * The result is encoded by deriving from std::integral_constant<bool, result>. + * + * If D is not of the form D = F(Args...) this class is not defined. + * + * This implements std::is_callable as proposed in N4446 for C++17. + * + * \ingroup CxxUtilities + */ + template <class F, class... Args, class R> + struct is_callable< F(Args...), R> : + decltype(Impl::is_callable_helper<R, F, Args...>(PriorityTag<42>())) + {}; + + + /** + * \brief Traits class to check if function is invocable + * + * \tparam F Function to check + * \tparam Args Function arguments to check + * + * This checks if F can be called with an arguments list of type Args.... + * The result is encoded by deriving from std::integral_constant<bool, result>. + * + * This implements std::is_invocable from C++17. + * + * \ingroup CxxUtilities + */ + template <class F, class... Args> + struct is_invocable : + decltype(Impl::is_callable_helper<void, F, Args...>(PriorityTag<42>())) + {}; + + /** + * \brief Traits class to check if function is invocable and the return type is compatible + * + * \tparam R Desired result type + * \tparam F Function to check + * \tparam Args Function arguments to check + * + * This checks if F can be called with an arguments list of type Args..., and + * if the return value can be converted to R. + * The result is encoded by deriving from std::integral_constant<bool, result>. + * + * This implements std::is_invocable_r from C++17. + * + * \ingroup CxxUtilities + */ + template <class R, class F, class... Args> + struct is_invocable_r : + decltype(Impl::is_callable_helper<R, F, Args...>(PriorityTag<42>())) + {}; #if DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED -using std::experimental::nonesuch; -using std::experimental::detected_or; -using std::experimental::is_detected; -using std::experimental::detected_t; -using std::experimental::is_detected_v; -using std::experimental::detected_or_t; -using std::experimental::is_detected_exact; -using std::experimental::is_detected_exact_v; -using std::experimental::is_detected_convertible; -using std::experimental::is_detected_convertible_v; + using std::experimental::nonesuch; + using std::experimental::detected_or; + using std::experimental::is_detected; + using std::experimental::detected_t; + using std::experimental::is_detected_v; + using std::experimental::detected_or_t; + using std::experimental::is_detected_exact; + using std::experimental::is_detected_exact_v; + using std::experimental::is_detected_convertible; + using std::experimental::is_detected_convertible_v; #else // DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED -// fallback version of std::experimental::is_detected et al., heavily scribbled -// from cppreference.com (but there is actually not much implementation to the thing) + // fallback version of std::experimental::is_detected et al., heavily scribbled + // from cppreference.com (but there is actually not much implementation to the thing) #ifndef DOXYGEN -namespace Impl { + namespace Impl { -// default version of detector, this gets matched on failure -template<typename Default, typename Void, template<typename...> class Op, typename... Args> -struct detector -{ -using value_t = std::false_type; -using type = Default; -}; + // default version of detector, this gets matched on failure + template<typename Default, typename Void, template<typename...> class Op, typename... Args> + struct detector + { + using value_t = std::false_type; + using type = Default; + }; -// specialization of detector that matches if Op<Args...> can be instantiated -template<typename Default, template<typename...> class Op, typename... Args> -struct detector<Default, void_t<Op<Args...>>, Op, Args...> -{ -using value_t = std::true_type; -using type = Op<Args...>; -}; + // specialization of detector that matches if Op<Args...> can be instantiated + template<typename Default, template<typename...> class Op, typename... Args> + struct detector<Default, void_t<Op<Args...>>, Op, Args...> + { + using value_t = std::true_type; + using type = Op<Args...>; + }; -} + } #endif // DOXYGEN -//! Type representing a lookup failure by std::detected_or and friends. -/** -* This type cannot be constructed, destroyed or copied. -* -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -struct nonesuch -{ -nonesuch() = delete; -~nonesuch() = delete; -nonesuch(const nonesuch&) = delete; -void operator=(const nonesuch&) = delete; -}; - -//! Detects whether `Op<Args...>` is valid and makes the result available. -/** -* This alias template is an alias for an unspecified class type with two -* nested `typedefs` `value_t` and `type`. It can be used to detect whether -* the meta function call `Op<Args...>` is valid and access the result of -* the call by inspecting the returned type, which is defined as follows: -* -* * If `Op<Args...>` can be instantiated, `value_t` is an alias for `std::true_type` -* and `type` is an alias for `Op<Args...>`. -* * If `Op<Args...>` is invalid, `value_t` is an alias for `std::false_type` -* and `type` is an alias for `Default`. -* -* This can be used to safely extract a nested `typedef` from a type `T` that -* might not define the `typedef`: -\code -struct A { using size_type = int ; }; -struct B; - -template<typename T> -using SizeType = typename T::size_type; - -// this extracts the nested typedef for int -using st_a = typename detected_or<std::size_t,SizeType,A>::type; -// as there is no nested typedef in B, this yields std::size_t -using st_b = typename detected_or<std::size_t,SizeType,B>::type; -\endcode -* -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<typename Default, template<typename...> class Op, typename... Args> -using detected_or = Impl::detector<Default,void,Op,Args...>; - -//! Detects whether `Op<Args...>` is valid. -/** -* This alias template checks whether `Op<Args...>` can be instantiated. It is -* equivalent to `typename detected_or<nonesuch,Op,Args...>::value_t`. -* -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<template<typename...> class Op, typename... Args> -using is_detected = typename detected_or<nonesuch,Op,Args...>::value_t; + //! Type representing a lookup failure by std::detected_or and friends. + /** + * This type cannot be constructed, destroyed or copied. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + struct nonesuch + { + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(const nonesuch&) = delete; + void operator=(const nonesuch&) = delete; + }; + + //! Detects whether `Op<Args...>` is valid and makes the result available. + /** + * This alias template is an alias for an unspecified class type with two + * nested `typedefs` `value_t` and `type`. It can be used to detect whether + * the meta function call `Op<Args...>` is valid and access the result of + * the call by inspecting the returned type, which is defined as follows: + * + * * If `Op<Args...>` can be instantiated, `value_t` is an alias for `std::true_type` + * and `type` is an alias for `Op<Args...>`. + * * If `Op<Args...>` is invalid, `value_t` is an alias for `std::false_type` + * and `type` is an alias for `Default`. + * + * This can be used to safely extract a nested `typedef` from a type `T` that + * might not define the `typedef`: + \code + struct A { using size_type = int ; }; + struct B; + + template<typename T> + using SizeType = typename T::size_type; + + // this extracts the nested typedef for int + using st_a = typename detected_or<std::size_t,SizeType,A>::type; + // as there is no nested typedef in B, this yields std::size_t + using st_b = typename detected_or<std::size_t,SizeType,B>::type; + \endcode + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<typename Default, template<typename...> class Op, typename... Args> + using detected_or = Impl::detector<Default,void,Op,Args...>; + + //! Detects whether `Op<Args...>` is valid. + /** + * This alias template checks whether `Op<Args...>` can be instantiated. It is + * equivalent to `typename detected_or<nonesuch,Op,Args...>::value_t`. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<template<typename...> class Op, typename... Args> + using is_detected = typename detected_or<nonesuch,Op,Args...>::value_t; #ifdef __cpp_variable_templates -//! Detects whether `Op<Args...>` is valid and makes the result available as a value. -/** -* This constexpr variable checks whether `Op<Args...>` can be instantiated. It is -* equivalent to `is_detected<Op,Args...>::value`. -* -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<template<typename...> class Op, typename... Args> -constexpr bool is_detected_v = is_detected<Op,Args...>::value; + //! Detects whether `Op<Args...>` is valid and makes the result available as a value. + /** + * This constexpr variable checks whether `Op<Args...>` can be instantiated. It is + * equivalent to `is_detected<Op,Args...>::value`. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<template<typename...> class Op, typename... Args> + constexpr bool is_detected_v = is_detected<Op,Args...>::value; #endif // __cpp_variable_templates -//! Returns `Op<Args...>` if that is valid; otherwise returns nonesuch. -/** -* This alias template can be used to instantiate `Op<Args...>` in a context that is -* not SFINAE-safe by appropriately wrapping the instantiation. If instantiation fails, -* the marker type nonesuch is returned instead. -* -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<template<typename...> class Op, typename... Args> -using detected_t = typename detected_or<nonesuch,Op,Args...>::type; - - -//! Returns `Op<Args...>` if that is valid; otherwise returns the fallback type `Default`. -/** -* This alias template can be used to instantiate `Op<Args...>` in a context that is -* not SFINAE-safe by appropriately wrapping the instantiation and automatically falling back -* to `Default` if instantiation fails. -* -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<typename Default, template<typename...> class Op, typename... Args> -using detected_or_t = typename detected_or<Default,Op,Args...>::type; - -//! Checks whether `Op<Args...>` is `Expected` without causing an error if `Op<Args...>` is invalid. -/** -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<typename Expected, template<typename...> class Op, typename... Args> -using is_detected_exact = std::is_same<Expected,detected_t<Op,Args...>>; + //! Returns `Op<Args...>` if that is valid; otherwise returns nonesuch. + /** + * This alias template can be used to instantiate `Op<Args...>` in a context that is + * not SFINAE-safe by appropriately wrapping the instantiation. If instantiation fails, + * the marker type nonesuch is returned instead. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<template<typename...> class Op, typename... Args> + using detected_t = typename detected_or<nonesuch,Op,Args...>::type; + + + //! Returns `Op<Args...>` if that is valid; otherwise returns the fallback type `Default`. + /** + * This alias template can be used to instantiate `Op<Args...>` in a context that is + * not SFINAE-safe by appropriately wrapping the instantiation and automatically falling back + * to `Default` if instantiation fails. + * + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<typename Default, template<typename...> class Op, typename... Args> + using detected_or_t = typename detected_or<Default,Op,Args...>::type; + + //! Checks whether `Op<Args...>` is `Expected` without causing an error if `Op<Args...>` is invalid. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<typename Expected, template<typename...> class Op, typename... Args> + using is_detected_exact = std::is_same<Expected,detected_t<Op,Args...>>; #ifdef __cpp_variable_templates -//! Convenient access to the result value of is_detected_exact. -/** -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<typename Expected, template<typename...> class Op, typename... Args> -constexpr bool is_detected_exact_v = is_detected_exact<Expected,Op,Args...>::value; + //! Convenient access to the result value of is_detected_exact. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<typename Expected, template<typename...> class Op, typename... Args> + constexpr bool is_detected_exact_v = is_detected_exact<Expected,Op,Args...>::value; #endif // __cpp_variable_templates -//! Checks whether `Op<Args...>` is convertible to `Target` without causing an error if `Op<Args...>` is invalid. -/** -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<typename Target, template<typename...> class Op, typename... Args> -using is_detected_convertible = std::is_convertible<Target,detected_t<Op,Args...>>; + //! Checks whether `Op<Args...>` is convertible to `Target` without causing an error if `Op<Args...>` is invalid. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<typename Target, template<typename...> class Op, typename... Args> + using is_detected_convertible = std::is_convertible<Target,detected_t<Op,Args...>>; #ifdef __cpp_variable_templates -//! Convenient access to the result value of is_detected_convertible. -/** -* \note This functionality is part of the C++ library fundamentals TS v2 and might -* or might not became part of C++2a. -* -* \ingroup CxxUtilities -*/ -template<typename Target, template<typename...> class Op, typename... Args> -constexpr bool is_detected_convertible_v = is_detected_convertible<Target,Op,Args...>::value; + //! Convenient access to the result value of is_detected_convertible. + /** + * \note This functionality is part of the C++ library fundamentals TS v2 and might + * or might not became part of C++2a. + * + * \ingroup CxxUtilities + */ + template<typename Target, template<typename...> class Op, typename... Args> + constexpr bool is_detected_convertible_v = is_detected_convertible<Target,Op,Args...>::value; #endif // __cpp_variable_templates #endif // DUNE_HAVE_CXX_EXPERIMENTAL_IS_DETECTED -// conjunction -// ----------- - -/** -* \brief forms the logical conjunction of the type traits B... -* -* \note This functionality is part of the C++17 standard. -* -* \ingroup CxxUtilities -**/ -template< class... B > -struct conjunction; - -template<> -struct conjunction<> -: std::true_type -{}; - -template< class B > -struct conjunction< B > -: B -{}; - -template< class B1, class... Bn > -struct conjunction< B1, Bn... > -: std::conditional_t< static_cast< bool >( B1::value ), conjunction< Bn... >, B1 > -{}; - - - -// disjunction -// ----------- - -/** -* \brief forms the logical disjunction of the type traits B... -* -* \note This functionality is part of the C++17 standard. -* -* \ingroup CxxUtilities -**/ -template< class... B > -struct disjunction; - -template<> -struct disjunction<> -: std::false_type -{}; - -template< class B > -struct disjunction< B > -: B -{}; - -template< class B1, class... Bn > -struct disjunction< B1, Bn... > -: std::conditional_t< static_cast< bool >( B1::value ), B1, disjunction< Bn... > > -{}; - -// negation -// -------- - -/** -* \brief forms the logical negation of the type traits B... -* -* \note This functionality is part of the C++17 standard. -* -* \ingroup CxxUtilities -**/ -template<class B> -struct negation : public bool_constant<!static_cast<bool>(B::value)> -{}; + // conjunction + // ----------- + + /** + * \brief forms the logical conjunction of the type traits B... + * + * \note This functionality is part of the C++17 standard. + * + * \ingroup CxxUtilities + **/ + template< class... B > + struct conjunction; + + template<> + struct conjunction<> + : std::true_type + {}; + + template< class B > + struct conjunction< B > + : B + {}; + + template< class B1, class... Bn > + struct conjunction< B1, Bn... > + : std::conditional_t< static_cast< bool >( B1::value ), conjunction< Bn... >, B1 > + {}; + + + + // disjunction + // ----------- + + /** + * \brief forms the logical disjunction of the type traits B... + * + * \note This functionality is part of the C++17 standard. + * + * \ingroup CxxUtilities + **/ + template< class... B > + struct disjunction; + + template<> + struct disjunction<> + : std::false_type + {}; + + template< class B > + struct disjunction< B > + : B + {}; + + template< class B1, class... Bn > + struct disjunction< B1, Bn... > + : std::conditional_t< static_cast< bool >( B1::value ), B1, disjunction< Bn... > > + {}; + + // negation + // -------- + + /** + * \brief forms the logical negation of the type traits B... + * + * \note This functionality is part of the C++17 standard. + * + * \ingroup CxxUtilities + **/ + template<class B> + struct negation : public bool_constant<!static_cast<bool>(B::value)> + {}; } // namespace Std diff --git a/dune/common/std/utility.hh b/dune/common/std/utility.hh index 7a06eefb8f6fb74852d44e8c214b56a53f7be9d6..f75977d2aca52f88356a245fe3305bd4894fdadf 100644 --- a/dune/common/std/utility.hh +++ b/dune/common/std/utility.hh @@ -5,21 +5,21 @@ #include <utility> #warning dune/common/std/utility.hh is deprecated and will be removed after Dune 2.8.\ -Include <utility> instead + Include <utility> instead namespace Dune { -namespace Std -{ + namespace Std + { -using std::integer_sequence; -using std::index_sequence; -using std::make_integer_sequence; -using std::make_index_sequence; -using std::index_sequence_for; + using std::integer_sequence; + using std::index_sequence; + using std::make_integer_sequence; + using std::make_index_sequence; + using std::index_sequence_for; -} // namespace Std + } // namespace Std } // namespace Dune diff --git a/dune/common/std/variant.hh b/dune/common/std/variant.hh index f855c9067129f77645b6bab8c49c642267174490..4113bd2f2a9d9495cf883a904a576ea60e82bc1a 100644 --- a/dune/common/std/variant.hh +++ b/dune/common/std/variant.hh @@ -5,20 +5,20 @@ #include <dune/internal/dune-common.hh> #warning dune/common/std/variant.hh is deprecated and will be removed after Dune 2.8.\ -Include <variant> instead + Include <variant> instead #include <variant> namespace Dune { namespace Std { -using std::variant; -using std::visit; -using std::variant_size; -using std::variant_size_v; -using std::get; -using std::get_if; -using std::holds_alternative; -using std::monostate; + using std::variant; + using std::visit; + using std::variant_size; + using std::variant_size_v; + using std::get; + using std::get_if; + using std::holds_alternative; + using std::monostate; } } diff --git a/dune/common/stdstreams.hh b/dune/common/stdstreams.hh index 7007e3300edaf933161a2c760de8303a609e4e94..db41eeb593e9456574d69f8309a5049cec23a0b8 100644 --- a/dune/common/stdstreams.hh +++ b/dune/common/stdstreams.hh @@ -2,13 +2,13 @@ // vi: set et ts=4 sw=2 sts=2: /** -\file -\brief Standard Dune debug streams + \file + \brief Standard Dune debug streams -The standard debug streams are compiled into libdune to exist -globally. This file declares the stream types and the global debug -level. -*/ + The standard debug streams are compiled into libdune to exist + globally. This file declares the stream types and the global debug + level. + */ #ifndef DUNE_COMMON_STDSTREAMS_HH #define DUNE_COMMON_STDSTREAMS_HH @@ -18,182 +18,182 @@ level. namespace Dune { -/** -\addtogroup DebugOut -@{ + /** + \addtogroup DebugOut + @{ -standard debug streams with level below MINIMAL_DEBUG_LEVEL will -collapse to doing nothing if output is requested. + standard debug streams with level below MINIMAL_DEBUG_LEVEL will + collapse to doing nothing if output is requested. -MINIMAL_DEBUG_LEVEL is set to DUNE_MINIMAL_DEBUG_LEVEL, which is -defined in config.h and can be changed by the configure option -@code --with-minimal-debug-level=[grave|warn|info|verb|vverb] @endcode + MINIMAL_DEBUG_LEVEL is set to DUNE_MINIMAL_DEBUG_LEVEL, which is + defined in config.h and can be changed by the configure option + @code --with-minimal-debug-level=[grave|warn|info|verb|vverb] @endcode -For a Dune-Release this should be set to at least 4 so that only -important messages are active. Dune-developers may adapt this -setting to their debugging needs locally + For a Dune-Release this should be set to at least 4 so that only + important messages are active. Dune-developers may adapt this + setting to their debugging needs locally -Keep in mind that libdune has to be recompiled if this value is changed! + Keep in mind that libdune has to be recompiled if this value is changed! -The singleton instances of the available debug streams can be found in -the \ref DebugOut "Standard Debug Streams" module + The singleton instances of the available debug streams can be found in + the \ref DebugOut "Standard Debug Streams" module -@} -*/ + @} + */ -/** -\defgroup StdStreams Standard Debug Streams -\ingroup DebugOut -@{ + /** + \defgroup StdStreams Standard Debug Streams + \ingroup DebugOut + @{ -Dune defines several standard output streams for the library -routines. + Dune defines several standard output streams for the library + routines. -Applications may control the standard streams via the attach/detach, -push/pop interface but should define an independent set of streams (see \ref DebugAppl ) + Applications may control the standard streams via the attach/detach, + push/pop interface but should define an independent set of streams (see \ref DebugAppl ) -*/ + */ -/** -@brief The default minimum debug level. + /** + @brief The default minimum debug level. -If the level of a stream is bigger than this value -it will be activated. -*/ + If the level of a stream is bigger than this value + it will be activated. + */ #ifndef DUNE_MINIMAL_DEBUG_LEVEL #define DUNE_MINIMAL_DEBUG_LEVEL 4 #endif -static const DebugLevel MINIMAL_DEBUG_LEVEL = DUNE_MINIMAL_DEBUG_LEVEL; - -/** -@brief The level of the very verbose debug stream. -@see dvverb -*/ -static const DebugLevel VERY_VERBOSE_DEBUG_LEVEL = 1; - -/** -@brief Type of very verbose debug stream. -@see dvverb -*/ -typedef DebugStream<VERY_VERBOSE_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DVVerbType; - -/** -\brief stream for very verbose output. - -\code -#include <dune/common/stdstreams.hh> -\endcode - -Information on the lowest -level. This is expected to report insane amounts of -information. Use of the activation-flag to only generate output -near the problem is recommended. -*/ -extern DVVerbType dvverb; - -/** -@brief The level of the verbose debug stream. -@see dvverb -*/ -static const DebugLevel VERBOSE_DEBUG_LEVEL = 2; - -/** -@brief Type of more verbose debug stream. -@see dverb -*/ -typedef DebugStream<VERBOSE_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DVerbType; - -/** -@brief Singleton of verbose debug stream. - -\code -#include <dune/common/stdstreams.hh> -\endcode -*/ -extern DVerbType dverb; - -/** -@brief The level of the informative debug stream. -@see dinfo -*/ -static const DebugLevel INFO_DEBUG_LEVEL = 3; - -/** -@brief Type of debug stream with info level. -@see dinfo -*/ -typedef DebugStream<INFO_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DInfoType; - -/** -@brief Stream for informative output. - -\code -#include <dune/common/stdstreams.hh> -\endcode - -Summary infos on what a module -does, runtimes, etc. -*/ -extern DInfoType dinfo; - -/** -@brief The level of the debug stream for warnings. -@see dwarn -*/ -static const DebugLevel WARN_DEBUG_LEVEL = 4; - -/** -@brief Type of debug stream with warn level. -@see dwarn -*/ -typedef DebugStream<WARN_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DWarnType; - -/** -@brief Stream for warnings indicating problems. - -\code -#include <dune/common/stdstreams.hh> -\endcode -*/ -extern DWarnType dwarn; - -/** -@brief The level of the debug stream for fatal errors. -@see dgrave -*/ -static const DebugLevel GRAVE_DEBUG_LEVEL = 5; - -/** @brief Type of debug stream for fatal errors.*/ -typedef DebugStream<GRAVE_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DGraveType; - -/** -@brief Stream for warnings indicating fatal errors. - -\code -#include <dune/common/stdstreams.hh> -\endcode -*/ -extern DGraveType dgrave; - -/** @brief The type of the stream used for error messages. */ -typedef DebugStream<1> DErrType; - -/** -@brief Stream for error messages. - -\code -#include <dune/common/stdstreams.hh> -\endcode - -Only packages integrating Dune -completely will redirect it. The output of derr is independent of -the debug-level, only the activation-flag is checked. -*/ -extern DErrType derr; - -/** }@ */ + static const DebugLevel MINIMAL_DEBUG_LEVEL = DUNE_MINIMAL_DEBUG_LEVEL; + + /** + @brief The level of the very verbose debug stream. + @see dvverb + */ + static const DebugLevel VERY_VERBOSE_DEBUG_LEVEL = 1; + + /** + @brief Type of very verbose debug stream. + @see dvverb + */ + typedef DebugStream<VERY_VERBOSE_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DVVerbType; + + /** + \brief stream for very verbose output. + + \code + #include <dune/common/stdstreams.hh> + \endcode + + Information on the lowest + level. This is expected to report insane amounts of + information. Use of the activation-flag to only generate output + near the problem is recommended. + */ + extern DVVerbType dvverb; + + /** + @brief The level of the verbose debug stream. + @see dvverb + */ + static const DebugLevel VERBOSE_DEBUG_LEVEL = 2; + + /** + @brief Type of more verbose debug stream. + @see dverb + */ + typedef DebugStream<VERBOSE_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DVerbType; + + /** + @brief Singleton of verbose debug stream. + + \code + #include <dune/common/stdstreams.hh> + \endcode + */ + extern DVerbType dverb; + + /** + @brief The level of the informative debug stream. + @see dinfo + */ + static const DebugLevel INFO_DEBUG_LEVEL = 3; + + /** + @brief Type of debug stream with info level. + @see dinfo + */ + typedef DebugStream<INFO_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DInfoType; + + /** + @brief Stream for informative output. + + \code + #include <dune/common/stdstreams.hh> + \endcode + + Summary infos on what a module + does, runtimes, etc. + */ + extern DInfoType dinfo; + + /** + @brief The level of the debug stream for warnings. + @see dwarn + */ + static const DebugLevel WARN_DEBUG_LEVEL = 4; + + /** + @brief Type of debug stream with warn level. + @see dwarn + */ + typedef DebugStream<WARN_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DWarnType; + + /** + @brief Stream for warnings indicating problems. + + \code + #include <dune/common/stdstreams.hh> + \endcode + */ + extern DWarnType dwarn; + + /** + @brief The level of the debug stream for fatal errors. + @see dgrave + */ + static const DebugLevel GRAVE_DEBUG_LEVEL = 5; + + /** @brief Type of debug stream for fatal errors.*/ + typedef DebugStream<GRAVE_DEBUG_LEVEL, MINIMAL_DEBUG_LEVEL> DGraveType; + + /** + @brief Stream for warnings indicating fatal errors. + + \code + #include <dune/common/stdstreams.hh> + \endcode + */ + extern DGraveType dgrave; + + /** @brief The type of the stream used for error messages. */ + typedef DebugStream<1> DErrType; + + /** + @brief Stream for error messages. + + \code + #include <dune/common/stdstreams.hh> + \endcode + + Only packages integrating Dune + completely will redirect it. The output of derr is independent of + the debug-level, only the activation-flag is checked. + */ + extern DErrType derr; + + /** }@ */ } #endif diff --git a/dune/common/stdthread.hh b/dune/common/stdthread.hh index 66cd8b17cd1a7bbeab58168192cfc3aa69fb27d1..75bf6bc415061706c4d3f05f54db506617190772 100644 --- a/dune/common/stdthread.hh +++ b/dune/common/stdthread.hh @@ -10,45 +10,45 @@ namespace Dune { -// used internally by assertCallOnce for the actual check -void doAssertCallOnce(const char *file, int line, const char *function); - -//! \brief Make sure call_once() works and provide a helpful error message -//! otherwise. -/** -* For call_once() to work, certain versions of libstdc++ need to be -* _linked_ with -pthread or similar flags. If that is not the case, -* call_once() will throw an exception. This function checks that -* call_once() can indeed be used, i.e. that it does not throw an exception -* when it should not, and that the code does indeed get executed. If -* call_once() cannot be used, assertCallOnce() aborts the program with a -* helpful error message. -* -* The check is only actually executed the first time assertCallOnce() is -* called. -* -* The arguments \c file and \c line specify the filename and line number -* that should appear in the error message. They are ignored if \c file is -* 0. The argument \c function specifies the name of the function to appear -* in the error message. It is ignored if \c function is 0. -*/ - -inline void assertCallOnce(const char *file = nullptr, int line = -1, -const char *function = nullptr) -{ -// make sure to call this only the first time this function is invoked -static const bool DUNE_UNUSED works -= (doAssertCallOnce(file, line, function), true); -} - -//! \brief Make sure call_once() works and provide a helpful error message -//! otherwise. -/** -* This calls assertCallOnce() and automatically provides information about -* the caller in the error message. -*/ + // used internally by assertCallOnce for the actual check + void doAssertCallOnce(const char *file, int line, const char *function); + + //! \brief Make sure call_once() works and provide a helpful error message + //! otherwise. + /** + * For call_once() to work, certain versions of libstdc++ need to be + * _linked_ with -pthread or similar flags. If that is not the case, + * call_once() will throw an exception. This function checks that + * call_once() can indeed be used, i.e. that it does not throw an exception + * when it should not, and that the code does indeed get executed. If + * call_once() cannot be used, assertCallOnce() aborts the program with a + * helpful error message. + * + * The check is only actually executed the first time assertCallOnce() is + * called. + * + * The arguments \c file and \c line specify the filename and line number + * that should appear in the error message. They are ignored if \c file is + * 0. The argument \c function specifies the name of the function to appear + * in the error message. It is ignored if \c function is 0. + */ + + inline void assertCallOnce(const char *file = nullptr, int line = -1, + const char *function = nullptr) + { + // make sure to call this only the first time this function is invoked + static const bool DUNE_UNUSED works + = (doAssertCallOnce(file, line, function), true); + } + + //! \brief Make sure call_once() works and provide a helpful error message + //! otherwise. + /** + * This calls assertCallOnce() and automatically provides information about + * the caller in the error message. + */ #define DUNE_ASSERT_CALL_ONCE() \ -::Dune::assertCallOnce(__FILE__, __LINE__, __func__) + ::Dune::assertCallOnce(__FILE__, __LINE__, __func__) } // namespace Dune diff --git a/dune/common/streamoperators.hh b/dune/common/streamoperators.hh index 12980ba03c424f10de898205dc58165a40a15e09..17ec160ef223dea2e481b33a47ce9b4d3096ff53 100644 --- a/dune/common/streamoperators.hh +++ b/dune/common/streamoperators.hh @@ -6,8 +6,8 @@ #include <dune/internal/dune-common.hh> /** \file -\brief Implementation of stream operators for std::array and std::tuple -*/ + \brief Implementation of stream operators for std::array and std::tuple + */ #include <array> #include <tuple> @@ -17,51 +17,51 @@ namespace Dune { -/** @addtogroup Common + /** @addtogroup Common -@{ -*/ + @{ + */ -//! Print a std::tuple -template<typename Stream, typename... Ts> -inline Stream& operator<<(Stream& stream, const std::tuple<Ts...>& t) -{ -stream<<"["; -if(sizeof...(Ts)>0) -{ -Hybrid::forEach(std::make_index_sequence<sizeof...(Ts)-1>{}, -[&](auto i){stream<<std::get<i>(t)<<",";}); -stream<<std::get<sizeof...(Ts)-1>(t); -} -stream<<"]"; -return stream; -} + //! Print a std::tuple + template<typename Stream, typename... Ts> + inline Stream& operator<<(Stream& stream, const std::tuple<Ts...>& t) + { + stream<<"["; + if(sizeof...(Ts)>0) + { + Hybrid::forEach(std::make_index_sequence<sizeof...(Ts)-1>{}, + [&](auto i){stream<<std::get<i>(t)<<",";}); + stream<<std::get<sizeof...(Ts)-1>(t); + } + stream<<"]"; + return stream; + } -//! Read a std::tuple -template<typename Stream, typename... Ts> -inline Stream& operator>>(Stream& stream, std::tuple<Ts...>& t) -{ -Hybrid::forEach(std::make_index_sequence<sizeof...(Ts)>{}, -[&](auto i){stream>>std::get<i>(t);}); -return stream; -} + //! Read a std::tuple + template<typename Stream, typename... Ts> + inline Stream& operator>>(Stream& stream, std::tuple<Ts...>& t) + { + Hybrid::forEach(std::make_index_sequence<sizeof...(Ts)>{}, + [&](auto i){stream>>std::get<i>(t);}); + return stream; + } -//! Print a std::array -template<typename Stream, typename T, std::size_t N> -inline Stream& operator<<(Stream& stream, const std::array<T,N>& a) -{ -stream<<"["; -if(N>0) -{ -for(std::size_t i=0; i<N-1; ++i) -stream<<a[i]<<","; -stream<<a[N-1]; -} -stream<<"]"; -return stream; -} + //! Print a std::array + template<typename Stream, typename T, std::size_t N> + inline Stream& operator<<(Stream& stream, const std::array<T,N>& a) + { + stream<<"["; + if(N>0) + { + for(std::size_t i=0; i<N-1; ++i) + stream<<a[i]<<","; + stream<<a[N-1]; + } + stream<<"]"; + return stream; + } -/** @} */ + /** @} */ } // end namespace Dune diff --git a/dune/common/stringutility.hh b/dune/common/stringutility.hh index 4596993e72f764e777dfb59877ac35b9e2118825..4c9de5f35356582c87fe8afefe6321759c4933e7 100644 --- a/dune/common/stringutility.hh +++ b/dune/common/stringutility.hh @@ -5,8 +5,8 @@ #include <dune/internal/dune-common.hh> /** \file -\brief Miscellaneous helper stuff -*/ + \brief Miscellaneous helper stuff + */ #include <cstddef> #include <cstring> @@ -22,92 +22,92 @@ namespace Dune { -/** -* @addtogroup StringUtilities -* -* @{ -*/ - -/** \brief Check whether a character container has a given prefix -* -* The container must support the begin() and size() methods. -*/ -template<typename C> -bool hasPrefix(const C& c, const char* prefix) { -std::size_t len = std::strlen(prefix); -return c.size() >= len && -std::equal(prefix, prefix+len, c.begin()); -} - -/** \brief Check whether a character container has a given suffix -* -* The container must support the begin() and size() methods and the -* const_iterator member type. -* -* \note This is slow for containers which don't have random access iterators. -* In the case of containers with bidirectional iterators, this -* slowness is unnecessary. -*/ -template<typename C> -bool hasSuffix(const C& c, const char* suffix) { -std::size_t len = std::strlen(suffix); -if(c.size() < len) return false; -typename C::const_iterator it = c.begin(); -std::advance(it, c.size() - len); -return std::equal(suffix, suffix+len, it); -} - -/** -* \brief Format values according to printf format string -* -* \param s The format string to be used -* \param args The valued to be formatted -* -* This is a wrapper to std::snprintf that provides -* overflow save printf functionality. For up to 1000 -* characters a static buffer is used. If this is not sufficient -* a dynamic buffer of appropriate size is allocated. -*/ -template<class... T> -static std::string formatString(const std::string& s, const T&... args) -{ -static const int bufferSize=1000; -char buffer[bufferSize]; - -// try to format with static buffer -int r = std::snprintf(buffer, bufferSize, s.c_str(), args...); - -// negative return values correspond to errors -if (r<0) -DUNE_THROW(Dune::Exception,"Could not convert format string using given arguments."); - -// if buffer was large enough return result as string -if (r<bufferSize) -return std::string(buffer); - -// if buffer was to small allocate a larger buffer using -// the predicted size hint (+1 for the terminating 0-byte). -int dynamicBufferSize = r+1; - -std::unique_ptr<char[]> dynamicBuffer; -try { -dynamicBuffer = std::make_unique<char[]>(dynamicBufferSize); -} -catch (const std::bad_alloc&) { -DUNE_THROW(Dune::Exception,"Could allocate large enough dynamic buffer in formatString."); -} - -// convert and check for errors again -r = std::snprintf(dynamicBuffer.get(), dynamicBufferSize, s.c_str(), args...); -if (r<0) -DUNE_THROW(Dune::Exception,"Could not convert format string using given arguments."); - -// the new buffer should always be large enough -assert(r<dynamicBufferSize); - -return std::string(dynamicBuffer.get()); -} -/** @} */ + /** + * @addtogroup StringUtilities + * + * @{ + */ + + /** \brief Check whether a character container has a given prefix + * + * The container must support the begin() and size() methods. + */ + template<typename C> + bool hasPrefix(const C& c, const char* prefix) { + std::size_t len = std::strlen(prefix); + return c.size() >= len && + std::equal(prefix, prefix+len, c.begin()); + } + + /** \brief Check whether a character container has a given suffix + * + * The container must support the begin() and size() methods and the + * const_iterator member type. + * + * \note This is slow for containers which don't have random access iterators. + * In the case of containers with bidirectional iterators, this + * slowness is unnecessary. + */ + template<typename C> + bool hasSuffix(const C& c, const char* suffix) { + std::size_t len = std::strlen(suffix); + if(c.size() < len) return false; + typename C::const_iterator it = c.begin(); + std::advance(it, c.size() - len); + return std::equal(suffix, suffix+len, it); + } + + /** + * \brief Format values according to printf format string + * + * \param s The format string to be used + * \param args The valued to be formatted + * + * This is a wrapper to std::snprintf that provides + * overflow save printf functionality. For up to 1000 + * characters a static buffer is used. If this is not sufficient + * a dynamic buffer of appropriate size is allocated. + */ + template<class... T> + static std::string formatString(const std::string& s, const T&... args) + { + static const int bufferSize=1000; + char buffer[bufferSize]; + + // try to format with static buffer + int r = std::snprintf(buffer, bufferSize, s.c_str(), args...); + + // negative return values correspond to errors + if (r<0) + DUNE_THROW(Dune::Exception,"Could not convert format string using given arguments."); + + // if buffer was large enough return result as string + if (r<bufferSize) + return std::string(buffer); + + // if buffer was to small allocate a larger buffer using + // the predicted size hint (+1 for the terminating 0-byte). + int dynamicBufferSize = r+1; + + std::unique_ptr<char[]> dynamicBuffer; + try { + dynamicBuffer = std::make_unique<char[]>(dynamicBufferSize); + } + catch (const std::bad_alloc&) { + DUNE_THROW(Dune::Exception,"Could allocate large enough dynamic buffer in formatString."); + } + + // convert and check for errors again + r = std::snprintf(dynamicBuffer.get(), dynamicBufferSize, s.c_str(), args...); + if (r<0) + DUNE_THROW(Dune::Exception,"Could not convert format string using given arguments."); + + // the new buffer should always be large enough + assert(r<dynamicBufferSize); + + return std::string(dynamicBuffer.get()); + } + /** @} */ } diff --git a/dune/common/test/arithmetictestsuite.hh b/dune/common/test/arithmetictestsuite.hh index 547c4ad59b202b7cd60392be290ac7616935ec6c..406ad4a125721128347290cf2e08498f67201abb 100644 --- a/dune/common/test/arithmetictestsuite.hh +++ b/dune/common/test/arithmetictestsuite.hh @@ -15,696 +15,696 @@ namespace Dune { /* -* silence warnings from GCC about using integer operands on a bool -* (when instantiated for T=bool) -*/ + * silence warnings from GCC about using integer operands on a bool + * (when instantiated for T=bool) + */ #if __GNUC__ >= 7 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wbool-operation" # pragma GCC diagnostic ignored "-Wint-in-bool-context" #endif -//! Test suite for arithmetic types -/** -* You usually want to call the member function `checkArithmetic()`. The -* individual component tests are however available for special needs. -*/ -class ArithmeticTestSuite : -public TestSuite -{ -public: -// inherit all constructors from TestSuite -using TestSuite::TestSuite; - -//! tag denoting any arithmetic type -struct Arithmetic {}; -//! tag denoting integral types -struct Integral : Arithmetic {}; -//! tag denoting boolean types -struct Boolean : Integral {}; -//! tag denoting signed integral types -struct Signed : Integral {}; -//! tag denoting unsigned integral types -struct Unsigned : Integral {}; -//! tag denoting floating point types -struct Floating : Arithmetic {}; - -private: -static const char *name(Arithmetic) { return "Arithmetic"; } -static const char *name(Integral ) { return "Integral"; } -static const char *name(Boolean ) { return "Boolean"; } -static const char *name(Signed ) { return "Signed"; } -static const char *name(Unsigned ) { return "Unsigned"; } -static const char *name(Floating ) { return "Floating"; } - -template<class C, class Then, class Else = void> -using Cond = typename std::conditional<C::value, Then, Else>::type; - -public: -//! determine arithmetic tag for the given type -/** -* `T` can be either one of the fundamental arithmetic types, in which -* case a default-constructed tag object for that type is returned. Or it -* can be a class derived from `Arithmetic`, in which case a -* default-constructed object of that class is is returned. -*/ -template<class T> -constexpr static auto tag(T = T{}) -{ -return -Cond<std::is_convertible<T, Arithmetic>, T, -Cond<std::is_floating_point<T>, Floating, -Cond<std::is_integral<T>, -Cond<std::is_same<bool, T>, Boolean, -Cond<std::is_signed<T>, Signed, -Cond<std::is_unsigned<T>, Unsigned -> > > -> > >{}; -} + //! Test suite for arithmetic types + /** + * You usually want to call the member function `checkArithmetic()`. The + * individual component tests are however available for special needs. + */ + class ArithmeticTestSuite : + public TestSuite + { + public: + // inherit all constructors from TestSuite + using TestSuite::TestSuite; + + //! tag denoting any arithmetic type + struct Arithmetic {}; + //! tag denoting integral types + struct Integral : Arithmetic {}; + //! tag denoting boolean types + struct Boolean : Integral {}; + //! tag denoting signed integral types + struct Signed : Integral {}; + //! tag denoting unsigned integral types + struct Unsigned : Integral {}; + //! tag denoting floating point types + struct Floating : Arithmetic {}; + + private: + static const char *name(Arithmetic) { return "Arithmetic"; } + static const char *name(Integral ) { return "Integral"; } + static const char *name(Boolean ) { return "Boolean"; } + static const char *name(Signed ) { return "Signed"; } + static const char *name(Unsigned ) { return "Unsigned"; } + static const char *name(Floating ) { return "Floating"; } + + template<class C, class Then, class Else = void> + using Cond = typename std::conditional<C::value, Then, Else>::type; + + public: + //! determine arithmetic tag for the given type + /** + * `T` can be either one of the fundamental arithmetic types, in which + * case a default-constructed tag object for that type is returned. Or it + * can be a class derived from `Arithmetic`, in which case a + * default-constructed object of that class is is returned. + */ + template<class T> + constexpr static auto tag(T = T{}) + { + return + Cond<std::is_convertible<T, Arithmetic>, T, + Cond<std::is_floating_point<T>, Floating, + Cond<std::is_integral<T>, + Cond<std::is_same<bool, T>, Boolean, + Cond<std::is_signed<T>, Signed, + Cond<std::is_unsigned<T>, Unsigned + > > > + > > >{}; + } #define DUNE_TEST_FUNCTION(T, tag) \ -static const auto function = \ -std::string(__func__) + "<" + className<T>() + ">(" + name(tag) + ")" + static const auto function = \ + std::string(__func__) + "<" + className<T>() + ">(" + name(tag) + ")" #define DUNE_TEST_CHECK(expr) \ -(check((expr), function) \ -<< __FILE__ << ":" << __LINE__ << ": Check \"" << #expr << "\"") - -// -// check basic operations: construct, copy, compare -// - -//! check the default constructors -template<class T> -void checkDefaultConstruct(Arithmetic arithmetic_tag) -{ -DUNE_UNUSED_PARAMETER(arithmetic_tag); -T DUNE_UNUSED t0; -(void)T(); -T DUNE_UNUSED t1{}; -T DUNE_UNUSED t2 = {}; -} - -//! check explicit conversion from and to int -template<class T> -void checkExplicitIntConvert(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); -// this test may be applied to boolean-type types. 0 and 1 are the only -// values that survive that. -T t0(0); DUNE_TEST_CHECK(int(t0) == 0); -T t1(1); DUNE_TEST_CHECK(int(t1) == 1); -} - -//! check the move constructor -template<class T> -void checkMoveConstruct(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); -for(int i : { 0, 1 }) -{ -T t0(i); - -T t1(std::move(t0)); -T t2 = std::move(t1); -T t3{ std::move(t2) }; -T t4 = { std::move(t3) }; - -DUNE_TEST_CHECK(bool(t4 == T(i))); -} -} - -//! check the copy constructor -template<class T> -void checkCopyConstruct(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); -for(int i : { 0, 1 }) -{ -T t0(i); - -T t1(t0); -T t2 = t1; -T t3{ t2 }; -T t4 = { t3 }; - -DUNE_TEST_CHECK(bool(t0 == T(i))); -DUNE_TEST_CHECK(bool(t1 == T(i))); -DUNE_TEST_CHECK(bool(t2 == T(i))); -DUNE_TEST_CHECK(bool(t3 == T(i))); -DUNE_TEST_CHECK(bool(t4 == T(i))); -} -} - -//! check the move assignment operator -template<class T> -void checkMoveAssign(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); -for(int i : { 0, 1 }) -{ -T t0(i); -T t2, t4; - -t2 = std::move(t0); -t4 = { std::move(t2) }; - -DUNE_TEST_CHECK(bool(t4 == T(i))); -} -} - -//! check the copy assignment operator -template<class T> -void checkCopyAssign(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); -for(int i : { 0, 1 }) -{ -T t0(i); -T t2, t4; - -t2 = t0; -t4 = { t2 }; - -DUNE_TEST_CHECK(bool(t0 == T(i))); -DUNE_TEST_CHECK(bool(t2 == T(i))); -DUNE_TEST_CHECK(bool(t4 == T(i))); -} -} - -//! check `==` and `!=` -/** -* \note We do not require the result to be _implicitly_ convertible to -* bool, but it must be contextually convertible to bool. -*/ -template<class T> -void checkEqual(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); -T t0(0); -T t1(1); - -DUNE_TEST_CHECK(bool(t0 == T(0))); -DUNE_TEST_CHECK(bool(t1 == T(1))); - -DUNE_TEST_CHECK(!bool(t0 == T(1))); -DUNE_TEST_CHECK(!bool(t1 == T(0))); -DUNE_TEST_CHECK(!bool(t0 == t1)); - -DUNE_TEST_CHECK(!bool(t0 != T(0))); -DUNE_TEST_CHECK(!bool(t1 != T(1))); - -DUNE_TEST_CHECK(bool(t0 != T(1))); -DUNE_TEST_CHECK(bool(t1 != T(0))); -DUNE_TEST_CHECK(bool(t0 != t1)); -} - -// -// checks for unary operators -// - -//! check postfix `++` -/** -* Applies to boolean (deprecated), integral, and floating point. -*/ -template<class T> -void checkPostfixInc(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -T t0(0); -DUNE_TEST_CHECK(bool(T(t0++) == T(0))); -DUNE_TEST_CHECK(bool(t0 == T(1))); -} -template<class T> -void checkPostfixInc(Boolean) {} - -//! check postfix `--` -/** -* Applies to integral (no boolean), and floating point. -*/ -template<class T> -void checkPostfixDec(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -T t1(1); -DUNE_TEST_CHECK(bool(T(t1--) == T(1))); -DUNE_TEST_CHECK(bool(t1 == T(0))); -} -template<class T> -void checkPostfixDec(Boolean) {} - -//! check prefix `+` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkPrefixPlus(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(+T(0)) == T(0))); -DUNE_TEST_CHECK(bool(T(+T(1)) == T(1))); -} - -//! check prefix `-` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkPrefixMinus(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(-T(0)) == T( 0))); -DUNE_TEST_CHECK(bool(T(-T(1)) == T(-1))); -} - -//! check prefix `!` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkPrefixNot(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(!T(0))); -DUNE_TEST_CHECK(!bool(!T(1))); -} - -//! check prefix `~` -/** -* Applies to boolean and integral. -*/ -template<class T> -void checkPrefixBitNot(Boolean arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(~T(0)))); -} -template<class T> -void checkPrefixBitNot(Integral arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(~T(0)))); -DUNE_TEST_CHECK(bool(T(~T(1)))); - -DUNE_TEST_CHECK(bool(T(~T(~T(0))) == T(0))); -DUNE_TEST_CHECK(bool(T(~T(~T(1))) == T(1))); -} -template<class T> -void checkPrefixBitNot(Unsigned arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -checkPrefixBitNot<T>(Integral{}); - -DUNE_TEST_CHECK(bool(T(~T(0)) == T(-1))); -DUNE_TEST_CHECK(bool(T(~T(1)) == T(-2))); -} -template<class T> -void checkPrefixBitNot(Floating) {} - -//! check postfix `++` -/** -* Applies to boolean (deprecated), integral, and floating point. -*/ -template<class T> -void checkPrefixInc(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -T t0(0); -DUNE_TEST_CHECK(bool(T(++t0) == T(1))); -DUNE_TEST_CHECK(bool(t0 == T(1))); -++t0 = T(0); -DUNE_TEST_CHECK(bool(t0 == T(0))); -} -template<class T> -void checkPrefixInc(Boolean) {} - -//! check postfix `--` -/** -* Applies to integral (no boolean), and floating point. -*/ -template<class T> -void checkPrefixDec(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -T t1(1); -DUNE_TEST_CHECK(bool(T(--t1) == T(0))); -DUNE_TEST_CHECK(bool(t1 == T(0))); -t1 = T(1); ---t1 = T(1); -DUNE_TEST_CHECK(bool(t1 == T(1))); -} -template<class T> -void checkPrefixDec(Boolean) {} - -// -// checks for infox operators -// - -//! check infix `*` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixMul(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(T(0)*T(0)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)*T(0)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(0)*T(1)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)*T(1)) == T(1))); -} - -//! check infix `/` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixDiv(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(T(0)/T(1)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)/T(1)) == T(1))); -} - -//! check infix `%` -/** -* Applies to boolean and integral. -*/ -template<class T> -void checkInfixRem(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(T(0)%T(1)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)%T(1)) == T(0))); -} -template<class T> -void checkInfixRem(Floating) {} - -//! check infix `+` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixPlus(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(T(0)+T(0)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)+T(0)) == T(1))); -DUNE_TEST_CHECK(bool(T(T(0)+T(1)) == T(1))); -DUNE_TEST_CHECK(bool(T(T(1)+T(1)) == T(2))); -} - -//! check infix `-` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixMinus(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(T(0)-T(0)) == T( 0))); -DUNE_TEST_CHECK(bool(T(T(1)-T(0)) == T( 1))); -DUNE_TEST_CHECK(bool(T(T(0)-T(1)) == T(-1))); -DUNE_TEST_CHECK(bool(T(T(1)-T(1)) == T( 0))); -} - -//! check infix `<<` -/** -* Applies to boolean and integral. -*/ -template<class T> -void checkInfixLShift(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(T(0)<<T(0)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)<<T(0)) == T(1))); -DUNE_TEST_CHECK(bool(T(T(0)<<T(1)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)<<T(1)) == T(2))); -} -template<class T> -void checkInfixLShift(Floating) {} - -//! check infix `>>` -/** -* Applies to boolean and integral. -*/ -template<class T> -void checkInfixRShift(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(T(0)>>T(0)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)>>T(0)) == T(1))); -DUNE_TEST_CHECK(bool(T(T(0)>>T(1)) == T(0))); -DUNE_TEST_CHECK(bool(T(T(1)>>T(1)) == T(0))); -} -template<class T> -void checkInfixRShift(Floating) {} - -//! check infix `<` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixLess(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -DUNE_TEST_CHECK(bool(T(0)<T(0)) == false); -DUNE_TEST_CHECK(bool(T(1)<T(0)) == false); -DUNE_TEST_CHECK(bool(T(0)<T(1)) == true ); -DUNE_TEST_CHECK(bool(T(1)<T(1)) == false); -} -template<class T> -void checkInfixLess(Signed arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -checkInfixLess<T>(Integral{}); - -DUNE_TEST_CHECK(bool(T(-1)<T( 0)) == true); -} -template<class T> -void checkInfixLess(Unsigned arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -checkInfixLess<T>(Integral{}); - -DUNE_TEST_CHECK(bool(T(-1)<T( 0)) == false); -} - -//! check infix `>` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixGreater(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -int values[] = { -1, 0, 1 }; -for(int i : values) -for(int j : values) -DUNE_TEST_CHECK(bool(T(i) > T(j)) == bool(T(j) < T(i))); -} - -//! check infix `<=` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixLessEqual(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -int values[] = { -1, 0, 1 }; -for(int i : values) -for(int j : values) -DUNE_TEST_CHECK(bool(T(i) <= T(j)) != bool(T(j) < T(i))); -} - -//! check infix `>=` -/** -* Applies to boolean, integral, and floating point. -*/ -template<class T> -void checkInfixGreaterEqual(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -int values[] = { -1, 0, 1 }; -for(int i : values) -for(int j : values) -DUNE_TEST_CHECK(bool(T(i) >= T(j)) != bool(T(i) < T(j))); -} - -//! check infix `&` -/** -* Applies to boolean and integral. -*/ -template<class T> -void checkInfixBitAnd(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 4; ++i) -for(int j = 0; j < 4; ++j) -DUNE_TEST_CHECK(bool(T(T(i) & T(j)) == T(i&j))); -} -template<class T> -void checkInfixBitAnd(Boolean arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 2; ++i) -for(int j = 0; j < 2; ++j) -DUNE_TEST_CHECK(bool(T(T(i) & T(j)) == T(i&j))); -} -template<class T> -void checkInfixBitAnd(Floating) {} - -//! check infix `^` -/** -* Applies to boolean and integral. -*/ -template<class T> -void checkInfixBitXor(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 4; ++i) -for(int j = 0; j < 4; ++j) -DUNE_TEST_CHECK(bool(T(T(i) ^ T(j)) == T(i^j))); -} -template<class T> -void checkInfixBitXor(Boolean arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 2; ++i) -for(int j = 0; j < 2; ++j) -// compare the bit-flipped versions so we don't depend on the number -// of bits in T. -DUNE_TEST_CHECK(bool(T(~T(T(i) ^ T(j))) == T(~(i^j)))); -} -template<class T> -void checkInfixBitXor(Floating) {} - -//! check infix `|` -/** -* Applies to boolean and integral. -*/ -template<class T> -void checkInfixBitOr(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 4; ++i) -for(int j = 0; j < 4; ++j) -DUNE_TEST_CHECK(bool(T(T(i) | T(j)) == T(i|j))); -} -template<class T> -void checkInfixBitOr(Boolean arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 2; ++i) -for(int j = 0; j < 2; ++j) -DUNE_TEST_CHECK(bool(T(T(i) | T(j)) == T(i|j))); -} -template<class T> -void checkInfixBitOr(Floating) {} - -//! check infix `&&` -/** -* Applies to boolean, integral and floating point. -*/ -template<class T> -void checkInfixAnd(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 4; ++i) -for(int j = 0; j < 4; ++j) -DUNE_TEST_CHECK(bool(T(i) && T(j)) == (i && j)); -} - -//! check infix `||` -/** -* Applies to boolean, integral and floating point. -*/ -template<class T> -void checkInfixOr(Arithmetic arithmetic_tag) -{ -DUNE_TEST_FUNCTION(T, arithmetic_tag); - -for(int i = 0; i < 4; ++i) -for(int j = 0; j < 4; ++j) -DUNE_TEST_CHECK(bool(T(i) || T(j)) == (i || j)); -} - -// -// checks for compound assignment operators -// + (check((expr), function) \ + << __FILE__ << ":" << __LINE__ << ": Check \"" << #expr << "\"") + + // + // check basic operations: construct, copy, compare + // + + //! check the default constructors + template<class T> + void checkDefaultConstruct(Arithmetic arithmetic_tag) + { + DUNE_UNUSED_PARAMETER(arithmetic_tag); + T DUNE_UNUSED t0; + (void)T(); + T DUNE_UNUSED t1{}; + T DUNE_UNUSED t2 = {}; + } + + //! check explicit conversion from and to int + template<class T> + void checkExplicitIntConvert(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + // this test may be applied to boolean-type types. 0 and 1 are the only + // values that survive that. + T t0(0); DUNE_TEST_CHECK(int(t0) == 0); + T t1(1); DUNE_TEST_CHECK(int(t1) == 1); + } + + //! check the move constructor + template<class T> + void checkMoveConstruct(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + + T t1(std::move(t0)); + T t2 = std::move(t1); + T t3{ std::move(t2) }; + T t4 = { std::move(t3) }; + + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check the copy constructor + template<class T> + void checkCopyConstruct(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + + T t1(t0); + T t2 = t1; + T t3{ t2 }; + T t4 = { t3 }; + + DUNE_TEST_CHECK(bool(t0 == T(i))); + DUNE_TEST_CHECK(bool(t1 == T(i))); + DUNE_TEST_CHECK(bool(t2 == T(i))); + DUNE_TEST_CHECK(bool(t3 == T(i))); + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check the move assignment operator + template<class T> + void checkMoveAssign(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + T t2, t4; + + t2 = std::move(t0); + t4 = { std::move(t2) }; + + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check the copy assignment operator + template<class T> + void checkCopyAssign(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + for(int i : { 0, 1 }) + { + T t0(i); + T t2, t4; + + t2 = t0; + t4 = { t2 }; + + DUNE_TEST_CHECK(bool(t0 == T(i))); + DUNE_TEST_CHECK(bool(t2 == T(i))); + DUNE_TEST_CHECK(bool(t4 == T(i))); + } + } + + //! check `==` and `!=` + /** + * \note We do not require the result to be _implicitly_ convertible to + * bool, but it must be contextually convertible to bool. + */ + template<class T> + void checkEqual(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + T t0(0); + T t1(1); + + DUNE_TEST_CHECK(bool(t0 == T(0))); + DUNE_TEST_CHECK(bool(t1 == T(1))); + + DUNE_TEST_CHECK(!bool(t0 == T(1))); + DUNE_TEST_CHECK(!bool(t1 == T(0))); + DUNE_TEST_CHECK(!bool(t0 == t1)); + + DUNE_TEST_CHECK(!bool(t0 != T(0))); + DUNE_TEST_CHECK(!bool(t1 != T(1))); + + DUNE_TEST_CHECK(bool(t0 != T(1))); + DUNE_TEST_CHECK(bool(t1 != T(0))); + DUNE_TEST_CHECK(bool(t0 != t1)); + } + + // + // checks for unary operators + // + + //! check postfix `++` + /** + * Applies to boolean (deprecated), integral, and floating point. + */ + template<class T> + void checkPostfixInc(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t0(0); + DUNE_TEST_CHECK(bool(T(t0++) == T(0))); + DUNE_TEST_CHECK(bool(t0 == T(1))); + } + template<class T> + void checkPostfixInc(Boolean) {} + + //! check postfix `--` + /** + * Applies to integral (no boolean), and floating point. + */ + template<class T> + void checkPostfixDec(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t1(1); + DUNE_TEST_CHECK(bool(T(t1--) == T(1))); + DUNE_TEST_CHECK(bool(t1 == T(0))); + } + template<class T> + void checkPostfixDec(Boolean) {} + + //! check prefix `+` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkPrefixPlus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(+T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(+T(1)) == T(1))); + } + + //! check prefix `-` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkPrefixMinus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(-T(0)) == T( 0))); + DUNE_TEST_CHECK(bool(T(-T(1)) == T(-1))); + } + + //! check prefix `!` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkPrefixNot(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(!T(0))); + DUNE_TEST_CHECK(!bool(!T(1))); + } + + //! check prefix `~` + /** + * Applies to boolean and integral. + */ + template<class T> + void checkPrefixBitNot(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(~T(0)))); + } + template<class T> + void checkPrefixBitNot(Integral arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(~T(0)))); + DUNE_TEST_CHECK(bool(T(~T(1)))); + + DUNE_TEST_CHECK(bool(T(~T(~T(0))) == T(0))); + DUNE_TEST_CHECK(bool(T(~T(~T(1))) == T(1))); + } + template<class T> + void checkPrefixBitNot(Unsigned arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + checkPrefixBitNot<T>(Integral{}); + + DUNE_TEST_CHECK(bool(T(~T(0)) == T(-1))); + DUNE_TEST_CHECK(bool(T(~T(1)) == T(-2))); + } + template<class T> + void checkPrefixBitNot(Floating) {} + + //! check postfix `++` + /** + * Applies to boolean (deprecated), integral, and floating point. + */ + template<class T> + void checkPrefixInc(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t0(0); + DUNE_TEST_CHECK(bool(T(++t0) == T(1))); + DUNE_TEST_CHECK(bool(t0 == T(1))); + ++t0 = T(0); + DUNE_TEST_CHECK(bool(t0 == T(0))); + } + template<class T> + void checkPrefixInc(Boolean) {} + + //! check postfix `--` + /** + * Applies to integral (no boolean), and floating point. + */ + template<class T> + void checkPrefixDec(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + T t1(1); + DUNE_TEST_CHECK(bool(T(--t1) == T(0))); + DUNE_TEST_CHECK(bool(t1 == T(0))); + t1 = T(1); + --t1 = T(1); + DUNE_TEST_CHECK(bool(t1 == T(1))); + } + template<class T> + void checkPrefixDec(Boolean) {} + + // + // checks for infox operators + // + + //! check infix `*` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixMul(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)*T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)*T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(0)*T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)*T(1)) == T(1))); + } + + //! check infix `/` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixDiv(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)/T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)/T(1)) == T(1))); + } + + //! check infix `%` + /** + * Applies to boolean and integral. + */ + template<class T> + void checkInfixRem(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)%T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)%T(1)) == T(0))); + } + template<class T> + void checkInfixRem(Floating) {} + + //! check infix `+` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixPlus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)+T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)+T(0)) == T(1))); + DUNE_TEST_CHECK(bool(T(T(0)+T(1)) == T(1))); + DUNE_TEST_CHECK(bool(T(T(1)+T(1)) == T(2))); + } + + //! check infix `-` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixMinus(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)-T(0)) == T( 0))); + DUNE_TEST_CHECK(bool(T(T(1)-T(0)) == T( 1))); + DUNE_TEST_CHECK(bool(T(T(0)-T(1)) == T(-1))); + DUNE_TEST_CHECK(bool(T(T(1)-T(1)) == T( 0))); + } + + //! check infix `<<` + /** + * Applies to boolean and integral. + */ + template<class T> + void checkInfixLShift(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)<<T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)<<T(0)) == T(1))); + DUNE_TEST_CHECK(bool(T(T(0)<<T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)<<T(1)) == T(2))); + } + template<class T> + void checkInfixLShift(Floating) {} + + //! check infix `>>` + /** + * Applies to boolean and integral. + */ + template<class T> + void checkInfixRShift(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(T(0)>>T(0)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)>>T(0)) == T(1))); + DUNE_TEST_CHECK(bool(T(T(0)>>T(1)) == T(0))); + DUNE_TEST_CHECK(bool(T(T(1)>>T(1)) == T(0))); + } + template<class T> + void checkInfixRShift(Floating) {} + + //! check infix `<` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixLess(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + DUNE_TEST_CHECK(bool(T(0)<T(0)) == false); + DUNE_TEST_CHECK(bool(T(1)<T(0)) == false); + DUNE_TEST_CHECK(bool(T(0)<T(1)) == true ); + DUNE_TEST_CHECK(bool(T(1)<T(1)) == false); + } + template<class T> + void checkInfixLess(Signed arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + checkInfixLess<T>(Integral{}); + + DUNE_TEST_CHECK(bool(T(-1)<T( 0)) == true); + } + template<class T> + void checkInfixLess(Unsigned arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + checkInfixLess<T>(Integral{}); + + DUNE_TEST_CHECK(bool(T(-1)<T( 0)) == false); + } + + //! check infix `>` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixGreater(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + int values[] = { -1, 0, 1 }; + for(int i : values) + for(int j : values) + DUNE_TEST_CHECK(bool(T(i) > T(j)) == bool(T(j) < T(i))); + } + + //! check infix `<=` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixLessEqual(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + int values[] = { -1, 0, 1 }; + for(int i : values) + for(int j : values) + DUNE_TEST_CHECK(bool(T(i) <= T(j)) != bool(T(j) < T(i))); + } + + //! check infix `>=` + /** + * Applies to boolean, integral, and floating point. + */ + template<class T> + void checkInfixGreaterEqual(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + int values[] = { -1, 0, 1 }; + for(int i : values) + for(int j : values) + DUNE_TEST_CHECK(bool(T(i) >= T(j)) != bool(T(i) < T(j))); + } + + //! check infix `&` + /** + * Applies to boolean and integral. + */ + template<class T> + void checkInfixBitAnd(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(T(i) & T(j)) == T(i&j))); + } + template<class T> + void checkInfixBitAnd(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 2; ++i) + for(int j = 0; j < 2; ++j) + DUNE_TEST_CHECK(bool(T(T(i) & T(j)) == T(i&j))); + } + template<class T> + void checkInfixBitAnd(Floating) {} + + //! check infix `^` + /** + * Applies to boolean and integral. + */ + template<class T> + void checkInfixBitXor(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(T(i) ^ T(j)) == T(i^j))); + } + template<class T> + void checkInfixBitXor(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 2; ++i) + for(int j = 0; j < 2; ++j) + // compare the bit-flipped versions so we don't depend on the number + // of bits in T. + DUNE_TEST_CHECK(bool(T(~T(T(i) ^ T(j))) == T(~(i^j)))); + } + template<class T> + void checkInfixBitXor(Floating) {} + + //! check infix `|` + /** + * Applies to boolean and integral. + */ + template<class T> + void checkInfixBitOr(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(T(i) | T(j)) == T(i|j))); + } + template<class T> + void checkInfixBitOr(Boolean arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 2; ++i) + for(int j = 0; j < 2; ++j) + DUNE_TEST_CHECK(bool(T(T(i) | T(j)) == T(i|j))); + } + template<class T> + void checkInfixBitOr(Floating) {} + + //! check infix `&&` + /** + * Applies to boolean, integral and floating point. + */ + template<class T> + void checkInfixAnd(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(i) && T(j)) == (i && j)); + } + + //! check infix `||` + /** + * Applies to boolean, integral and floating point. + */ + template<class T> + void checkInfixOr(Arithmetic arithmetic_tag) + { + DUNE_TEST_FUNCTION(T, arithmetic_tag); + + for(int i = 0; i < 4; ++i) + for(int j = 0; j < 4; ++j) + DUNE_TEST_CHECK(bool(T(i) || T(j)) == (i || j)); + } + + // + // checks for compound assignment operators + // #define DUNE_TEST_PEEL(...) __VA_ARGS__ #define DUNE_TEST_ASSIGN(OP, name, Tag, lrange, rrange) \ -template<class T> \ -void checkAssign##name(Tag arithmetic_tag) \ -{ \ -DUNE_TEST_FUNCTION(T, arithmetic_tag); \ -\ -for(int i : { DUNE_TEST_PEEL lrange }) \ -for(int j : { DUNE_TEST_PEEL rrange }) \ -{ \ -T t(i); \ -DUNE_TEST_CHECK(bool((t OP##= T(j)) == T(T(i) OP T(j)))); \ -DUNE_TEST_CHECK(bool(t == T(T(i) OP T(j)))); \ -} \ -} + template<class T> \ + void checkAssign##name(Tag arithmetic_tag) \ + { \ + DUNE_TEST_FUNCTION(T, arithmetic_tag); \ + \ + for(int i : { DUNE_TEST_PEEL lrange }) \ + for(int j : { DUNE_TEST_PEEL rrange }) \ + { \ + T t(i); \ + DUNE_TEST_CHECK(bool((t OP##= T(j)) == T(T(i) OP T(j)))); \ + DUNE_TEST_CHECK(bool(t == T(T(i) OP T(j)))); \ + } \ + } #define DUNE_TEST_ASSIGN_DISABLE(name, Tag) \ -template<class T> \ -void checkAssign##name(Tag) {} - -DUNE_TEST_ASSIGN(*, Mul, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) -DUNE_TEST_ASSIGN(/, Div, Arithmetic, (0, 1, 2, 3), ( 1, 2, 4)) -DUNE_TEST_ASSIGN(%, Rem, Arithmetic, (0, 1, 2, 3), ( 1, 2, 3)) -DUNE_TEST_ASSIGN_DISABLE(Rem, Floating) - -DUNE_TEST_ASSIGN(+, Plus, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) -DUNE_TEST_ASSIGN(-, Minus, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) - -DUNE_TEST_ASSIGN(<<, LShift, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) -DUNE_TEST_ASSIGN(>>, RShift, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) -DUNE_TEST_ASSIGN(<<, LShift, Boolean, (0, 1 ), (0, 1 )) -DUNE_TEST_ASSIGN(>>, RShift, Boolean, (0, 1 ), (0, 1 )) -DUNE_TEST_ASSIGN_DISABLE(LShift, Floating) -DUNE_TEST_ASSIGN_DISABLE(RShift, Floating) - -DUNE_TEST_ASSIGN(&, BitAnd, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) -DUNE_TEST_ASSIGN(^, BitXor, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) -DUNE_TEST_ASSIGN(|, BitOr, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) -DUNE_TEST_ASSIGN_DISABLE(BitAnd, Floating) -DUNE_TEST_ASSIGN_DISABLE(BitXor, Floating) -DUNE_TEST_ASSIGN_DISABLE(BitOr, Floating) + template<class T> \ + void checkAssign##name(Tag) {} + + DUNE_TEST_ASSIGN(*, Mul, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(/, Div, Arithmetic, (0, 1, 2, 3), ( 1, 2, 4)) + DUNE_TEST_ASSIGN(%, Rem, Arithmetic, (0, 1, 2, 3), ( 1, 2, 3)) + DUNE_TEST_ASSIGN_DISABLE(Rem, Floating) + + DUNE_TEST_ASSIGN(+, Plus, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(-, Minus, Arithmetic, (0, 1, 2, 3), (0, 1, 2, 3)) + + DUNE_TEST_ASSIGN(<<, LShift, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(>>, RShift, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(<<, LShift, Boolean, (0, 1 ), (0, 1 )) + DUNE_TEST_ASSIGN(>>, RShift, Boolean, (0, 1 ), (0, 1 )) + DUNE_TEST_ASSIGN_DISABLE(LShift, Floating) + DUNE_TEST_ASSIGN_DISABLE(RShift, Floating) + + DUNE_TEST_ASSIGN(&, BitAnd, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(^, BitXor, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN(|, BitOr, Integral, (0, 1, 2, 3), (0, 1, 2, 3)) + DUNE_TEST_ASSIGN_DISABLE(BitAnd, Floating) + DUNE_TEST_ASSIGN_DISABLE(BitXor, Floating) + DUNE_TEST_ASSIGN_DISABLE(BitOr, Floating) #undef DUNE_TEST_ASSIGN_DISABLE #undef DUNE_TEST_ASSIGN @@ -712,87 +712,87 @@ DUNE_TEST_ASSIGN_DISABLE(BitOr, Floating) #undef DUNE_TEST_FUNCTION #undef DUNE_TEST_CHECK -// -// checks collections -// - -//! run the full arithmetic type testsuite -/** -* `T` is the type to check. `Tag` determines the arithmetic category; it -* is one of the classes derived from `Arithmetic`. Alternatively, `Tag` -* may be one of the fundamental arithmetic types, in which case it will -* be converted to the appropriate category tag automatically. -* -* To check an extended unsigned integer type, you might use one of the -* following calls: -* \code -* test.checkArithmetic<MyExtUnsigned, unsigned>(); -* test.checkArithmetic<MyExtUnsigned>(0u); -* test.checkArithmetic<MyExtUnsigned, ArithemticTestSuite::Unsigned>(); -* test.checkArithmetic<MyExtUnsigned>(ArithemticTestSuite::Unsigned{}); -* \endcode -*/ -template<class T, class Tag> -void checkArithmetic(Tag = Tag{}) -{ -auto arithmetic_tag = this->tag<Tag>(); - -checkDefaultConstruct<T>(arithmetic_tag); -checkExplicitIntConvert<T>(arithmetic_tag); -checkMoveConstruct<T>(arithmetic_tag); -checkCopyConstruct<T>(arithmetic_tag); -checkMoveAssign<T>(arithmetic_tag); -checkCopyAssign<T>(arithmetic_tag); -checkEqual<T>(arithmetic_tag); - -checkPostfixInc<T>(arithmetic_tag); -checkPostfixDec<T>(arithmetic_tag); - -checkPrefixPlus<T>(arithmetic_tag); -checkPrefixMinus<T>(arithmetic_tag); -checkPrefixNot<T>(arithmetic_tag); -checkPrefixBitNot<T>(arithmetic_tag); - -checkPrefixInc<T>(arithmetic_tag); -checkPrefixDec<T>(arithmetic_tag); - -checkInfixMul<T>(arithmetic_tag); -checkInfixDiv<T>(arithmetic_tag); -checkInfixRem<T>(arithmetic_tag); - -checkInfixPlus<T>(arithmetic_tag); -checkInfixMinus<T>(arithmetic_tag); - -checkInfixLShift<T>(arithmetic_tag); -checkInfixRShift<T>(arithmetic_tag); - -checkInfixLess<T>(arithmetic_tag); -checkInfixGreater<T>(arithmetic_tag); -checkInfixLessEqual<T>(arithmetic_tag); -checkInfixGreaterEqual<T>(arithmetic_tag); - -checkInfixBitAnd<T>(arithmetic_tag); -checkInfixBitXor<T>(arithmetic_tag); -checkInfixBitOr<T>(arithmetic_tag); - -checkInfixAnd<T>(arithmetic_tag); -checkInfixOr<T>(arithmetic_tag); - -checkAssignMul<T>(arithmetic_tag); -checkAssignDiv<T>(arithmetic_tag); -checkAssignRem<T>(arithmetic_tag); - -checkAssignPlus<T>(arithmetic_tag); -checkAssignMinus<T>(arithmetic_tag); - -checkAssignLShift<T>(arithmetic_tag); -checkAssignRShift<T>(arithmetic_tag); - -checkAssignBitAnd<T>(arithmetic_tag); -checkAssignBitXor<T>(arithmetic_tag); -checkAssignBitOr<T>(arithmetic_tag); -} -}; + // + // checks collections + // + + //! run the full arithmetic type testsuite + /** + * `T` is the type to check. `Tag` determines the arithmetic category; it + * is one of the classes derived from `Arithmetic`. Alternatively, `Tag` + * may be one of the fundamental arithmetic types, in which case it will + * be converted to the appropriate category tag automatically. + * + * To check an extended unsigned integer type, you might use one of the + * following calls: + * \code + * test.checkArithmetic<MyExtUnsigned, unsigned>(); + * test.checkArithmetic<MyExtUnsigned>(0u); + * test.checkArithmetic<MyExtUnsigned, ArithemticTestSuite::Unsigned>(); + * test.checkArithmetic<MyExtUnsigned>(ArithemticTestSuite::Unsigned{}); + * \endcode + */ + template<class T, class Tag> + void checkArithmetic(Tag = Tag{}) + { + auto arithmetic_tag = this->tag<Tag>(); + + checkDefaultConstruct<T>(arithmetic_tag); + checkExplicitIntConvert<T>(arithmetic_tag); + checkMoveConstruct<T>(arithmetic_tag); + checkCopyConstruct<T>(arithmetic_tag); + checkMoveAssign<T>(arithmetic_tag); + checkCopyAssign<T>(arithmetic_tag); + checkEqual<T>(arithmetic_tag); + + checkPostfixInc<T>(arithmetic_tag); + checkPostfixDec<T>(arithmetic_tag); + + checkPrefixPlus<T>(arithmetic_tag); + checkPrefixMinus<T>(arithmetic_tag); + checkPrefixNot<T>(arithmetic_tag); + checkPrefixBitNot<T>(arithmetic_tag); + + checkPrefixInc<T>(arithmetic_tag); + checkPrefixDec<T>(arithmetic_tag); + + checkInfixMul<T>(arithmetic_tag); + checkInfixDiv<T>(arithmetic_tag); + checkInfixRem<T>(arithmetic_tag); + + checkInfixPlus<T>(arithmetic_tag); + checkInfixMinus<T>(arithmetic_tag); + + checkInfixLShift<T>(arithmetic_tag); + checkInfixRShift<T>(arithmetic_tag); + + checkInfixLess<T>(arithmetic_tag); + checkInfixGreater<T>(arithmetic_tag); + checkInfixLessEqual<T>(arithmetic_tag); + checkInfixGreaterEqual<T>(arithmetic_tag); + + checkInfixBitAnd<T>(arithmetic_tag); + checkInfixBitXor<T>(arithmetic_tag); + checkInfixBitOr<T>(arithmetic_tag); + + checkInfixAnd<T>(arithmetic_tag); + checkInfixOr<T>(arithmetic_tag); + + checkAssignMul<T>(arithmetic_tag); + checkAssignDiv<T>(arithmetic_tag); + checkAssignRem<T>(arithmetic_tag); + + checkAssignPlus<T>(arithmetic_tag); + checkAssignMinus<T>(arithmetic_tag); + + checkAssignLShift<T>(arithmetic_tag); + checkAssignRShift<T>(arithmetic_tag); + + checkAssignBitAnd<T>(arithmetic_tag); + checkAssignBitXor<T>(arithmetic_tag); + checkAssignBitOr<T>(arithmetic_tag); + } + }; #if __GNUC__ >= 7 # pragma GCC diagnostic pop diff --git a/dune/common/test/checkmatrixinterface.hh b/dune/common/test/checkmatrixinterface.hh index fd6a320ab0020dbc6d4d9719c6aa53cead3cd57c..5dcc1ba378e79fc0d6b3534adb1f7eea4cafe210 100644 --- a/dune/common/test/checkmatrixinterface.hh +++ b/dune/common/test/checkmatrixinterface.hh @@ -14,20 +14,20 @@ /* -* @file -* @brief This file provides an interface check for dense matrices. -* @author Christoph Gersbacher -*/ + * @file + * @brief This file provides an interface check for dense matrices. + * @author Christoph Gersbacher + */ namespace Dune { -// External forward declarations for namespace Dune -// ------------------------------------------------ + // External forward declarations for namespace Dune + // ------------------------------------------------ -template< class, int, int > class FieldMatrix; -template< class, int > class DiagonalMatrix; + template< class, int, int > class FieldMatrix; + template< class, int > class DiagonalMatrix; } // namespace Dune @@ -36,377 +36,377 @@ template< class, int > class DiagonalMatrix; namespace CheckMatrixInterface { -namespace Capabilities -{ - -// hasStaticSizes -// -------------- - -template< class Matrix > -struct hasStaticSizes -{ -static const bool v = false; -static const int rows = ~0; -static const int cols = ~0; -}; - -template< class Matrix > -struct hasStaticSizes< const Matrix > -{ -static const bool v = hasStaticSizes< Matrix >::v; -static const int rows = hasStaticSizes< Matrix >::rows; -static const int cols = hasStaticSizes< Matrix >::cols; -}; - - - -// isSquare -// -------- - -template< class Matrix > -struct isSquare -{ -static const bool v = false; -}; - -template< class Matrix > -struct isSquare< const Matrix > -{ -static const bool v = isSquare< Matrix >::v; -}; - - - -// Template specializations for Dune::FieldMatrix -// ---------------------------------------------- - -template< class K, int r, int c > -struct hasStaticSizes< Dune::FieldMatrix< K, r, c > > -{ -static const bool v = true; -static const int rows = r; -static const int cols = c; -}; - -template< class K, int rows, int cols > -struct isSquare< Dune::FieldMatrix< K, rows, cols > > -{ -static const bool v = ( rows == cols ); -}; - - - -// Template specializations for Dune::DiagonalMatrix -// ------------------------------------------------- - -template< class K, int n > -struct hasStaticSizes< Dune::DiagonalMatrix<K,n> > -{ -static const bool v = true; -static const int rows = n; -static const int cols = n; -}; - -template< class K, int n > -struct isSquare< Dune::DiagonalMatrix<K,n> > -{ -static const bool v = true; -}; - -} // namespace Capabilities - - - -// UseDynamicVector -// ---------------- - -template< class Matrix > -struct UseDynamicVector -{ -typedef typename Matrix::value_type value_type; - -typedef Dune::DynamicVector< value_type > domain_type; -typedef domain_type range_type; + namespace Capabilities + { -static domain_type domain ( const Matrix &matrix, value_type v = value_type() ) -{ -return domain_type( matrix.M(), v ); -} + // hasStaticSizes + // -------------- -static range_type range ( const Matrix &matrix, value_type v = value_type() ) -{ -return range_type( matrix.N(), v ); -} -}; + template< class Matrix > + struct hasStaticSizes + { + static const bool v = false; + static const int rows = ~0; + static const int cols = ~0; + }; + template< class Matrix > + struct hasStaticSizes< const Matrix > + { + static const bool v = hasStaticSizes< Matrix >::v; + static const int rows = hasStaticSizes< Matrix >::rows; + static const int cols = hasStaticSizes< Matrix >::cols; + }; -// UseFieldVector -// -------------- -template< class K, int rows, int cols > -struct UseFieldVector -{ -typedef K value_type; + // isSquare + // -------- -typedef Dune::FieldVector< K, cols > domain_type; -typedef Dune::FieldVector< K, rows > range_type; + template< class Matrix > + struct isSquare + { + static const bool v = false; + }; -template< class Matrix > -static domain_type domain ( const Matrix &, value_type v = value_type() ) -{ -return domain_type( v ); -} + template< class Matrix > + struct isSquare< const Matrix > + { + static const bool v = isSquare< Matrix >::v; + }; -template< class Matrix > -static range_type range ( const Matrix &, value_type v = value_type() ) -{ -return range_type( v ); -} -}; + // Template specializations for Dune::FieldMatrix + // ---------------------------------------------- -// MatrixSizeHelper -// ---------------- + template< class K, int r, int c > + struct hasStaticSizes< Dune::FieldMatrix< K, r, c > > + { + static const bool v = true; + static const int rows = r; + static const int cols = c; + }; -template< class Matrix, bool hasStaticSizes = Capabilities::hasStaticSizes< Matrix >::v > -struct MatrixSizeHelper; + template< class K, int rows, int cols > + struct isSquare< Dune::FieldMatrix< K, rows, cols > > + { + static const bool v = ( rows == cols ); + }; -template< class Matrix > -struct MatrixSizeHelper< Matrix, false > -{ -typedef typename Matrix::size_type size_type; -static const size_type rows ( const Matrix &matrix ) { return matrix.rows(); } -static const size_type cols ( const Matrix &matrix ) { return matrix.cols(); } -}; -template< class Matrix > -struct MatrixSizeHelper< Matrix, true > -{ -typedef typename Matrix::size_type size_type; -static const size_type rows ( const Matrix & ) { return Matrix::rows; } -static const size_type cols ( const Matrix & ) { return Matrix::cols; } -}; + // Template specializations for Dune::DiagonalMatrix + // ------------------------------------------------- + template< class K, int n > + struct hasStaticSizes< Dune::DiagonalMatrix<K,n> > + { + static const bool v = true; + static const int rows = n; + static const int cols = n; + }; -// CheckIfSquareMatrix -// ------------------- + template< class K, int n > + struct isSquare< Dune::DiagonalMatrix<K,n> > + { + static const bool v = true; + }; -template< class Matrix, class Traits, bool isSquare = Capabilities::isSquare< Matrix >::v > -struct CheckIfSquareMatrix; + } // namespace Capabilities -template< class Matrix, class Traits > -struct CheckIfSquareMatrix< Matrix, Traits, false > -{ -static void apply ( const Matrix &) {} -static void apply ( Matrix &) {} -}; -template< class Matrix, class Traits > -struct CheckIfSquareMatrix< Matrix, Traits, true > -{ -typedef typename Matrix::value_type value_type; + // UseDynamicVector + // ---------------- -static value_type tolerance () -{ -return value_type( 16 ) * std::numeric_limits< value_type >::epsilon(); -} - -static void apply ( const Matrix &matrix ) -{ -const value_type determinant = matrix.determinant(); -if( determinant > tolerance() ) -{ -typename Traits::domain_type x = Traits::domain( matrix ); -const typename Traits::range_type b = Traits::range( matrix ); -matrix.solve( x, b ); -} -} - -static void apply ( Matrix &matrix ) -{ -apply( const_cast< const Matrix & >( matrix ) ); -if( matrix.determinant() > tolerance() ) -matrix.invert(); -} -}; + template< class Matrix > + struct UseDynamicVector + { + typedef typename Matrix::value_type value_type; + typedef Dune::DynamicVector< value_type > domain_type; + typedef domain_type range_type; + static domain_type domain ( const Matrix &matrix, value_type v = value_type() ) + { + return domain_type( matrix.M(), v ); + } -// CheckConstMatrix -// ---------------- - -template< class Matrix, class Traits > -struct CheckConstMatrix -{ -// required type definitions -typedef typename Matrix::size_type size_type; + static range_type range ( const Matrix &matrix, value_type v = value_type() ) + { + return range_type( matrix.N(), v ); + } + }; -typedef typename Matrix::value_type value_type; -typedef typename Matrix::field_type field_type; -typedef typename Matrix::block_type block_type; -typedef typename Matrix::const_row_reference const_row_reference; -typedef typename Matrix::ConstIterator ConstIterator; + // UseFieldVector + // -------------- -static void apply ( const Matrix &matrix ) -{ -checkDataAccess ( matrix ); -checkIterators ( matrix ); -checkLinearAlgebra ( matrix ); -checkNorms ( matrix ); -checkSizes ( matrix ); -CheckIfSquareMatrix< Matrix, Traits >::apply( matrix ); - -// TODO: check comparison -// bool operator == ( const Matrix &other ); -// bool operator != ( const Matrix &other ); -} - -// check size methods -static void checkSizes ( const Matrix &matrix ) -{ -DUNE_UNUSED const size_type size = matrix.size(); -const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); -const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); -const size_type N = matrix.N(); -const size_type M = matrix.M(); - -if( N != rows || M != cols ) -DUNE_THROW( Dune::RangeError, "Returned inconsistent sizes." ); -} - -// check read-only access to data -static void checkDataAccess ( const Matrix &matrix ) -{ -const size_type size = matrix.size(); -for( size_type i = size_type( 0 ); i < size; ++i ) -DUNE_UNUSED const_row_reference row = matrix[ i ]; + template< class K, int rows, int cols > + struct UseFieldVector + { + typedef K value_type; -const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); -const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); -for( size_type i = size_type( 0 ); i < rows; ++i ) -{ -for( size_type j = size_type( 0 ); j < cols; ++j ) -{ -DUNE_UNUSED bool exists = matrix.exists( i, j ); -DUNE_UNUSED const value_type &value = matrix[ i ][ j ]; -} -} -} - -// check norms -static void checkNorms ( const Matrix &matrix ) -{ -typedef typename Dune::FieldTraits< value_type >::real_type real_type; -real_type frobenius_norm = matrix.frobenius_norm(); -real_type frobenius_norm2 = matrix.frobenius_norm2(); -real_type infinity_norm = matrix.infinity_norm() ; -real_type infinity_norm_real = matrix.infinity_norm_real(); - -if( std::min( std::min( frobenius_norm, frobenius_norm2 ), -std::min( infinity_norm, infinity_norm_real ) ) < real_type( 0 ) ) -DUNE_THROW( Dune::InvalidStateException, "Norms must return non-negative value." ); -} - -// check basic linear algebra methods -static void checkLinearAlgebra ( const Matrix &matrix ) -{ -typename Traits::domain_type domain = Traits::domain( matrix ); -typename Traits::range_type range = Traits::range( matrix ); -typename Traits::value_type alpha( 1 ); - -matrix.mv( domain, range ); -matrix.mtv( range, domain ); -matrix.umv( domain, range ); -matrix.umtv( range, domain ); -matrix.umhv( range, domain ); -matrix.mmv( domain, range ); -matrix.mmtv( range, domain ); -matrix.mmhv( range, domain ); -matrix.usmv( alpha, domain, range ); -matrix.usmtv( alpha, range, domain ); -matrix.usmhv( alpha, range, domain ); -} - -// check iterator methods -static void checkIterators ( const Matrix &matrix ) -{ -const ConstIterator end = matrix.end(); -for( ConstIterator it = matrix.begin(); it != end; ++it ) -DUNE_UNUSED const_row_reference row = *it; -} -}; + typedef Dune::FieldVector< K, cols > domain_type; + typedef Dune::FieldVector< K, rows > range_type; + template< class Matrix > + static domain_type domain ( const Matrix &, value_type v = value_type() ) + { + return domain_type( v ); + } + template< class Matrix > + static range_type range ( const Matrix &, value_type v = value_type() ) + { + return range_type( v ); + } + }; -// CheckNonConstMatrix -// ------------------- -template< class Matrix, class Traits > -struct CheckNonConstMatrix -{ -// required type definitions -typedef typename Matrix::size_type size_type; -typedef typename Matrix::value_type value_type; -typedef typename Matrix::row_reference row_reference; -typedef typename Matrix::row_type row_type; -typedef typename Matrix::Iterator Iterator; - -static void apply ( Matrix &matrix ) -{ -checkIterators( matrix ); -checkAssignment( matrix ); - -CheckIfSquareMatrix< Matrix, Traits >::apply( matrix ); - -// TODO: check scalar/matrix and matrix/matrix operations -// Matrix &operator+= ( const Matrix &other ); -// Matrix &operator-= ( const Matrix &other ); -// Matrix &operator*= ( const value_type &v ); -// Matrix &operator/= ( const value_type &v ); -// Matrix &axpy += ( const value_type &v, const Matrix &other ); -// Matrix &axpy += ( const value_type &v, const Matrix &other ); -// Matrix &leftmultiply ( const Matrix &other ); -// Matrix &rightmultiply ( const Matrix &other ); -} - -// check assignment -static void checkAssignment ( Matrix &matrix ) -{ -matrix = value_type( 1 ); -const size_type size = matrix.size(); -for( size_type i = size_type( 0 ); i < size; ++i ) -{ -row_reference row = matrix[ i ]; -row = row_type( value_type( 0 ) ); -} + // MatrixSizeHelper + // ---------------- -const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); -const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); -for( size_type i = size_type( 0 ); i < rows; ++i ) -{ -for( size_type j = size_type( 0 ); j < cols; ++j ) -matrix[ i ][ j ] = ( i == j ? value_type( 1 ) : value_type( 0 ) ); -} -} + template< class Matrix, bool hasStaticSizes = Capabilities::hasStaticSizes< Matrix >::v > + struct MatrixSizeHelper; -// check iterator methods -static void checkIterators ( Matrix &matrix ) -{ -const Iterator end = matrix.end(); -for( Iterator it = matrix.begin(); it != end; ++it ) -{ -row_reference row = *it; -row = row_type( value_type( 0 ) ); -} -} -}; + template< class Matrix > + struct MatrixSizeHelper< Matrix, false > + { + typedef typename Matrix::size_type size_type; + static const size_type rows ( const Matrix &matrix ) { return matrix.rows(); } + static const size_type cols ( const Matrix &matrix ) { return matrix.cols(); } + }; + + template< class Matrix > + struct MatrixSizeHelper< Matrix, true > + { + typedef typename Matrix::size_type size_type; + static const size_type rows ( const Matrix & ) { return Matrix::rows; } + static const size_type cols ( const Matrix & ) { return Matrix::cols; } + }; + + + + // CheckIfSquareMatrix + // ------------------- + + template< class Matrix, class Traits, bool isSquare = Capabilities::isSquare< Matrix >::v > + struct CheckIfSquareMatrix; + + template< class Matrix, class Traits > + struct CheckIfSquareMatrix< Matrix, Traits, false > + { + static void apply ( const Matrix &) {} + + static void apply ( Matrix &) {} + }; + + template< class Matrix, class Traits > + struct CheckIfSquareMatrix< Matrix, Traits, true > + { + typedef typename Matrix::value_type value_type; + + static value_type tolerance () + { + return value_type( 16 ) * std::numeric_limits< value_type >::epsilon(); + } + + static void apply ( const Matrix &matrix ) + { + const value_type determinant = matrix.determinant(); + if( determinant > tolerance() ) + { + typename Traits::domain_type x = Traits::domain( matrix ); + const typename Traits::range_type b = Traits::range( matrix ); + matrix.solve( x, b ); + } + } + + static void apply ( Matrix &matrix ) + { + apply( const_cast< const Matrix & >( matrix ) ); + if( matrix.determinant() > tolerance() ) + matrix.invert(); + } + }; + + + + // CheckConstMatrix + // ---------------- + + template< class Matrix, class Traits > + struct CheckConstMatrix + { + // required type definitions + typedef typename Matrix::size_type size_type; + + typedef typename Matrix::value_type value_type; + typedef typename Matrix::field_type field_type; + typedef typename Matrix::block_type block_type; + + typedef typename Matrix::const_row_reference const_row_reference; + + typedef typename Matrix::ConstIterator ConstIterator; + + static void apply ( const Matrix &matrix ) + { + checkDataAccess ( matrix ); + checkIterators ( matrix ); + checkLinearAlgebra ( matrix ); + checkNorms ( matrix ); + checkSizes ( matrix ); + CheckIfSquareMatrix< Matrix, Traits >::apply( matrix ); + + // TODO: check comparison + // bool operator == ( const Matrix &other ); + // bool operator != ( const Matrix &other ); + } + + // check size methods + static void checkSizes ( const Matrix &matrix ) + { + DUNE_UNUSED const size_type size = matrix.size(); + const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); + const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); + const size_type N = matrix.N(); + const size_type M = matrix.M(); + + if( N != rows || M != cols ) + DUNE_THROW( Dune::RangeError, "Returned inconsistent sizes." ); + } + + // check read-only access to data + static void checkDataAccess ( const Matrix &matrix ) + { + const size_type size = matrix.size(); + for( size_type i = size_type( 0 ); i < size; ++i ) + DUNE_UNUSED const_row_reference row = matrix[ i ]; + + const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); + const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); + for( size_type i = size_type( 0 ); i < rows; ++i ) + { + for( size_type j = size_type( 0 ); j < cols; ++j ) + { + DUNE_UNUSED bool exists = matrix.exists( i, j ); + DUNE_UNUSED const value_type &value = matrix[ i ][ j ]; + } + } + } + + // check norms + static void checkNorms ( const Matrix &matrix ) + { + typedef typename Dune::FieldTraits< value_type >::real_type real_type; + real_type frobenius_norm = matrix.frobenius_norm(); + real_type frobenius_norm2 = matrix.frobenius_norm2(); + real_type infinity_norm = matrix.infinity_norm() ; + real_type infinity_norm_real = matrix.infinity_norm_real(); + + if( std::min( std::min( frobenius_norm, frobenius_norm2 ), + std::min( infinity_norm, infinity_norm_real ) ) < real_type( 0 ) ) + DUNE_THROW( Dune::InvalidStateException, "Norms must return non-negative value." ); + } + + // check basic linear algebra methods + static void checkLinearAlgebra ( const Matrix &matrix ) + { + typename Traits::domain_type domain = Traits::domain( matrix ); + typename Traits::range_type range = Traits::range( matrix ); + typename Traits::value_type alpha( 1 ); + + matrix.mv( domain, range ); + matrix.mtv( range, domain ); + matrix.umv( domain, range ); + matrix.umtv( range, domain ); + matrix.umhv( range, domain ); + matrix.mmv( domain, range ); + matrix.mmtv( range, domain ); + matrix.mmhv( range, domain ); + matrix.usmv( alpha, domain, range ); + matrix.usmtv( alpha, range, domain ); + matrix.usmhv( alpha, range, domain ); + } + + // check iterator methods + static void checkIterators ( const Matrix &matrix ) + { + const ConstIterator end = matrix.end(); + for( ConstIterator it = matrix.begin(); it != end; ++it ) + DUNE_UNUSED const_row_reference row = *it; + } + }; + + + + // CheckNonConstMatrix + // ------------------- + + template< class Matrix, class Traits > + struct CheckNonConstMatrix + { + // required type definitions + typedef typename Matrix::size_type size_type; + typedef typename Matrix::value_type value_type; + typedef typename Matrix::row_reference row_reference; + typedef typename Matrix::row_type row_type; + typedef typename Matrix::Iterator Iterator; + + static void apply ( Matrix &matrix ) + { + checkIterators( matrix ); + checkAssignment( matrix ); + + CheckIfSquareMatrix< Matrix, Traits >::apply( matrix ); + + // TODO: check scalar/matrix and matrix/matrix operations + // Matrix &operator+= ( const Matrix &other ); + // Matrix &operator-= ( const Matrix &other ); + // Matrix &operator*= ( const value_type &v ); + // Matrix &operator/= ( const value_type &v ); + // Matrix &axpy += ( const value_type &v, const Matrix &other ); + // Matrix &axpy += ( const value_type &v, const Matrix &other ); + // Matrix &leftmultiply ( const Matrix &other ); + // Matrix &rightmultiply ( const Matrix &other ); + } + + // check assignment + static void checkAssignment ( Matrix &matrix ) + { + matrix = value_type( 1 ); + + const size_type size = matrix.size(); + for( size_type i = size_type( 0 ); i < size; ++i ) + { + row_reference row = matrix[ i ]; + row = row_type( value_type( 0 ) ); + } + + const size_type rows = MatrixSizeHelper< Matrix >::rows( matrix ); + const size_type cols = MatrixSizeHelper< Matrix >::cols( matrix ); + for( size_type i = size_type( 0 ); i < rows; ++i ) + { + for( size_type j = size_type( 0 ); j < cols; ++j ) + matrix[ i ][ j ] = ( i == j ? value_type( 1 ) : value_type( 0 ) ); + } + } + + // check iterator methods + static void checkIterators ( Matrix &matrix ) + { + const Iterator end = matrix.end(); + for( Iterator it = matrix.begin(); it != end; ++it ) + { + row_reference row = *it; + row = row_type( value_type( 0 ) ); + } + } + }; } // namespace CheckMatrixInterface @@ -415,21 +415,21 @@ row = row_type( value_type( 0 ) ); namespace Dune { -// checkMatrixInterface -// -------------------- + // checkMatrixInterface + // -------------------- -template< class Matrix, class Traits = CheckMatrixInterface::UseDynamicVector< Matrix > > -void checkMatrixInterface ( const Matrix &matrix ) -{ -CheckMatrixInterface::CheckConstMatrix< Matrix, Traits >::apply( matrix ); -} + template< class Matrix, class Traits = CheckMatrixInterface::UseDynamicVector< Matrix > > + void checkMatrixInterface ( const Matrix &matrix ) + { + CheckMatrixInterface::CheckConstMatrix< Matrix, Traits >::apply( matrix ); + } -template< class Matrix, class Traits = CheckMatrixInterface::UseDynamicVector< Matrix > > -void checkMatrixInterface ( Matrix &matrix ) -{ -checkMatrixInterface( const_cast< const Matrix & >( matrix ) ); -CheckMatrixInterface::CheckNonConstMatrix< Matrix, Traits >::apply( matrix ); -} + template< class Matrix, class Traits = CheckMatrixInterface::UseDynamicVector< Matrix > > + void checkMatrixInterface ( Matrix &matrix ) + { + checkMatrixInterface( const_cast< const Matrix & >( matrix ) ); + CheckMatrixInterface::CheckNonConstMatrix< Matrix, Traits >::apply( matrix ); + } } // namespace Dune diff --git a/dune/common/test/collectorstream.hh b/dune/common/test/collectorstream.hh index def6fb76bdee2a92c3bbff72b59d7b32d3c6ef58..eea38638a9b7ec590dc6e3fbc1932e80d7e8c044 100644 --- a/dune/common/test/collectorstream.hh +++ b/dune/common/test/collectorstream.hh @@ -17,61 +17,61 @@ namespace Dune { /** -* \brief Data collector stream -* -* A class derived from std::ostringstream that allows to -* collect data via a temporary returned object. To facilitate -* this it stores a callback that is used to pass the collected -* data to its creator on destruction. -* -* In order to avoid passing the same data twice, copy construction -* is forbidden and only move construction is allowed. -*/ + * \brief Data collector stream + * + * A class derived from std::ostringstream that allows to + * collect data via a temporary returned object. To facilitate + * this it stores a callback that is used to pass the collected + * data to its creator on destruction. + * + * In order to avoid passing the same data twice, copy construction + * is forbidden and only move construction is allowed. + */ class CollectorStream : public std::ostringstream { public: -/** -* \brief Create from callback -* -* \tparam CallBack Type of callback. Must be convertible to std::function<void(std::string)> -* \param callBack A copy of this function will be stored and called on destruction. -*/ -template<class CallBack, -Dune::disableCopyMove<CollectorStream, CallBack> = 0> -CollectorStream(CallBack&& callBack) : -callBack_(callBack) -{} - -CollectorStream(const CollectorStream& other) = delete; - -/** -* \brief Move constructor -* -* This will take over the data and callback from the -* moved from CollectorStream and disable the callback -* in the latter. -*/ -CollectorStream(CollectorStream&& other) : -callBack_(other.callBack_) -{ -(*this) << other.str(); -other.callBack_ = [](std::string){}; -} - -/** -* \brief Destructor -* -* This calls the callback function given on creation -* passing all collected data as a single string argument. -*/ -~CollectorStream() -{ -callBack_(this->str()); -} + /** + * \brief Create from callback + * + * \tparam CallBack Type of callback. Must be convertible to std::function<void(std::string)> + * \param callBack A copy of this function will be stored and called on destruction. + */ + template<class CallBack, + Dune::disableCopyMove<CollectorStream, CallBack> = 0> + CollectorStream(CallBack&& callBack) : + callBack_(callBack) + {} + + CollectorStream(const CollectorStream& other) = delete; + + /** + * \brief Move constructor + * + * This will take over the data and callback from the + * moved from CollectorStream and disable the callback + * in the latter. + */ + CollectorStream(CollectorStream&& other) : + callBack_(other.callBack_) + { + (*this) << other.str(); + other.callBack_ = [](std::string){}; + } + + /** + * \brief Destructor + * + * This calls the callback function given on creation + * passing all collected data as a single string argument. + */ + ~CollectorStream() + { + callBack_(this->str()); + } private: -std::function<void(std::string)> callBack_; + std::function<void(std::string)> callBack_; }; diff --git a/dune/common/test/dummyiterator.hh b/dune/common/test/dummyiterator.hh index a78b88acf6d3e35d7e4fe606818c74aa38f321a9..2645ad0e8dd11085dfc719975221db98f94872cb 100644 --- a/dune/common/test/dummyiterator.hh +++ b/dune/common/test/dummyiterator.hh @@ -11,35 +11,35 @@ template<typename T> class dummyiterator -: public Dune::BidirectionalIteratorFacade<dummyiterator<T>, T, T&, -std::ptrdiff_t> + : public Dune::BidirectionalIteratorFacade<dummyiterator<T>, T, T&, + std::ptrdiff_t> { -friend class dummyiterator<const typename std::remove_const<T>::type>; + friend class dummyiterator<const typename std::remove_const<T>::type>; -T *value; + T *value; public: -dummyiterator(T& value_) -: value(&value_) -{} - -template<typename T2> -dummyiterator -( const dummyiterator<T2>& o, -typename std::enable_if<std::is_convertible<T2&, T&>::value>::type* = 0) -: value(o.value) -{} - -T& derefence() const { -return *value; -} - -bool equals(const dummyiterator& o) const { -return value == o.value; -} - -void increment() {} -void decrement() {} + dummyiterator(T& value_) + : value(&value_) + {} + + template<typename T2> + dummyiterator + ( const dummyiterator<T2>& o, + typename std::enable_if<std::is_convertible<T2&, T&>::value>::type* = 0) + : value(o.value) + {} + + T& derefence() const { + return *value; + } + + bool equals(const dummyiterator& o) const { + return value == o.value; + } + + void increment() {} + void decrement() {} }; #endif // DUNE_COMMON_DUMMYITERATOR_HH diff --git a/dune/common/test/iteratorfacadetest.hh b/dune/common/test/iteratorfacadetest.hh index b3f46ae903e45a4960ef24690caa7099a48eca7e..bfbbfafc0cf81d584e3600aa2334b5ee844d0680 100644 --- a/dune/common/test/iteratorfacadetest.hh +++ b/dune/common/test/iteratorfacadetest.hh @@ -8,44 +8,44 @@ #include <dune/common/typetraits.hh> template<class T, -template<class,class,class,class> class IteratorFacade=Dune::RandomAccessIteratorFacade> + template<class,class,class,class> class IteratorFacade=Dune::RandomAccessIteratorFacade> class TestContainer { public: -typedef Dune::GenericIterator<TestContainer<T,IteratorFacade>,T,T&,std::ptrdiff_t,IteratorFacade> iterator; + typedef Dune::GenericIterator<TestContainer<T,IteratorFacade>,T,T&,std::ptrdiff_t,IteratorFacade> iterator; -typedef Dune::GenericIterator<const TestContainer<T,IteratorFacade>,const T,const T&,std::ptrdiff_t,IteratorFacade> const_iterator; + typedef Dune::GenericIterator<const TestContainer<T,IteratorFacade>,const T,const T&,std::ptrdiff_t,IteratorFacade> const_iterator; -TestContainer(){ -for(int i=0; i < 100; i++) -values_[i]=i; -} + TestContainer(){ + for(int i=0; i < 100; i++) + values_[i]=i; + } -iterator begin(){ -return iterator(*this, 0); -} + iterator begin(){ + return iterator(*this, 0); + } -const_iterator begin() const { -return const_iterator(*this, 0); -} + const_iterator begin() const { + return const_iterator(*this, 0); + } -iterator end(){ -return iterator(*this, 100); -} + iterator end(){ + return iterator(*this, 100); + } -const_iterator end() const { -return const_iterator(*this, 100); -} + const_iterator end() const { + return const_iterator(*this, 100); + } -T& operator[](int i){ -return values_[i]; -} + T& operator[](int i){ + return values_[i]; + } -const T& operator[](int i) const { -return values_[i]; -} + const T& operator[](int i) const { + return values_[i]; + } private: -T values_[100]; + T values_[100]; }; #endif diff --git a/dune/common/test/iteratortest.hh b/dune/common/test/iteratortest.hh index 9df8da6496a7e22657935ac431e00908c18bd0dd..28a0531b337f9c8ca71225289556ee73482a67b5 100644 --- a/dune/common/test/iteratortest.hh +++ b/dune/common/test/iteratortest.hh @@ -11,271 +11,271 @@ #include <dune/common/unused.hh> /** -* @brief Test whether the class Iter implements the interface of an STL output iterator -* -* @param iterator Iterator to test -* @param iterations Number of times that 'iterator' can be safely incremented -* @param value A value that is sent to the output iterator -*/ + * @brief Test whether the class Iter implements the interface of an STL output iterator + * + * @param iterator Iterator to test + * @param iterations Number of times that 'iterator' can be safely incremented + * @param value A value that is sent to the output iterator + */ template<class Iter, class Value> void testOutputIterator(Iter iterator, std::size_t iterations, Value value) { -// Test whether iterator is copy-constructible -// The new iterator object will go out of scope at the end of this method, and hence -// destructibility will also be tested. -Iter tmp1(iterator); - -// Test whether iterator is copy-assignable -Iter tmp2 = iterator; - -// Test whether pre-increment and assignment works -for (size_t i=0; i<iterations; ++i, ++tmp1) -// An output iterator can only be dereferenced as an lvalue (if in a dereferenceable state). -// It shall only be dereferenced as the left-side of an assignment statement. -*tmp1 = value; - -// Test whether post-increment and assignment works -for (size_t i=0; i<iterations; ++i, tmp2++) -*tmp2 = value; - -// Test whether std::iterator_traits is properly specialized -// The AlwaysTrue<A> construction allows one to test whether the type A exists at all, -// without assuming anything further about A. -static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::difference_type>::value, -"std::iterator_traits::difference_type is not defined!"); -static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::value_type>::value, -"std::iterator_traits::value_type is not defined!"); -static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::pointer>::value, -"std::iterator_traits::pointer is not defined!"); -static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::reference>::value, -"std::iterator_traits::reference is not defined!"); - -// Make sure the iterator_category is properly set -static_assert(std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::output_iterator_tag>::value, -"std::iterator_traits::iterator_category is not properly defined!"); + // Test whether iterator is copy-constructible + // The new iterator object will go out of scope at the end of this method, and hence + // destructibility will also be tested. + Iter tmp1(iterator); + + // Test whether iterator is copy-assignable + Iter tmp2 = iterator; + + // Test whether pre-increment and assignment works + for (size_t i=0; i<iterations; ++i, ++tmp1) + // An output iterator can only be dereferenced as an lvalue (if in a dereferenceable state). + // It shall only be dereferenced as the left-side of an assignment statement. + *tmp1 = value; + + // Test whether post-increment and assignment works + for (size_t i=0; i<iterations; ++i, tmp2++) + *tmp2 = value; + + // Test whether std::iterator_traits is properly specialized + // The AlwaysTrue<A> construction allows one to test whether the type A exists at all, + // without assuming anything further about A. + static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::difference_type>::value, + "std::iterator_traits::difference_type is not defined!"); + static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::value_type>::value, + "std::iterator_traits::value_type is not defined!"); + static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::pointer>::value, + "std::iterator_traits::pointer is not defined!"); + static_assert(Dune::AlwaysTrue<typename std::iterator_traits<Iter>::reference>::value, + "std::iterator_traits::reference is not defined!"); + + // Make sure the iterator_category is properly set + static_assert(std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::output_iterator_tag>::value, + "std::iterator_traits::iterator_category is not properly defined!"); } /** -* @brief Test whether the class Iter implements the interface of an STL forward iterator -* -* @param begin Iterator positioned at the start -* @param end Iterator positioned at the end -* @param opt Functor for doing whatever one wants -*/ + * @brief Test whether the class Iter implements the interface of an STL forward iterator + * + * @param begin Iterator positioned at the start + * @param end Iterator positioned at the end + * @param opt Functor for doing whatever one wants + */ template<class Iter, class Opt> int testForwardIterator(Iter begin, Iter end, Opt& opt) { -// Return status -int ret=0; - -// Test whether iterator is can be value-initialized. -// These object will go out of scope at the end of this method, and hence -// it will also test whether these objects are destructible. -Iter defaultConstructedIterator1{}, defaultConstructedIterator2{}; - -// Since C++14, value-initialized forward iterators are specified as the -// end iterator of the same, empty sequence. Hence, they should compare equal. -// Notice that value-initialization and default-initialization are not the -// same for raw pointers. Since these are POD, value-initialization leads -// to zero-initialization while default-initialization would leave them -// uninitialized such that the comparison is undefined behaviour. -if (defaultConstructedIterator1 != defaultConstructedIterator2) { -std::cerr<<"Default constructed iterators do not compare equal for "+Dune::className<Iter>()+"."<<std::endl; -ret=1; -} - -// Test whether iterator is copy-constructible -Iter tmp1(begin); - -// Test whether iterator is copy-assignable -Iter tmp=begin; - -// Test for inequality -if (tmp!=begin || tmp1!=begin || tmp!=tmp1) { -std::cerr<<" Copying iterator failed "<<__FILE__<<":"<<__LINE__<<std::endl; -ret=1; -} - -// Test for equality -if (not (tmp==begin && tmp1==begin && tmp==tmp1)) { -std::cerr<<" Copying iterator failed "<<__FILE__<<":"<<__LINE__<<std::endl; -ret=1; -} - -// Test whether pre-increment works -for(; begin!=end; ++begin) -// Test rvalue dereferencing -opt(*begin); - -// Test whether post-increment works -for(; begin!=end; begin++) -opt(*begin); - -// Test whether std::iterator_traits is properly specialized -// The is_same<A,A> construction allows one to test whether the type A exists at all, -// without assuming anything further about A. -static_assert(std::is_same<typename std::iterator_traits<Iter>::difference_type, typename std::iterator_traits<Iter>::difference_type>::value, -"std::iterator_traits::difference_type is not defined!"); -static_assert(std::is_same<typename std::iterator_traits<Iter>::value_type, typename std::iterator_traits<Iter>::value_type>::value, -"std::iterator_traits::value_type is not defined!"); -static_assert(std::is_same<typename std::iterator_traits<Iter>::pointer, typename std::iterator_traits<Iter>::pointer>::value, -"std::iterator_traits::pointer is not defined!"); -static_assert(std::is_same<typename std::iterator_traits<Iter>::reference, typename std::iterator_traits<Iter>::reference>::value, -"std::iterator_traits::reference is not defined!"); - -// Make sure the iterator_category is properly set -static_assert(std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::forward_iterator_tag>::value -or std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::bidirectional_iterator_tag>::value -or std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::random_access_iterator_tag>::value, -"std::iterator_traits::iterator_category is not properly defined!"); - -return ret; + // Return status + int ret=0; + + // Test whether iterator is can be value-initialized. + // These object will go out of scope at the end of this method, and hence + // it will also test whether these objects are destructible. + Iter defaultConstructedIterator1{}, defaultConstructedIterator2{}; + + // Since C++14, value-initialized forward iterators are specified as the + // end iterator of the same, empty sequence. Hence, they should compare equal. + // Notice that value-initialization and default-initialization are not the + // same for raw pointers. Since these are POD, value-initialization leads + // to zero-initialization while default-initialization would leave them + // uninitialized such that the comparison is undefined behaviour. + if (defaultConstructedIterator1 != defaultConstructedIterator2) { + std::cerr<<"Default constructed iterators do not compare equal for "+Dune::className<Iter>()+"."<<std::endl; + ret=1; + } + + // Test whether iterator is copy-constructible + Iter tmp1(begin); + + // Test whether iterator is copy-assignable + Iter tmp=begin; + + // Test for inequality + if (tmp!=begin || tmp1!=begin || tmp!=tmp1) { + std::cerr<<" Copying iterator failed "<<__FILE__<<":"<<__LINE__<<std::endl; + ret=1; + } + + // Test for equality + if (not (tmp==begin && tmp1==begin && tmp==tmp1)) { + std::cerr<<" Copying iterator failed "<<__FILE__<<":"<<__LINE__<<std::endl; + ret=1; + } + + // Test whether pre-increment works + for(; begin!=end; ++begin) + // Test rvalue dereferencing + opt(*begin); + + // Test whether post-increment works + for(; begin!=end; begin++) + opt(*begin); + + // Test whether std::iterator_traits is properly specialized + // The is_same<A,A> construction allows one to test whether the type A exists at all, + // without assuming anything further about A. + static_assert(std::is_same<typename std::iterator_traits<Iter>::difference_type, typename std::iterator_traits<Iter>::difference_type>::value, + "std::iterator_traits::difference_type is not defined!"); + static_assert(std::is_same<typename std::iterator_traits<Iter>::value_type, typename std::iterator_traits<Iter>::value_type>::value, + "std::iterator_traits::value_type is not defined!"); + static_assert(std::is_same<typename std::iterator_traits<Iter>::pointer, typename std::iterator_traits<Iter>::pointer>::value, + "std::iterator_traits::pointer is not defined!"); + static_assert(std::is_same<typename std::iterator_traits<Iter>::reference, typename std::iterator_traits<Iter>::reference>::value, + "std::iterator_traits::reference is not defined!"); + + // Make sure the iterator_category is properly set + static_assert(std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::forward_iterator_tag>::value + or std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::bidirectional_iterator_tag>::value + or std::is_same<typename std::iterator_traits<Iter>::iterator_category, std::random_access_iterator_tag>::value, + "std::iterator_traits::iterator_category is not properly defined!"); + + return ret; } /** -* @brief Tests the capabilities of a bidirectional iterator. -* -* Namely it test whether random positions can be reached from -* each directions. -* -* @param begin Iterator positioned at the stsrt. -* @param end Iterator positioned at the end. -* @param opt Functor for doing whatever one wants. -*/ + * @brief Tests the capabilities of a bidirectional iterator. + * + * Namely it test whether random positions can be reached from + * each directions. + * + * @param begin Iterator positioned at the stsrt. + * @param end Iterator positioned at the end. + * @param opt Functor for doing whatever one wants. + */ template<class Iter, class Opt> int testBidirectionalIterator(Iter begin, Iter end, Opt opt) { -int ret=testForwardIterator(begin, end, opt); -for(Iter pre = end, post = end; pre != begin; ) -{ -if(pre != post--) -{ -std::cerr << "Postdecrement did not return the old iterator" -<< std::endl; -++ret; -} -if(--pre != post) -{ -std::cerr << "Predecrement did not return the new iterator" -<< std::endl; -++ret; -} -opt(*pre); -} - -typename Iter::difference_type size = std::distance(begin, end); -srand(300); - -int no= (size>10) ? 10 : size; - -for(int i=0; i < no; i++) -{ -int index = static_cast<int>(size*(rand()/(RAND_MAX+1.0))); -int backwards=size-index; -Iter tbegin = begin; -Iter tend = end; -for(int j=0; j < index; j++) ++tbegin; -for(int j=0; j < backwards; j++) --tend; - -if(tbegin != tend) -{ -std::cerr<<"Did not reach same index by starting forward from " -<<"begin and backwards from end."<<std::endl; -++ret; -} -} -return ret; + int ret=testForwardIterator(begin, end, opt); + for(Iter pre = end, post = end; pre != begin; ) + { + if(pre != post--) + { + std::cerr << "Postdecrement did not return the old iterator" + << std::endl; + ++ret; + } + if(--pre != post) + { + std::cerr << "Predecrement did not return the new iterator" + << std::endl; + ++ret; + } + opt(*pre); + } + + typename Iter::difference_type size = std::distance(begin, end); + srand(300); + + int no= (size>10) ? 10 : size; + + for(int i=0; i < no; i++) + { + int index = static_cast<int>(size*(rand()/(RAND_MAX+1.0))); + int backwards=size-index; + Iter tbegin = begin; + Iter tend = end; + for(int j=0; j < index; j++) ++tbegin; + for(int j=0; j < backwards; j++) --tend; + + if(tbegin != tend) + { + std::cerr<<"Did not reach same index by starting forward from " + <<"begin and backwards from end."<<std::endl; + ++ret; + } + } + return ret; } template<class Iter, class Opt> int testRandomAccessIterator(Iter begin, Iter end, Opt opt){ -int ret=testBidirectionalIterator(begin, end, opt); - -typename Iter::difference_type size = end-begin; - -srand(300); - -int no= (size>10) ? 10 : size; - -for(int i=0; i < no; i++) -{ -int index = static_cast<int>(size*(rand()/(RAND_MAX+1.0))); -opt(begin[index]); -} - -// Test the less than operator -if(begin != end &&!( begin<end)) -{ -std::cerr<<"! (begin()<end())"<<std::endl; -ret++; -} - -if(begin != end) { -if(begin-end >= 0) { -std::cerr<<"begin!=end, but begin-end >= 0!"<<std::endl; -ret++; -} -if(end-begin <= 0) { -std::cerr<<"begin!=end, but end-begin <= 0!"<<std::endl; -ret++; -} -} - -for(int i=0; i < no; i++) -{ -int index = static_cast<int>(size*(rand()/(RAND_MAX+1.0))); -Iter rand(begin), test(begin), res{}; -rand+=index; - -if((res=begin+index) != rand) -{ -std::cerr << " i+n should have the result i+=n, where i is the " -<<"iterator and n is the difference type!" <<std::endl; -ret++; -} -for(int j = 0; j < index; j++) -++test; - -if(test != rand) -{ -std::cerr << "i+=n should have the same result as applying the" -<< "increment ooperator n times!"<< std::endl; -ret++; -} - -rand=end, test=end; -rand-=index; - - -if((end-index) != rand) -{ -std::cerr << " i-n should have the result i-=n, where i is the " -<<"iterator and n is the difference type!" <<std::endl; -ret++; -} -for(int j = 0; j < index; j++) ---test; - -if(test != rand) -{ -std::cerr << "i+=n should have the same result as applying the" -<< "increment ooperator n times!"<< std::endl; -ret++; -} -} - -for(int i=0; i < no; i++) -{ -Iter iter1 = begin+static_cast<int>(size*(rand()/(RAND_MAX+1.0))); -Iter iter2 = begin+static_cast<int>(size*(rand()/(RAND_MAX+1.0))); -typename Iter::difference_type diff = iter2 -iter1; -if((iter1+diff)!=iter2) { -std::cerr<< "i+(j-i) = j should hold, where i,j are iterators!"<<std::endl; -ret++; -} -} - -return ret; + int ret=testBidirectionalIterator(begin, end, opt); + + typename Iter::difference_type size = end-begin; + + srand(300); + + int no= (size>10) ? 10 : size; + + for(int i=0; i < no; i++) + { + int index = static_cast<int>(size*(rand()/(RAND_MAX+1.0))); + opt(begin[index]); + } + + // Test the less than operator + if(begin != end &&!( begin<end)) + { + std::cerr<<"! (begin()<end())"<<std::endl; + ret++; + } + + if(begin != end) { + if(begin-end >= 0) { + std::cerr<<"begin!=end, but begin-end >= 0!"<<std::endl; + ret++; + } + if(end-begin <= 0) { + std::cerr<<"begin!=end, but end-begin <= 0!"<<std::endl; + ret++; + } + } + + for(int i=0; i < no; i++) + { + int index = static_cast<int>(size*(rand()/(RAND_MAX+1.0))); + Iter rand(begin), test(begin), res{}; + rand+=index; + + if((res=begin+index) != rand) + { + std::cerr << " i+n should have the result i+=n, where i is the " + <<"iterator and n is the difference type!" <<std::endl; + ret++; + } + for(int j = 0; j < index; j++) + ++test; + + if(test != rand) + { + std::cerr << "i+=n should have the same result as applying the" + << "increment ooperator n times!"<< std::endl; + ret++; + } + + rand=end, test=end; + rand-=index; + + + if((end-index) != rand) + { + std::cerr << " i-n should have the result i-=n, where i is the " + <<"iterator and n is the difference type!" <<std::endl; + ret++; + } + for(int j = 0; j < index; j++) + --test; + + if(test != rand) + { + std::cerr << "i+=n should have the same result as applying the" + << "increment ooperator n times!"<< std::endl; + ret++; + } + } + + for(int i=0; i < no; i++) + { + Iter iter1 = begin+static_cast<int>(size*(rand()/(RAND_MAX+1.0))); + Iter iter2 = begin+static_cast<int>(size*(rand()/(RAND_MAX+1.0))); + typename Iter::difference_type diff = iter2 -iter1; + if((iter1+diff)!=iter2) { + std::cerr<< "i+(j-i) = j should hold, where i,j are iterators!"<<std::endl; + ret++; + } + } + + return ret; } template<class Iter, class Opt, typename iterator_category> @@ -284,129 +284,129 @@ int testIterator(Iter& begin, Iter& end, Opt& opt, iterator_category cat); template<class Iter, class Opt> int testIterator(Iter& begin, Iter& end, Opt& opt, std::forward_iterator_tag) { -return testForwardIterator(begin, end, opt); + return testForwardIterator(begin, end, opt); } template<class Iter, class Opt> int testIterator(Iter& begin, Iter& end, Opt& opt, std::bidirectional_iterator_tag) { -return testBidirectionalIterator(begin, end, opt); + return testBidirectionalIterator(begin, end, opt); } template<class Iter, class Opt> int testIterator(Iter& begin, Iter& end, Opt& opt, std::random_access_iterator_tag) { -// std::cout << "Testing iterator "; -int ret = testRandomAccessIterator(begin, end, opt); -//std::cout<<std::endl; -return ret; + // std::cout << "Testing iterator "; + int ret = testRandomAccessIterator(begin, end, opt); + //std::cout<<std::endl; + return ret; } template<class Iter, class Opt> int testConstIterator(Iter& begin, Iter& end, Opt& opt) { -//std::cout << "Testing constant iterator: "; -int ret=testIterator(begin, end, opt, typename std::iterator_traits<Iter>::iterator_category()); -//std::cout<<std::endl; -return ret; + //std::cout << "Testing constant iterator: "; + int ret=testIterator(begin, end, opt, typename std::iterator_traits<Iter>::iterator_category()); + //std::cout<<std::endl; + return ret; } template<bool> struct TestSorting { -template<class Container, typename IteratorTag> -static void testSorting(Container&, IteratorTag) -{} -template<class Container> -static void testSorting(Container& c, std::random_access_iterator_tag) -{ -std::sort(c.begin(), c.end()); -} + template<class Container, typename IteratorTag> + static void testSorting(Container&, IteratorTag) + {} + template<class Container> + static void testSorting(Container& c, std::random_access_iterator_tag) + { + std::sort(c.begin(), c.end()); + } } ; template<> struct TestSorting<false> { -template<class Container> -static void testSorting(Container&, std::random_access_iterator_tag) -{} -template<class Container, typename IteratorTag> -static void testSorting(Container&, IteratorTag) -{} + template<class Container> + static void testSorting(Container&, std::random_access_iterator_tag) + {} + template<class Container, typename IteratorTag> + static void testSorting(Container&, IteratorTag) + {} }; template<class Container, class Opt, bool testSort> int testIterator(Container& c, Opt& opt) { -typename Container::iterator begin=c.begin(), end=c.end(); -typename Container::const_iterator cbegin(begin); -typename Container::const_iterator cbegin1 DUNE_UNUSED = begin; -typename Container::const_iterator cend=c.end(); -int ret = 0; + typename Container::iterator begin=c.begin(), end=c.end(); + typename Container::const_iterator cbegin(begin); + typename Container::const_iterator cbegin1 DUNE_UNUSED = begin; + typename Container::const_iterator cend=c.end(); + int ret = 0; -TestSorting<testSort>::testSorting(c, typename std::iterator_traits<typename Container::iterator>::iterator_category()); + TestSorting<testSort>::testSorting(c, typename std::iterator_traits<typename Container::iterator>::iterator_category()); -if(end!=cend || cend!=end) -{ -std::cerr<<"constant and mutable iterators should be equal!"<<std::endl; -ret=1; -} -ret += testConstIterator(cbegin, cend, opt); -if(testSort) -ret += testIterator(begin,end,opt); + if(end!=cend || cend!=end) + { + std::cerr<<"constant and mutable iterators should be equal!"<<std::endl; + ret=1; + } + ret += testConstIterator(cbegin, cend, opt); + if(testSort) + ret += testIterator(begin,end,opt); -return ret; + return ret; } template<class Container, class Opt> int testIterator(Container& c, Opt& opt) { -return testIterator<Container,Opt,true>(c,opt); + return testIterator<Container,Opt,true>(c,opt); } template<class Iter, class Opt> void testAssignment(Iter begin, Iter end, Opt&) { -//std::cout << "Assignment: "; -for(; begin!=end; begin++) -*begin=typename std::iterator_traits<Iter>::value_type(); -//std::cout<<" Done."<< std::endl; + //std::cout << "Assignment: "; + for(; begin!=end; begin++) + *begin=typename std::iterator_traits<Iter>::value_type(); + //std::cout<<" Done."<< std::endl; } template<class Iter, class Opt> int testIterator(Iter& begin, Iter& end, Opt& opt) { -testAssignment(begin, end, opt); -return testConstIterator(begin, end, opt); + testAssignment(begin, end, opt); + return testConstIterator(begin, end, opt); } template<class T> class Printer { -typename std::remove_const<T>::type res; + typename std::remove_const<T>::type res; public: -Printer() : res(0){} -void operator()(const T& t){ -res+=t; -// std::cout << t <<" "; -} + Printer() : res(0){} + void operator()(const T& t){ + res+=t; + // std::cout << t <<" "; + } }; template<class Container, class Opt> int testIterator(const Container& c, Opt& opt) { -typename Container::const_iterator begin=c.begin(), end=c.end(); -return testConstIterator(begin,end, opt); + typename Container::const_iterator begin=c.begin(), end=c.end(); + return testConstIterator(begin,end, opt); } template<class Container> int testIterator(Container& c) { -Printer<typename std::iterator_traits<typename Container::iterator>::value_type> print; -return testIterator(c,print); + Printer<typename std::iterator_traits<typename Container::iterator>::value_type> print; + return testIterator(c,print); } #endif diff --git a/dune/common/test/parameterizedobjectfactorysingleton.hh b/dune/common/test/parameterizedobjectfactorysingleton.hh index 9b2e0d2abdf01b3ee5a4b487fcad19c503fa0f7d..e9448fa8481df4a73a7dea3cd5bf067ddf2841f5 100644 --- a/dune/common/test/parameterizedobjectfactorysingleton.hh +++ b/dune/common/test/parameterizedobjectfactorysingleton.hh @@ -7,38 +7,38 @@ #include <string> #define DefineImplementation2(IF,T) \ -struct T : public IF { \ -T() {} \ -std::string info() override { \ -return #T; \ -} \ -} + struct T : public IF { \ + T() {} \ + std::string info() override { \ + return #T; \ + } \ + } #define DefineImplementation(IF,T,...) \ -struct T : public IF { \ -T(__VA_ARGS__) {} \ -std::string info() override { \ -return #T; \ -} \ -} + struct T : public IF { \ + T(__VA_ARGS__) {} \ + std::string info() override { \ + return #T; \ + } \ + } struct InterfaceA { -virtual std::string info() = 0; -virtual ~InterfaceA() = default; + virtual std::string info() = 0; + virtual ~InterfaceA() = default; }; struct InterfaceB { -virtual std::string info() = 0; -virtual ~InterfaceB() = default; + virtual std::string info() = 0; + virtual ~InterfaceB() = default; }; template<typename Interface> Dune::ParameterizedObjectFactory<std::unique_ptr<Interface>(int)> & globalPtrFactory() { -return Dune::Singleton<Dune::ParameterizedObjectFactory<std::unique_ptr<Interface>(int)>>::instance(); + return Dune::Singleton<Dune::ParameterizedObjectFactory<std::unique_ptr<Interface>(int)>>::instance(); } #endif //#ifndef DUNE_COMMON_TEST_PARAMETERIZEDOBJECTFACTORYSINGLETON_HH diff --git a/dune/common/test/testsuite.hh b/dune/common/test/testsuite.hh index 0468e88df367488743e4e4b149cf70c6c8135a41..55da4cb45be4926c178f7d368e4985e2c58ff02a 100644 --- a/dune/common/test/testsuite.hh +++ b/dune/common/test/testsuite.hh @@ -17,186 +17,186 @@ namespace Dune { -/** -* \brief A Simple helper class to organize your test suite -* -* Usage: Construct a TestSuite and call check() or require() -* with the condition to check and probably a name for this check. -* These methods return a stream such that you can pipe in an -* explanantion accompanied by respective data to give a reason -* for a test failure. -*/ -class TestSuite -{ -public: -enum ThrowPolicy -{ -AlwaysThrow, -ThrowOnRequired -}; - -/** -* \brief Create TestSuite -* -* \param name A name to identify this TestSuite. Defaults to "". -* \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. -*/ -TestSuite(ThrowPolicy policy, std::string name="") : -name_(name), -checks_(0), -failedChecks_(0), -throwPolicy_(policy==AlwaysThrow) -{} - -/** -* \brief Create TestSuite -* -* \param name A name to identify this TestSuite. Defaults to "". -* \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. Defaults to ThrowOnRequired -*/ -TestSuite(std::string name="", ThrowPolicy policy=ThrowOnRequired) : -name_(name), -checks_(0), -failedChecks_(0), -throwPolicy_(policy==AlwaysThrow) -{} - -/** -* \brief Check condition -* -* This will throw an exception if the check fails and if the AlwaysThrow policy was used on creation. -* -* \param conditon Checks if this is true and increases the failure counter if not. -* \param name A name to identify this check. Defaults to "" -* \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. -*/ -CollectorStream check(bool condition, std::string name="") -{ -++checks_; -if (not condition) -++failedChecks_; - -return CollectorStream([condition, name, this](std::string reason) { -if (not condition) -this->announceCheckResult(throwPolicy_, "CHECK ", name, reason); -}); -} - -/** -* \brief Check a required condition -* -* This will always throw an exception if the check fails. -* -* \param conditon Checks if this is true and increases the failure counter if not. -* \param name A name to identify this check. Defaults to "" -* \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. -*/ -CollectorStream require(bool condition, std::string name="") -{ -++checks_; -if (not condition) -++failedChecks_; - -return CollectorStream([condition, name, this](std::string reason) { -if (not condition) -this->announceCheckResult(true, "REQUIRED CHECK", name, reason); -}); -} - -/** -* \brief Collect data from a sub-TestSuite -* -* This will incorporate the accumulated results of the sub-TestSuite -* into this one. If the sub-TestSuite failed, i.e., contained failed -* checks, a summary will be printed. -*/ -void subTest(const TestSuite& subTest) -{ -checks_ += subTest.checks_; -failedChecks_ += subTest.failedChecks_; - -if (not subTest) -announceCheckResult(throwPolicy_, "SUBTEST", subTest.name(), std::to_string(subTest.failedChecks_)+"/"+std::to_string(subTest.checks_) + " checks failed in this subtest."); -} - -/** -* \brief Check if this TestSuite failed -* -* \returns False if any of the executed tests failed, otherwise true. -*/ -explicit operator bool () const -{ -return (failedChecks_==0); -} - -/** -* \brief Query name -* -* \returns Name of this TestSuite -*/ -std::string name() const -{ -return name_; -} - -/** -* \brief Print a summary of this TestSuite -* -* \returns False if any of the executed tests failed, otherwise true. -*/ -bool report() const -{ -if (failedChecks_>0) -std::cout << composeMessage("TEST ", name(), std::to_string(failedChecks_)+"/"+std::to_string(checks_) + " checks failed in this test.") << std::endl; -return (failedChecks_==0); -} - -/** -* \brief Exit the test. -* -* This wil print a summary of the test and return an integer -* to be used on program exit. -* -* \returns 1 if any of the executed tests failed, otherwise 0. -*/ -int exit() const -{ -return (report() ? 0: 1); -} - -protected: - -// Compose a diagnostic message -static std::string composeMessage(std::string type, std::string name, std::string reason) -{ -std::ostringstream s; -s << type << " FAILED"; -if (name!="") -s << "(" << name << ")"; -s << ": "; -if (reason!="") -s << reason; -return s.str(); -} - -// Announce check results. To be called on failed checks -static void announceCheckResult(bool throwException, std::string type, std::string name, std::string reason) -{ -std::string message = composeMessage(type, name, reason); -std::cout << message << std::endl; -if (throwException) -{ -Dune::Exception ex; -ex.message(message); -throw ex; -} -} - -std::string name_; -std::size_t checks_; -std::size_t failedChecks_; -bool throwPolicy_; -}; + /** + * \brief A Simple helper class to organize your test suite + * + * Usage: Construct a TestSuite and call check() or require() + * with the condition to check and probably a name for this check. + * These methods return a stream such that you can pipe in an + * explanantion accompanied by respective data to give a reason + * for a test failure. + */ + class TestSuite + { + public: + enum ThrowPolicy + { + AlwaysThrow, + ThrowOnRequired + }; + + /** + * \brief Create TestSuite + * + * \param name A name to identify this TestSuite. Defaults to "". + * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. + */ + TestSuite(ThrowPolicy policy, std::string name="") : + name_(name), + checks_(0), + failedChecks_(0), + throwPolicy_(policy==AlwaysThrow) + {} + + /** + * \brief Create TestSuite + * + * \param name A name to identify this TestSuite. Defaults to "". + * \param policy If AlwaysThrow any failing check will throw, otherwise only required checks will do. Defaults to ThrowOnRequired + */ + TestSuite(std::string name="", ThrowPolicy policy=ThrowOnRequired) : + name_(name), + checks_(0), + failedChecks_(0), + throwPolicy_(policy==AlwaysThrow) + {} + + /** + * \brief Check condition + * + * This will throw an exception if the check fails and if the AlwaysThrow policy was used on creation. + * + * \param conditon Checks if this is true and increases the failure counter if not. + * \param name A name to identify this check. Defaults to "" + * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. + */ + CollectorStream check(bool condition, std::string name="") + { + ++checks_; + if (not condition) + ++failedChecks_; + + return CollectorStream([condition, name, this](std::string reason) { + if (not condition) + this->announceCheckResult(throwPolicy_, "CHECK ", name, reason); + }); + } + + /** + * \brief Check a required condition + * + * This will always throw an exception if the check fails. + * + * \param conditon Checks if this is true and increases the failure counter if not. + * \param name A name to identify this check. Defaults to "" + * \returns A CollectorStream that can be used to create a diagnostic message to be printed on failure. + */ + CollectorStream require(bool condition, std::string name="") + { + ++checks_; + if (not condition) + ++failedChecks_; + + return CollectorStream([condition, name, this](std::string reason) { + if (not condition) + this->announceCheckResult(true, "REQUIRED CHECK", name, reason); + }); + } + + /** + * \brief Collect data from a sub-TestSuite + * + * This will incorporate the accumulated results of the sub-TestSuite + * into this one. If the sub-TestSuite failed, i.e., contained failed + * checks, a summary will be printed. + */ + void subTest(const TestSuite& subTest) + { + checks_ += subTest.checks_; + failedChecks_ += subTest.failedChecks_; + + if (not subTest) + announceCheckResult(throwPolicy_, "SUBTEST", subTest.name(), std::to_string(subTest.failedChecks_)+"/"+std::to_string(subTest.checks_) + " checks failed in this subtest."); + } + + /** + * \brief Check if this TestSuite failed + * + * \returns False if any of the executed tests failed, otherwise true. + */ + explicit operator bool () const + { + return (failedChecks_==0); + } + + /** + * \brief Query name + * + * \returns Name of this TestSuite + */ + std::string name() const + { + return name_; + } + + /** + * \brief Print a summary of this TestSuite + * + * \returns False if any of the executed tests failed, otherwise true. + */ + bool report() const + { + if (failedChecks_>0) + std::cout << composeMessage("TEST ", name(), std::to_string(failedChecks_)+"/"+std::to_string(checks_) + " checks failed in this test.") << std::endl; + return (failedChecks_==0); + } + + /** + * \brief Exit the test. + * + * This wil print a summary of the test and return an integer + * to be used on program exit. + * + * \returns 1 if any of the executed tests failed, otherwise 0. + */ + int exit() const + { + return (report() ? 0: 1); + } + + protected: + + // Compose a diagnostic message + static std::string composeMessage(std::string type, std::string name, std::string reason) + { + std::ostringstream s; + s << type << " FAILED"; + if (name!="") + s << "(" << name << ")"; + s << ": "; + if (reason!="") + s << reason; + return s.str(); + } + + // Announce check results. To be called on failed checks + static void announceCheckResult(bool throwException, std::string type, std::string name, std::string reason) + { + std::string message = composeMessage(type, name, reason); + std::cout << message << std::endl; + if (throwException) + { + Dune::Exception ex; + ex.message(message); + throw ex; + } + } + + std::string name_; + std::size_t checks_; + std::size_t failedChecks_; + bool throwPolicy_; + }; diff --git a/dune/common/timer.hh b/dune/common/timer.hh index c1aff57264990da388262f271287d17f5740b202..05b85ad2b70934edbc2f1cacaed9151b2129bec1 100644 --- a/dune/common/timer.hh +++ b/dune/common/timer.hh @@ -14,138 +14,138 @@ namespace Dune { -/** @addtogroup Common -@{ -*/ - -/*! \file -\brief A simple timing class. -*/ - - -/** \brief A simple stop watch - -This class reports the elapsed user-time, i.e. time spent computing, -after the last call to Timer::reset(). The results are seconds and -fractional seconds. Note that the resolution of the timing depends -on your OS kernel which should be somewhere in the milisecond range. - -The class is basically a wrapper for the libc-function getrusage() - -\warning In a multi-threading situation, this class does NOT return wall-time! -Instead, the run time for all threads will be added up. -For example, if you have four threads running in parallel taking one second each, -then the Timer class will return an elapsed time of four seconds. - -*/ -class Timer -{ -public: - -/** \brief A new timer, create and reset -* -* \param startImmediately If true (default) the timer starts counting immediately -*/ -Timer (bool startImmediately=true) noexcept -{ -isRunning_ = startImmediately; -reset(); -} - -//! Reset timer while keeping the running/stopped state -void reset() noexcept -{ -sumElapsed_ = 0.0; -storedLastElapsed_ = 0.0; -rawReset(); -} - - -//! Start the timer and continue measurement if it is not running. Otherwise do nothing. -void start() noexcept -{ -if (not (isRunning_)) -{ -rawReset(); -isRunning_ = true; -} -} - - -//! Get elapsed user-time from last reset until now/last stop in seconds. -double elapsed () const noexcept -{ -// if timer is running add the time elapsed since last start to sum -if (isRunning_) -return sumElapsed_ + lastElapsed(); - -return sumElapsed_; -} - - -//! Get elapsed user-time from last start until now/last stop in seconds. -double lastElapsed () const noexcept -{ -// if timer is running return the current value -if (isRunning_) -return rawElapsed(); - -// if timer is not running return stored value from last run -return storedLastElapsed_; -} - - -//! Stop the timer and return elapsed(). -double stop() noexcept -{ -if (isRunning_) -{ -// update storedLastElapsed_ and sumElapsed_ and stop timer -storedLastElapsed_ = lastElapsed(); -sumElapsed_ += storedLastElapsed_; -isRunning_ = false; -} -return elapsed(); -} - - -private: - -bool isRunning_; -double sumElapsed_; -double storedLastElapsed_; + /** @addtogroup Common + @{ + */ + + /*! \file + \brief A simple timing class. + */ + + + /** \brief A simple stop watch + + This class reports the elapsed user-time, i.e. time spent computing, + after the last call to Timer::reset(). The results are seconds and + fractional seconds. Note that the resolution of the timing depends + on your OS kernel which should be somewhere in the milisecond range. + + The class is basically a wrapper for the libc-function getrusage() + + \warning In a multi-threading situation, this class does NOT return wall-time! + Instead, the run time for all threads will be added up. + For example, if you have four threads running in parallel taking one second each, + then the Timer class will return an elapsed time of four seconds. + + */ + class Timer + { + public: + + /** \brief A new timer, create and reset + * + * \param startImmediately If true (default) the timer starts counting immediately + */ + Timer (bool startImmediately=true) noexcept + { + isRunning_ = startImmediately; + reset(); + } + + //! Reset timer while keeping the running/stopped state + void reset() noexcept + { + sumElapsed_ = 0.0; + storedLastElapsed_ = 0.0; + rawReset(); + } + + + //! Start the timer and continue measurement if it is not running. Otherwise do nothing. + void start() noexcept + { + if (not (isRunning_)) + { + rawReset(); + isRunning_ = true; + } + } + + + //! Get elapsed user-time from last reset until now/last stop in seconds. + double elapsed () const noexcept + { + // if timer is running add the time elapsed since last start to sum + if (isRunning_) + return sumElapsed_ + lastElapsed(); + + return sumElapsed_; + } + + + //! Get elapsed user-time from last start until now/last stop in seconds. + double lastElapsed () const noexcept + { + // if timer is running return the current value + if (isRunning_) + return rawElapsed(); + + // if timer is not running return stored value from last run + return storedLastElapsed_; + } + + + //! Stop the timer and return elapsed(). + double stop() noexcept + { + if (isRunning_) + { + // update storedLastElapsed_ and sumElapsed_ and stop timer + storedLastElapsed_ = lastElapsed(); + sumElapsed_ += storedLastElapsed_; + isRunning_ = false; + } + return elapsed(); + } + + + private: + + bool isRunning_; + double sumElapsed_; + double storedLastElapsed_; #ifdef TIMER_USE_STD_CLOCK -void rawReset() noexcept -{ -cstart = std::clock(); -} + void rawReset() noexcept + { + cstart = std::clock(); + } -double rawElapsed () const noexcept -{ -return (std::clock()-cstart) / static_cast<double>(CLOCKS_PER_SEC); -} + double rawElapsed () const noexcept + { + return (std::clock()-cstart) / static_cast<double>(CLOCKS_PER_SEC); + } -std::clock_t cstart; + std::clock_t cstart; #else -void rawReset() noexcept -{ -cstart = std::chrono::high_resolution_clock::now(); -} - -double rawElapsed () const noexcept -{ -std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); -std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double> >(now - cstart); -return time_span.count(); -} - -std::chrono::high_resolution_clock::time_point cstart; + void rawReset() noexcept + { + cstart = std::chrono::high_resolution_clock::now(); + } + + double rawElapsed () const noexcept + { + std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); + std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double> >(now - cstart); + return time_span.count(); + } + + std::chrono::high_resolution_clock::time_point cstart; #endif -}; // end class Timer + }; // end class Timer -/** @} end documentation */ + /** @} end documentation */ } // end namespace diff --git a/dune/common/to_unique_ptr.hh b/dune/common/to_unique_ptr.hh index e12b0499e416d4fa63a3d51849e5625e241a60ae..07525526d0815c9df616cdafc7964997ba6f6b52 100644 --- a/dune/common/to_unique_ptr.hh +++ b/dune/common/to_unique_ptr.hh @@ -9,91 +9,91 @@ namespace Dune { -/// \brief An owning pointer wrapper that can be assigned to (smart) pointers. Cannot be copied. -/// Transfers ownership by cast to any (smart) pointer type. Releases the stored pointer on transfer. -/// NOTE: This is an intermediate solution to switch to std::unique_ptr in later releases smoothly. -/** -* Example of usage: -* ``` -* ToUniquePtr<int> f() { return new int(1); } -* auto g() { return makeToUnique<int>(2); } -* -* int* p1 = f(); // p1 gets ownership, must delete explicitly -* delete p1; -* -* std::unique_ptr<int> p2 = f(); -* std::shared_ptr<int> p3 = f(); -* -* auto p4 = f(); // ToUniquePtr has itself pointer semantic -* std::cout << *p4 << '\n'; -* -* std::unique_ptr<int> p5( g() ); -* ``` -**/ -template <class T> -class ToUniquePtr -: public std::unique_ptr<T> -{ -using Super = std::unique_ptr<T>; - -public: -// Member types: -//@{ - -using pointer = typename Super::pointer; - -//@} - - -public: -// Constructors: -//@{ - -/// Constructor, stores the pointer. -ToUniquePtr(pointer ptr = pointer()) noexcept -: Super(ptr) -{} - -/// Constructor, creates a `nullptr` -ToUniquePtr(std::nullptr_t) noexcept -: Super(nullptr) -{} - -//@} - - -public: -// Conversion operators: -//@{ - -/// Convert to underlying pointer, releases the stored pointer. -/// \deprecated Cast to raw pointer is deprecated. Use std::unique_ptr or std::shared_ptr instead. -/// Will be removed after Dune 2.8 -[[deprecated("Cast to raw pointer is deprecated. Use std::unique_ptr or std::shared_ptr instead.")]] -operator pointer() noexcept { return Super::release(); } - -/// Convert to unique_ptr, invalidates the stored pointer -operator std::unique_ptr<T>() noexcept { return std::move(static_cast<Super&>(*this)); } - -/// Convert to shared_ptr, invalidates the stored pointer -operator std::shared_ptr<T>() noexcept { return std::move(static_cast<Super&>(*this)); } - -/// Checks whether *this owns an object -explicit operator bool() noexcept { return bool(static_cast<Super&>(*this)); } - -/// Checks whether *this owns an object -explicit operator bool() const noexcept { return bool(static_cast<Super const&>(*this)); } - -//@} -}; - - -/// Constructs an object of type T and wraps it in a ToUniquePtr, \relates ToUniquePtr -template <class T, class... Args> -ToUniquePtr<T> makeToUnique(Args&&... args) -{ -return {new T(std::forward<Args>(args)...)}; -} + /// \brief An owning pointer wrapper that can be assigned to (smart) pointers. Cannot be copied. + /// Transfers ownership by cast to any (smart) pointer type. Releases the stored pointer on transfer. + /// NOTE: This is an intermediate solution to switch to std::unique_ptr in later releases smoothly. + /** + * Example of usage: + * ``` + * ToUniquePtr<int> f() { return new int(1); } + * auto g() { return makeToUnique<int>(2); } + * + * int* p1 = f(); // p1 gets ownership, must delete explicitly + * delete p1; + * + * std::unique_ptr<int> p2 = f(); + * std::shared_ptr<int> p3 = f(); + * + * auto p4 = f(); // ToUniquePtr has itself pointer semantic + * std::cout << *p4 << '\n'; + * + * std::unique_ptr<int> p5( g() ); + * ``` + **/ + template <class T> + class ToUniquePtr + : public std::unique_ptr<T> + { + using Super = std::unique_ptr<T>; + + public: + // Member types: + //@{ + + using pointer = typename Super::pointer; + + //@} + + + public: + // Constructors: + //@{ + + /// Constructor, stores the pointer. + ToUniquePtr(pointer ptr = pointer()) noexcept + : Super(ptr) + {} + + /// Constructor, creates a `nullptr` + ToUniquePtr(std::nullptr_t) noexcept + : Super(nullptr) + {} + + //@} + + + public: + // Conversion operators: + //@{ + + /// Convert to underlying pointer, releases the stored pointer. + /// \deprecated Cast to raw pointer is deprecated. Use std::unique_ptr or std::shared_ptr instead. + /// Will be removed after Dune 2.8 + [[deprecated("Cast to raw pointer is deprecated. Use std::unique_ptr or std::shared_ptr instead.")]] + operator pointer() noexcept { return Super::release(); } + + /// Convert to unique_ptr, invalidates the stored pointer + operator std::unique_ptr<T>() noexcept { return std::move(static_cast<Super&>(*this)); } + + /// Convert to shared_ptr, invalidates the stored pointer + operator std::shared_ptr<T>() noexcept { return std::move(static_cast<Super&>(*this)); } + + /// Checks whether *this owns an object + explicit operator bool() noexcept { return bool(static_cast<Super&>(*this)); } + + /// Checks whether *this owns an object + explicit operator bool() const noexcept { return bool(static_cast<Super const&>(*this)); } + + //@} + }; + + + /// Constructs an object of type T and wraps it in a ToUniquePtr, \relates ToUniquePtr + template <class T, class... Args> + ToUniquePtr<T> makeToUnique(Args&&... args) + { + return {new T(std::forward<Args>(args)...)}; + } } // end namespace Dune diff --git a/dune/common/transpose.hh b/dune/common/transpose.hh index 59fe8e28d75a856e0ac7e2a8e805bfc10bb4c916..fe211f6c467c89e7f27da43c65cfcd1e2834092e 100644 --- a/dune/common/transpose.hh +++ b/dune/common/transpose.hh @@ -13,63 +13,63 @@ namespace Dune { namespace Impl { -// Wrapper representing the transposed of a matrix. -// Creating the wrapper does not compute anything -// but only serves for tagging the wrapped matrix -// for transposition. -template<class M> -class TransposedMatrixWrapper -{ -public: + // Wrapper representing the transposed of a matrix. + // Creating the wrapper does not compute anything + // but only serves for tagging the wrapped matrix + // for transposition. + template<class M> + class TransposedMatrixWrapper + { + public: -enum { -//! The number of rows. -rows = M::cols, -//! The number of columns. -cols = M::rows -}; + enum { + //! The number of rows. + rows = M::cols, + //! The number of columns. + cols = M::rows + }; -TransposedMatrixWrapper(const M& matrix) : matrix_(matrix) {} -TransposedMatrixWrapper(const TransposedMatrixWrapper&) = delete; -TransposedMatrixWrapper(TransposedMatrixWrapper&&) = delete; + TransposedMatrixWrapper(const M& matrix) : matrix_(matrix) {} + TransposedMatrixWrapper(const TransposedMatrixWrapper&) = delete; + TransposedMatrixWrapper(TransposedMatrixWrapper&&) = delete; -template<class OtherField, int otherRows> -friend auto operator* (const FieldMatrix<OtherField, otherRows, rows>& matrixA, -const TransposedMatrixWrapper& matrixB) -{ -using ThisField = typename FieldTraits<M>::field_type; -using Field = typename PromotionTraits<ThisField, OtherField>::PromotedType; -FieldMatrix<Field, otherRows, cols> result; -for (std::size_t j=0; j<otherRows; ++j) -matrixB.matrix_.mv(matrixA[j], result[j]); -return result; -} + template<class OtherField, int otherRows> + friend auto operator* (const FieldMatrix<OtherField, otherRows, rows>& matrixA, + const TransposedMatrixWrapper& matrixB) + { + using ThisField = typename FieldTraits<M>::field_type; + using Field = typename PromotionTraits<ThisField, OtherField>::PromotedType; + FieldMatrix<Field, otherRows, cols> result; + for (std::size_t j=0; j<otherRows; ++j) + matrixB.matrix_.mv(matrixA[j], result[j]); + return result; + } -private: + private: -const M& matrix_; -}; + const M& matrix_; + }; } // namespace Impl /** -* \brief Create a wrapper modelling the transposed matrix -* -* Currently the wrapper only implements -* \code -* auto c = a*transpose(b); -* \endcode -* if a is a FieldMatrix of appropriate size. This is -* optimal even for sparse b because it only relies on -* calling b.mv(a[i], c[i]) for the rows of a. -* -* Since the created object only stores a reference -* to the wrapped matrix, it cannot be modified and -* should not be stored but used directly. -*/ + * \brief Create a wrapper modelling the transposed matrix + * + * Currently the wrapper only implements + * \code + * auto c = a*transpose(b); + * \endcode + * if a is a FieldMatrix of appropriate size. This is + * optimal even for sparse b because it only relies on + * calling b.mv(a[i], c[i]) for the rows of a. + * + * Since the created object only stores a reference + * to the wrapped matrix, it cannot be modified and + * should not be stored but used directly. + */ template<class Matrix> auto transpose(const Matrix& matrix) { -return Impl::TransposedMatrixWrapper<Matrix>(matrix); + return Impl::TransposedMatrixWrapper<Matrix>(matrix); } diff --git a/dune/common/tupleutility.hh b/dune/common/tupleutility.hh index e755555fe576ddf9e0fadcb46b5865df33855659..aaef7c7a8a6c3d7d252bb6187017e401ff27a2a9 100644 --- a/dune/common/tupleutility.hh +++ b/dune/common/tupleutility.hh @@ -15,564 +15,564 @@ namespace Dune { -/** @addtogroup TupleUtilities -* -* @{ -*/ - -/** -* @file -* @brief Contains utility classes which can be used with std::tuple. -*/ - -/** -* \brief Apply function with arguments from a given tuple -* -* \param f A callable object -* \param args Tuple containing the arguments -* \param indices Indices to arguments in tuple as std::integer_sequence -* -* This will call the function with arguments generated by unpacking those -* entries of the tuple that show up given integer_sequence. -* -* \ingroup Utility -*/ -template<class F, class ArgTuple, class I, I... i> -decltype(auto) applyPartial(F&& f, ArgTuple&& args, std::integer_sequence<I, i...> /*indices*/) -{ -return f(std::get<i>(args)...); -} - -template<class T> -struct TupleAccessTraits -{ -typedef typename std::add_const<T>::type& ConstType; -typedef T& NonConstType; -typedef const typename std::remove_const<T>::type& ParameterType; -}; - -template<class T> -struct TupleAccessTraits<T*> -{ -typedef typename std::add_const<T>::type* ConstType; -typedef T* NonConstType; -typedef T* ParameterType; -}; - -template<class T> -struct TupleAccessTraits<T&> -{ -typedef T& ConstType; -typedef T& NonConstType; -typedef T& ParameterType; -}; - -/** -* @brief A helper template that initializes a std::tuple consisting of pointers -* to nullptr. -* -* A std::tuple of nullptr may be useful when you use a std::tuple of pointers -* in a class which you can only initialise in a later stage. -*/ -template<class T> -struct NullPointerInitialiser; - -template<class... Args> -struct NullPointerInitialiser<std::tuple<Args...> > -{ -typedef std::tuple<Args...> ResultType; -static ResultType apply() -{ -return ResultType(static_cast<Args>(nullptr)...); -} -}; - -/** -* @brief Helper template to clone the type definition of a std::tuple with the -* storage types replaced by a user-defined rule. -* -* Suppose all storage types A_i in a std::tuple define a type A_i::B. You can -* build up a pair consisting of the types defined by A_i::B in the following -* way: -* -* \code -* template <class A> -* struct MyEvaluator -* { -* typedef typename A::B Type; -* }; -* -* typedef ForEachType<MyEvaluator, ATuple>::Type BTuple; -* \endcode -* -* Here, MyEvaluator is a helper struct that extracts the correct type from -* the storage types of the tuple defined by the tuple ATuple. -* -* \sa AddRefTypeEvaluator, AddPtrTypeEvaluator, genericTransformTuple(), -* and transformTuple(). -*/ -template<template <class> class TE, class T> -struct ForEachType; - -template<template <class> class TE, class... Args> -struct ForEachType<TE, std::tuple<Args...> > -{ -typedef std::tuple<typename TE<Args>::Type...> Type; -}; + /** @addtogroup TupleUtilities + * + * @{ + */ + + /** + * @file + * @brief Contains utility classes which can be used with std::tuple. + */ + + /** + * \brief Apply function with arguments from a given tuple + * + * \param f A callable object + * \param args Tuple containing the arguments + * \param indices Indices to arguments in tuple as std::integer_sequence + * + * This will call the function with arguments generated by unpacking those + * entries of the tuple that show up given integer_sequence. + * + * \ingroup Utility + */ + template<class F, class ArgTuple, class I, I... i> + decltype(auto) applyPartial(F&& f, ArgTuple&& args, std::integer_sequence<I, i...> /*indices*/) + { + return f(std::get<i>(args)...); + } + + template<class T> + struct TupleAccessTraits + { + typedef typename std::add_const<T>::type& ConstType; + typedef T& NonConstType; + typedef const typename std::remove_const<T>::type& ParameterType; + }; + + template<class T> + struct TupleAccessTraits<T*> + { + typedef typename std::add_const<T>::type* ConstType; + typedef T* NonConstType; + typedef T* ParameterType; + }; + + template<class T> + struct TupleAccessTraits<T&> + { + typedef T& ConstType; + typedef T& NonConstType; + typedef T& ParameterType; + }; + + /** + * @brief A helper template that initializes a std::tuple consisting of pointers + * to nullptr. + * + * A std::tuple of nullptr may be useful when you use a std::tuple of pointers + * in a class which you can only initialise in a later stage. + */ + template<class T> + struct NullPointerInitialiser; + + template<class... Args> + struct NullPointerInitialiser<std::tuple<Args...> > + { + typedef std::tuple<Args...> ResultType; + static ResultType apply() + { + return ResultType(static_cast<Args>(nullptr)...); + } + }; + + /** + * @brief Helper template to clone the type definition of a std::tuple with the + * storage types replaced by a user-defined rule. + * + * Suppose all storage types A_i in a std::tuple define a type A_i::B. You can + * build up a pair consisting of the types defined by A_i::B in the following + * way: + * + * \code + * template <class A> + * struct MyEvaluator + * { + * typedef typename A::B Type; + * }; + * + * typedef ForEachType<MyEvaluator, ATuple>::Type BTuple; + * \endcode + * + * Here, MyEvaluator is a helper struct that extracts the correct type from + * the storage types of the tuple defined by the tuple ATuple. + * + * \sa AddRefTypeEvaluator, AddPtrTypeEvaluator, genericTransformTuple(), + * and transformTuple(). + */ + template<template <class> class TE, class T> + struct ForEachType; + + template<template <class> class TE, class... Args> + struct ForEachType<TE, std::tuple<Args...> > + { + typedef std::tuple<typename TE<Args>::Type...> Type; + }; #ifndef DOXYGEN -template<class Tuple, class Functor, std::size_t... I> -inline auto genericTransformTupleBackendImpl(Tuple& t, Functor& f, const std::index_sequence<I...>& ) --> std::tuple<decltype(f(std::get<I>(t)))...> -{ -return std::tuple<decltype(f(std::get<I>(t)))...>(f(std::get<I>(t))...); -} - -template<class... Args, class Functor> -auto genericTransformTupleBackend(std::tuple<Args...>& t, Functor& f) -> -decltype(genericTransformTupleBackendImpl(t, f,std::index_sequence_for<Args...>{})) -{ -return genericTransformTupleBackendImpl(t, f,std::index_sequence_for<Args...>{}); -} - -template<class... Args, class Functor> -auto genericTransformTupleBackend(const std::tuple<Args...>& t, Functor& f) -> -decltype(genericTransformTupleBackendImpl(t, f, std::index_sequence_for<Args...>{})) -{ -return genericTransformTupleBackendImpl(t, f, std::index_sequence_for<Args...>{}); -} + template<class Tuple, class Functor, std::size_t... I> + inline auto genericTransformTupleBackendImpl(Tuple& t, Functor& f, const std::index_sequence<I...>& ) + -> std::tuple<decltype(f(std::get<I>(t)))...> + { + return std::tuple<decltype(f(std::get<I>(t)))...>(f(std::get<I>(t))...); + } + + template<class... Args, class Functor> + auto genericTransformTupleBackend(std::tuple<Args...>& t, Functor& f) -> + decltype(genericTransformTupleBackendImpl(t, f,std::index_sequence_for<Args...>{})) + { + return genericTransformTupleBackendImpl(t, f,std::index_sequence_for<Args...>{}); + } + + template<class... Args, class Functor> + auto genericTransformTupleBackend(const std::tuple<Args...>& t, Functor& f) -> + decltype(genericTransformTupleBackendImpl(t, f, std::index_sequence_for<Args...>{})) + { + return genericTransformTupleBackendImpl(t, f, std::index_sequence_for<Args...>{}); + } #endif -/** -* This function does for the value of a std::tuple what ForEachType does for the -* type of a std::tuple: it transforms the value using a user-provided policy -* functor. -* -* \param t The std::tuple value to transform. -* \param f The functor to use to transform the values. -* -* The functor should have the following form: -* -* \code -* struct Functor -* { -* template<class> -* struct TypeEvaluator -* { -* typedef user-defined Type; -* }; -* -* template<class T> -* typename TypeEvaluator<T>::Type operator()(T& val); -* -* template<class T> -* typename TypeEvaluator<T>::Type operator()(T& val) const; -* -* template<class T> -* typename TypeEvaluator<T>::Type operator()(const T& val); -* -* template<class T> -* typename TypeEvaluator<T>::Type operator()(const T& val) const; -* }; -* \endcode -* -* The member class template \c TypeEvaluator should be a class template -* suitable as the \c TypeEvaluator template parameter for ForEachType. The -* function call operator \c operator() is used to transform the value; only -* the signatures of \c operator() which are actually used must be present. -*/ -template<class Tuple, class Functor> -auto genericTransformTuple(Tuple&& t, Functor&& f) -> -decltype(genericTransformTupleBackend(t, f)) -{ -return genericTransformTupleBackend(t, f); -} - -/** -* \tparam TE TypeEvaluator class template. -* \tparam An Type of extra arguments to pass to \c TE<T>::apply(). \c void -* means "no argument". Only trailing arguments may be void. -* -* This class stores references to a number of arguments it receives in the -* constructor. Later, its function call operator \c operator() may be -* called with a parameter \c t of type \c T. \c operator() will then call -* the static method \c TE<T>::apply(t,args...), where \c args... is the -* sequence of arguments the object was constructed with. \c operator() -* will convert the result to type \c TE<T>::Type and return it. -* -* \c TE should be an extended version of the \c TypeEvaluator class -* template parameter of ForEachType, for instance: -* -* \code -* template <class T> -* struct TypeEvaluator -* { -* typedef T* Type; -* static Type apply(T& t, void* a0) -* { -* return t ? &t : static_cast<T*>(a0); -* } -* }; -* \endcode -* -* In this example, for the value transformation, it takes a reference to a value -* of type T and return the pointer to that value, unless the value evaluates to false -* in boolean context. If the value evaluates to false, it will instead return the -* pointer from the extra argument. -*/ -template<template<class> class TE, class... Args> -class TransformTupleFunctor -{ -mutable std::tuple<Args&...> tup; - -template<class T, std::size_t... I> -inline auto apply(T&& t, const std::index_sequence<I...>& ) -> -decltype(TE<T>::apply(t,std::get<I>(tup)...)) const -{ -return TE<T>::apply(t,std::get<I>(tup)...); -} - -public: -template<class T> -struct TypeEvaluator : public TE<T> -{}; - -TransformTupleFunctor(Args&&... args) -: tup(args...) -{ } - -template<class T> -inline auto operator()(T&& t) -> -decltype(this->apply(t,std::index_sequence_for<Args...>{})) const -{ -return apply(t,std::index_sequence_for<Args...>{}); -} -}; - -template<template<class> class TE, class... Args> -TransformTupleFunctor<TE, Args...> makeTransformTupleFunctor(Args&&... args) -{ -return TransformTupleFunctor<TE, Args...>(args...); -} - -/** -* This function provides functionality similar to genericTransformTuple(), -* although less general and closer in spirit to ForEachType. -* -* \tparam TypeEvaluator Used as the \c TE template argument to -* TransformTupleFunctor internally. -* \tparam Tuple Type of the std::tuple to transform. -* \tparam Args Types of extra argument to call the transformation -* function with. -* -* \param orig Tuple value to be transformed. -* \param args Extra arguments values to provide to the transformation -* function. -* -* The \c TypeEvaluator class template should be suitable as the \c TE -* template argument for TransformTupleFunctor. It has the following form -* (an extension of the \c TypeEvaluator template argument of ForEachType): -* -* \code -* template <class T> -* struct TypeEvaluator -* { -* typedef UserDefined Type; -* -* template<class... Args> -* static Type apply(T& t, Args&... args); -* }; -* \endcode -* -* \sa genericTransforTuple(), ForEachType, AddRefTypeEvaluator, and -* AddPtrTypeEvaluator. -*/ -template<template<class> class TypeEvaluator, class Tuple, class... Args> -auto transformTuple(Tuple&& orig, Args&&... args) -> -decltype(genericTransformTuple(orig, makeTransformTupleFunctor<TypeEvaluator>(args...))) -{ -return genericTransformTuple(orig, makeTransformTupleFunctor<TypeEvaluator>(args...)); -} - -//! \c TypeEvaluator to turn a type \c T into a reference to \c T -/** -* This is suitable as the \c TypeEvaluator template parameter for -* ForEachType and transformTuple(). -*/ -template<class T> -struct AddRefTypeEvaluator -{ -typedef T& Type; -static Type apply(T& t) -{ -return t; -} -}; - -//! \c TypeEvaluator to turn a type \c T into a pointer to \c T -/** -* This is suitable as the \c TypeEvaluator template parameter for -* ForEachType and transformTuple(). -*/ -template<class T> -struct AddPtrTypeEvaluator -{ -typedef typename std::remove_reference<T>::type* Type; -static Type apply(T& t) -{ -return &t; -} -}; - -// Specialization, in case the type is already a reference -template<class T> -struct AddPtrTypeEvaluator<T&> -{ -typedef typename std::remove_reference<T>::type* Type; -static Type apply(T& t) -{ -return &t; -} -}; - -/** -* @brief Type for reverse element access. -* -* Counterpart to ElementType for reverse element access. -*/ -template<int N, class Tuple> -struct AtType -{ -typedef typename std::tuple_element<std::tuple_size<Tuple>::value - N - 1, Tuple>::type Type; -}; - -/** -* @brief Reverse element access. -* -* While Element<...> gives you the arguments beginning at the front of a -* std::tuple, At<...> starts at the end, which may be more convenient, depending -* on how you built your std::tuple. -*/ -template<int N> -struct At -{ -template<typename Tuple> -static typename TupleAccessTraits<typename AtType<N, Tuple>::Type>::NonConstType -get(Tuple& t) -{ -return std::get<std::tuple_size<Tuple>::value - N - 1>(t); -} - -template<typename Tuple> -static typename TupleAccessTraits<typename AtType<N, Tuple>::Type>::ConstType -get(const Tuple& t) -{ -return std::get<std::tuple_size<Tuple>::value - N - 1>(t); -} -}; - -/** -* @brief Deletes all objects pointed to in a std::tuple of pointers. -*/ -template<class Tuple> -struct PointerPairDeletor -{ -template<typename... Ts> -static void apply(std::tuple<Ts...>& t) -{ -Hybrid::forEach(t,[&](auto&& ti){delete ti; ti=nullptr;}); -} -}; - -/** -* @brief Finding the index of a certain type in a std::tuple -* -* \tparam Tuple The std::tuple type to search in. -* \tparam Predicate Predicate which tells FirstPredicateIndex which types -* in Tuple to accept. This should be a class template -* taking a single type template argument. When -* instantiated, it should contain a static member -* constant \c value which should be convertible to bool. -* A type is accepted if \c value is \c true, otherwise it -* is rejected and the next type is tried. Look at IsType -* for a sample implementation. -* \tparam start First index to try. This can be adjusted to skip -* leading tuple elements. -* \tparam size This parameter is an implementation detail and should -* not be adjusted by the users of this class. It should -* always be equal to the size of the std::tuple. -* -* This class can search for a type in std::tuple. It will apply the predicate -* to each type in std::tuple in turn, and set its member constant \c value to -* the index of the first type that was accepted by the predicate. If none -* of the types are accepted by the predicate, a static_assert is triggered. -*/ -template<class Tuple, template<class> class Predicate, std::size_t start = 0, -std::size_t size = std::tuple_size<Tuple>::value> -class FirstPredicateIndex : -public std::conditional<Predicate<typename std::tuple_element<start, -Tuple>::type>::value, -std::integral_constant<std::size_t, start>, -FirstPredicateIndex<Tuple, Predicate, start+1> >::type -{ -static_assert(std::tuple_size<Tuple>::value == size, "The \"size\" " -"template parameter of FirstPredicateIndex is an " -"implementation detail and should never be set " -"explicitly!"); -}; + /** + * This function does for the value of a std::tuple what ForEachType does for the + * type of a std::tuple: it transforms the value using a user-provided policy + * functor. + * + * \param t The std::tuple value to transform. + * \param f The functor to use to transform the values. + * + * The functor should have the following form: + * + * \code + * struct Functor + * { + * template<class> + * struct TypeEvaluator + * { + * typedef user-defined Type; + * }; + * + * template<class T> + * typename TypeEvaluator<T>::Type operator()(T& val); + * + * template<class T> + * typename TypeEvaluator<T>::Type operator()(T& val) const; + * + * template<class T> + * typename TypeEvaluator<T>::Type operator()(const T& val); + * + * template<class T> + * typename TypeEvaluator<T>::Type operator()(const T& val) const; + * }; + * \endcode + * + * The member class template \c TypeEvaluator should be a class template + * suitable as the \c TypeEvaluator template parameter for ForEachType. The + * function call operator \c operator() is used to transform the value; only + * the signatures of \c operator() which are actually used must be present. + */ + template<class Tuple, class Functor> + auto genericTransformTuple(Tuple&& t, Functor&& f) -> + decltype(genericTransformTupleBackend(t, f)) + { + return genericTransformTupleBackend(t, f); + } + + /** + * \tparam TE TypeEvaluator class template. + * \tparam An Type of extra arguments to pass to \c TE<T>::apply(). \c void + * means "no argument". Only trailing arguments may be void. + * + * This class stores references to a number of arguments it receives in the + * constructor. Later, its function call operator \c operator() may be + * called with a parameter \c t of type \c T. \c operator() will then call + * the static method \c TE<T>::apply(t,args...), where \c args... is the + * sequence of arguments the object was constructed with. \c operator() + * will convert the result to type \c TE<T>::Type and return it. + * + * \c TE should be an extended version of the \c TypeEvaluator class + * template parameter of ForEachType, for instance: + * + * \code + * template <class T> + * struct TypeEvaluator + * { + * typedef T* Type; + * static Type apply(T& t, void* a0) + * { + * return t ? &t : static_cast<T*>(a0); + * } + * }; + * \endcode + * + * In this example, for the value transformation, it takes a reference to a value + * of type T and return the pointer to that value, unless the value evaluates to false + * in boolean context. If the value evaluates to false, it will instead return the + * pointer from the extra argument. + */ + template<template<class> class TE, class... Args> + class TransformTupleFunctor + { + mutable std::tuple<Args&...> tup; + + template<class T, std::size_t... I> + inline auto apply(T&& t, const std::index_sequence<I...>& ) -> + decltype(TE<T>::apply(t,std::get<I>(tup)...)) const + { + return TE<T>::apply(t,std::get<I>(tup)...); + } + + public: + template<class T> + struct TypeEvaluator : public TE<T> + {}; + + TransformTupleFunctor(Args&&... args) + : tup(args...) + { } + + template<class T> + inline auto operator()(T&& t) -> + decltype(this->apply(t,std::index_sequence_for<Args...>{})) const + { + return apply(t,std::index_sequence_for<Args...>{}); + } + }; + + template<template<class> class TE, class... Args> + TransformTupleFunctor<TE, Args...> makeTransformTupleFunctor(Args&&... args) + { + return TransformTupleFunctor<TE, Args...>(args...); + } + + /** + * This function provides functionality similar to genericTransformTuple(), + * although less general and closer in spirit to ForEachType. + * + * \tparam TypeEvaluator Used as the \c TE template argument to + * TransformTupleFunctor internally. + * \tparam Tuple Type of the std::tuple to transform. + * \tparam Args Types of extra argument to call the transformation + * function with. + * + * \param orig Tuple value to be transformed. + * \param args Extra arguments values to provide to the transformation + * function. + * + * The \c TypeEvaluator class template should be suitable as the \c TE + * template argument for TransformTupleFunctor. It has the following form + * (an extension of the \c TypeEvaluator template argument of ForEachType): + * + * \code + * template <class T> + * struct TypeEvaluator + * { + * typedef UserDefined Type; + * + * template<class... Args> + * static Type apply(T& t, Args&... args); + * }; + * \endcode + * + * \sa genericTransforTuple(), ForEachType, AddRefTypeEvaluator, and + * AddPtrTypeEvaluator. + */ + template<template<class> class TypeEvaluator, class Tuple, class... Args> + auto transformTuple(Tuple&& orig, Args&&... args) -> + decltype(genericTransformTuple(orig, makeTransformTupleFunctor<TypeEvaluator>(args...))) + { + return genericTransformTuple(orig, makeTransformTupleFunctor<TypeEvaluator>(args...)); + } + + //! \c TypeEvaluator to turn a type \c T into a reference to \c T + /** + * This is suitable as the \c TypeEvaluator template parameter for + * ForEachType and transformTuple(). + */ + template<class T> + struct AddRefTypeEvaluator + { + typedef T& Type; + static Type apply(T& t) + { + return t; + } + }; + + //! \c TypeEvaluator to turn a type \c T into a pointer to \c T + /** + * This is suitable as the \c TypeEvaluator template parameter for + * ForEachType and transformTuple(). + */ + template<class T> + struct AddPtrTypeEvaluator + { + typedef typename std::remove_reference<T>::type* Type; + static Type apply(T& t) + { + return &t; + } + }; + + // Specialization, in case the type is already a reference + template<class T> + struct AddPtrTypeEvaluator<T&> + { + typedef typename std::remove_reference<T>::type* Type; + static Type apply(T& t) + { + return &t; + } + }; + + /** + * @brief Type for reverse element access. + * + * Counterpart to ElementType for reverse element access. + */ + template<int N, class Tuple> + struct AtType + { + typedef typename std::tuple_element<std::tuple_size<Tuple>::value - N - 1, Tuple>::type Type; + }; + + /** + * @brief Reverse element access. + * + * While Element<...> gives you the arguments beginning at the front of a + * std::tuple, At<...> starts at the end, which may be more convenient, depending + * on how you built your std::tuple. + */ + template<int N> + struct At + { + template<typename Tuple> + static typename TupleAccessTraits<typename AtType<N, Tuple>::Type>::NonConstType + get(Tuple& t) + { + return std::get<std::tuple_size<Tuple>::value - N - 1>(t); + } + + template<typename Tuple> + static typename TupleAccessTraits<typename AtType<N, Tuple>::Type>::ConstType + get(const Tuple& t) + { + return std::get<std::tuple_size<Tuple>::value - N - 1>(t); + } + }; + + /** + * @brief Deletes all objects pointed to in a std::tuple of pointers. + */ + template<class Tuple> + struct PointerPairDeletor + { + template<typename... Ts> + static void apply(std::tuple<Ts...>& t) + { + Hybrid::forEach(t,[&](auto&& ti){delete ti; ti=nullptr;}); + } + }; + + /** + * @brief Finding the index of a certain type in a std::tuple + * + * \tparam Tuple The std::tuple type to search in. + * \tparam Predicate Predicate which tells FirstPredicateIndex which types + * in Tuple to accept. This should be a class template + * taking a single type template argument. When + * instantiated, it should contain a static member + * constant \c value which should be convertible to bool. + * A type is accepted if \c value is \c true, otherwise it + * is rejected and the next type is tried. Look at IsType + * for a sample implementation. + * \tparam start First index to try. This can be adjusted to skip + * leading tuple elements. + * \tparam size This parameter is an implementation detail and should + * not be adjusted by the users of this class. It should + * always be equal to the size of the std::tuple. + * + * This class can search for a type in std::tuple. It will apply the predicate + * to each type in std::tuple in turn, and set its member constant \c value to + * the index of the first type that was accepted by the predicate. If none + * of the types are accepted by the predicate, a static_assert is triggered. + */ + template<class Tuple, template<class> class Predicate, std::size_t start = 0, + std::size_t size = std::tuple_size<Tuple>::value> + class FirstPredicateIndex : + public std::conditional<Predicate<typename std::tuple_element<start, + Tuple>::type>::value, + std::integral_constant<std::size_t, start>, + FirstPredicateIndex<Tuple, Predicate, start+1> >::type + { + static_assert(std::tuple_size<Tuple>::value == size, "The \"size\" " + "template parameter of FirstPredicateIndex is an " + "implementation detail and should never be set " + "explicitly!"); + }; #ifndef DOXYGEN -template<class Tuple, template<class> class Predicate, std::size_t size> -class FirstPredicateIndex<Tuple, Predicate, size, size> -{ -static_assert(Std::to_false_type<Tuple>::value, "None of the std::tuple element " -"types matches the predicate!"); -}; + template<class Tuple, template<class> class Predicate, std::size_t size> + class FirstPredicateIndex<Tuple, Predicate, size, size> + { + static_assert(Std::to_false_type<Tuple>::value, "None of the std::tuple element " + "types matches the predicate!"); + }; #endif // !DOXYGEN -/** -* @brief Generator for predicates accepting one particular type -* -* \tparam T The type to accept. -* -* The generated predicate class is useful together with -* FirstPredicateIndex. It will accept exactly the type that is given as -* the \c T template parameter. -*/ -template<class T> -struct IsType -{ -//! @brief The actual predicate -template<class U> -struct Predicate : public std::is_same<T, U> {}; -}; - -/** -* @brief Find the first occurrence of a type in a std::tuple -* -* \tparam Tuple The std::tuple type to search in. -* \tparam T Type to search for. -* \tparam start First index to try. This can be adjusted to skip leading -* std::tuple elements. -* -* This class can search for a particular type in std::tuple. It will check each -* type in the std::tuple in turn, and set its member constant \c value to the -* index of the first occurrence of type was found. If the type was not -* found, a static_assert is triggered. -*/ -template<class Tuple, class T, std::size_t start = 0> -struct FirstTypeIndex : -public FirstPredicateIndex<Tuple, IsType<T>::template Predicate, start> -{ }; - -/** -* \brief Helper template to append a type to a std::tuple -* -* \tparam Tuple The std::tuple type to extend -* \tparam T The type to be appended to the std::tuple -*/ -template<class Tuple, class T> -struct PushBackTuple; - -template<class... Args, class T> -struct PushBackTuple<typename std::tuple<Args...>, T> -{ -typedef typename std::tuple<Args..., T> type; -}; - -/** -* \brief Helper template to prepend a type to a std::tuple -* -* \tparam Tuple The std::tuple type to extend -* \tparam T The type to be prepended to the std::tuple -*/ -template<class Tuple, class T> -struct PushFrontTuple; - -template<class... Args, class T> -struct PushFrontTuple<typename std::tuple<Args...>, T> -{ -typedef typename std::tuple<T, Args...> type; -}; - -/** -* \brief Apply reduce with meta binary function to template -* -* For a tuple\<T0,T1,...,TN-1,TN,...\> the exported result is -* -* F\< ... F\< F\< F\<Seed,T0\>\::type, T1\>\::type, T2\>\::type, ... TN-1\>\::type -* -* \tparam F Binary meta function -* \tparam Tuple Apply reduce operation to this std::tuple -* \tparam Seed Initial value for reduce operation -* \tparam N Reduce the first N std::tuple elements -*/ -template< -template <class, class> class F, -class Tuple, -class Seed=std::tuple<>, -int N=std::tuple_size<Tuple>::value> -struct ReduceTuple -{ -typedef typename ReduceTuple<F, Tuple, Seed, N-1>::type Accumulated; -typedef typename std::tuple_element<N-1, Tuple>::type Value; - -//! Result of the reduce operation -typedef typename F<Accumulated, Value>::type type; -}; - -/** -* \brief Apply reduce with meta binary function to template -* -* Specialization for reduction of 0 elements. -* The exported result type is Seed. -* -* \tparam F Binary meta function -* \tparam Tuple Apply reduce operation to this std::tuple -* \tparam Seed Initial value for reduce operation -*/ -template< -template <class, class> class F, -class Tuple, -class Seed> -struct ReduceTuple<F, Tuple, Seed, 0> -{ -//! Result of the reduce operation -typedef Seed type; -}; - -/** -* \brief Join two std::tuple's -* -* For Head=std::tuple<T0,...,TN> and Tail=std::tuple<S0,...,SM> -* the exported result is std::tuple<T0,..,TN,S0,...,SM>. -* -* \tparam Head Head of resulting std::tuple -* \tparam Tail Tail of resulting std::tuple -*/ -template<class Head, class Tail> -struct JoinTuples -{ -//! Result of the join operation -typedef typename ReduceTuple<PushBackTuple, Tail, Head>::type type; -}; - -/** -* \brief Flatten a std::tuple of std::tuple's -* -* This flattens a std::tuple of tuples std::tuple<std::tuple<T0,...,TN>, std::tuple<S0,...,SM> > -* and exports std::tuple<T0,..,TN,S0,...,SM>. -* -* \tparam TupleTuple A std::tuple of std::tuple's -*/ -template<class Tuple> -struct FlattenTuple -{ -//! Result of the flatten operation -typedef typename ReduceTuple<JoinTuples, Tuple>::type type; -}; - -/** }@ */ + /** + * @brief Generator for predicates accepting one particular type + * + * \tparam T The type to accept. + * + * The generated predicate class is useful together with + * FirstPredicateIndex. It will accept exactly the type that is given as + * the \c T template parameter. + */ + template<class T> + struct IsType + { + //! @brief The actual predicate + template<class U> + struct Predicate : public std::is_same<T, U> {}; + }; + + /** + * @brief Find the first occurrence of a type in a std::tuple + * + * \tparam Tuple The std::tuple type to search in. + * \tparam T Type to search for. + * \tparam start First index to try. This can be adjusted to skip leading + * std::tuple elements. + * + * This class can search for a particular type in std::tuple. It will check each + * type in the std::tuple in turn, and set its member constant \c value to the + * index of the first occurrence of type was found. If the type was not + * found, a static_assert is triggered. + */ + template<class Tuple, class T, std::size_t start = 0> + struct FirstTypeIndex : + public FirstPredicateIndex<Tuple, IsType<T>::template Predicate, start> + { }; + + /** + * \brief Helper template to append a type to a std::tuple + * + * \tparam Tuple The std::tuple type to extend + * \tparam T The type to be appended to the std::tuple + */ + template<class Tuple, class T> + struct PushBackTuple; + + template<class... Args, class T> + struct PushBackTuple<typename std::tuple<Args...>, T> + { + typedef typename std::tuple<Args..., T> type; + }; + + /** + * \brief Helper template to prepend a type to a std::tuple + * + * \tparam Tuple The std::tuple type to extend + * \tparam T The type to be prepended to the std::tuple + */ + template<class Tuple, class T> + struct PushFrontTuple; + + template<class... Args, class T> + struct PushFrontTuple<typename std::tuple<Args...>, T> + { + typedef typename std::tuple<T, Args...> type; + }; + + /** + * \brief Apply reduce with meta binary function to template + * + * For a tuple\<T0,T1,...,TN-1,TN,...\> the exported result is + * + * F\< ... F\< F\< F\<Seed,T0\>\::type, T1\>\::type, T2\>\::type, ... TN-1\>\::type + * + * \tparam F Binary meta function + * \tparam Tuple Apply reduce operation to this std::tuple + * \tparam Seed Initial value for reduce operation + * \tparam N Reduce the first N std::tuple elements + */ + template< + template <class, class> class F, + class Tuple, + class Seed=std::tuple<>, + int N=std::tuple_size<Tuple>::value> + struct ReduceTuple + { + typedef typename ReduceTuple<F, Tuple, Seed, N-1>::type Accumulated; + typedef typename std::tuple_element<N-1, Tuple>::type Value; + + //! Result of the reduce operation + typedef typename F<Accumulated, Value>::type type; + }; + + /** + * \brief Apply reduce with meta binary function to template + * + * Specialization for reduction of 0 elements. + * The exported result type is Seed. + * + * \tparam F Binary meta function + * \tparam Tuple Apply reduce operation to this std::tuple + * \tparam Seed Initial value for reduce operation + */ + template< + template <class, class> class F, + class Tuple, + class Seed> + struct ReduceTuple<F, Tuple, Seed, 0> + { + //! Result of the reduce operation + typedef Seed type; + }; + + /** + * \brief Join two std::tuple's + * + * For Head=std::tuple<T0,...,TN> and Tail=std::tuple<S0,...,SM> + * the exported result is std::tuple<T0,..,TN,S0,...,SM>. + * + * \tparam Head Head of resulting std::tuple + * \tparam Tail Tail of resulting std::tuple + */ + template<class Head, class Tail> + struct JoinTuples + { + //! Result of the join operation + typedef typename ReduceTuple<PushBackTuple, Tail, Head>::type type; + }; + + /** + * \brief Flatten a std::tuple of std::tuple's + * + * This flattens a std::tuple of tuples std::tuple<std::tuple<T0,...,TN>, std::tuple<S0,...,SM> > + * and exports std::tuple<T0,..,TN,S0,...,SM>. + * + * \tparam TupleTuple A std::tuple of std::tuple's + */ + template<class Tuple> + struct FlattenTuple + { + //! Result of the flatten operation + typedef typename ReduceTuple<JoinTuples, Tuple>::type type; + }; + + /** }@ */ } #endif diff --git a/dune/common/tuplevector.hh b/dune/common/tuplevector.hh index 6bbf50332dd142b7281468edaeed3a159572968c..83db32f567459aaf1a832a078d498abbe5bdc9ec 100644 --- a/dune/common/tuplevector.hh +++ b/dune/common/tuplevector.hh @@ -14,10 +14,10 @@ /** -* \file -* \brief Provides the TupleVector class that augments std::tuple by operator[] -* \author Carsten Gräser -*/ + * \file + * \brief Provides the TupleVector class that augments std::tuple by operator[] + * \author Carsten Gräser + */ namespace Dune { @@ -25,62 +25,62 @@ namespace Dune /** -* \brief A class augmenting std::tuple by element access via operator[] -* -* \ingroup Utilities -*/ + * \brief A class augmenting std::tuple by element access via operator[] + * + * \ingroup Utilities + */ template<class... T> class TupleVector : public std::tuple<T...> { -using Base = std::tuple<T...>; + using Base = std::tuple<T...>; -template<class... TT> -using TupleConstructorDetector = decltype(Base(std::declval<TT&&>()...)); + template<class... TT> + using TupleConstructorDetector = decltype(Base(std::declval<TT&&>()...)); -template<class... TT> -using hasTupleConstructor = Dune::Std::is_detected<TupleConstructorDetector, TT...>; + template<class... TT> + using hasTupleConstructor = Dune::Std::is_detected<TupleConstructorDetector, TT...>; public: -/** \brief Construct from a set of arguments -* -* This is only available if you can construct -* the underlying std::tuple from the same argument -* list. -*/ -template<class... TT, -std::enable_if_t<hasTupleConstructor<TT...>::value, int> = 0> -constexpr TupleVector(TT&&... tt) : -Base(std::forward<TT>(tt)...) -{} - -/** \brief Default constructor -*/ -constexpr TupleVector() -{} - -/** \brief Const access to the tuple elements -*/ -template<std::size_t i> -constexpr decltype(auto) operator[](const Dune::index_constant<i>&) const -{ -return std::get<i>(*this); -} - -/** \brief Non-const access to the tuple elements -*/ -template<std::size_t i> -decltype(auto) operator[](const Dune::index_constant<i>&) -{ -return std::get<i>(*this); -} - -/** \brief Number of elements of the tuple */ -static constexpr std::size_t size() -{ -return std::tuple_size<Base>::value; -} + /** \brief Construct from a set of arguments + * + * This is only available if you can construct + * the underlying std::tuple from the same argument + * list. + */ + template<class... TT, + std::enable_if_t<hasTupleConstructor<TT...>::value, int> = 0> + constexpr TupleVector(TT&&... tt) : + Base(std::forward<TT>(tt)...) + {} + + /** \brief Default constructor + */ + constexpr TupleVector() + {} + + /** \brief Const access to the tuple elements + */ + template<std::size_t i> + constexpr decltype(auto) operator[](const Dune::index_constant<i>&) const + { + return std::get<i>(*this); + } + + /** \brief Non-const access to the tuple elements + */ + template<std::size_t i> + decltype(auto) operator[](const Dune::index_constant<i>&) + { + return std::get<i>(*this); + } + + /** \brief Number of elements of the tuple */ + static constexpr std::size_t size() + { + return std::tuple_size<Base>::value; + } }; @@ -88,9 +88,9 @@ return std::tuple_size<Base>::value; template<class... T> constexpr auto makeTupleVector(T&&... t) { -// The std::decay_t<T> is is a slight simplification, -// because std::reference_wrapper needs special care. -return TupleVector<std::decay_t<T>...>(std::forward<T>(t)...); + // The std::decay_t<T> is is a slight simplification, + // because std::reference_wrapper needs special care. + return TupleVector<std::decay_t<T>...>(std::forward<T>(t)...); } diff --git a/dune/common/typelist.hh b/dune/common/typelist.hh index 06c3a36dec7636f5e8e249a02f5d0a4da736b9ff..14b96489b9ee33a13c36afa9f94ce4644cf329db 100644 --- a/dune/common/typelist.hh +++ b/dune/common/typelist.hh @@ -12,232 +12,232 @@ namespace Dune { -/** -* \brief A type that refers to another type -* -* \ingroup TypeUtilities -* -* The referred-to type can be accessed using the contained typedef `type` -* or, if you have a `MetaType` object by using the dereferencing operator. -* -* MetaType<T> is an empty literal class. Objects of type `MetaType<T>` can -* still be used even if `T` is incomplete or non-constructible. They can -* even be used if `T` is complete but non-instatiable -* (e.g. `std::tuple<void>`), although you need to be extra careful to avoid -* attempts to instantiate the template parameter `T` due to -* argument-dependent lookup (ADL). -* -* Objects of type `MetaType` are passed to the generic lambda when -* iterating over a `TypeList` using `Hybrid::forEach()`. -*/ -template<class T> -struct MetaType { -//! The referred-to type -using type = T; -}; - -/** -* \brief A simple type list -* -* \ingroup TypeUtilities -* -* The purpose of this is to encapsulate a list of types. -* This allows, e.g., to pack an argument-pack into one type. -* In contrast to a std::tuple a TypeList can be created -* without creating any object of the stored types. -* -* This can, e.g., be used for overload resolution -* with tag-dispatch where TypeList is used as tag. -* In combination with PriorityTag this allows to emulate -* partial specialization of function templates in -* a sane way, i.e., without the hassle of classic -* specialization of function templates -* -* A `TypeList<T...>` can be iterated over using `Hybrid::forEach()`. For -* the purpose of iterating with `Hybrid::forEach()`, the members of -* `TypeList<T...>{}` are `MetaType<T>{}...`. This allows iteration over -* incomplete and non-constructible types, since no attempt is made to -* create objects of those types: -* \code -* using namespace Hybrid; -* struct NonConstructible { NonConstructible() = delete; }; -* forEach(TypeList<void, NonConstructible, int>{}, [] (auto metaType) { -* std::cout << className<typename decltype(metaType)::type>() -* << std::endl; -* }); -* \endcode -* -* It is also possible to iterate over complete-but-non-instantiable types, -* e.g. `tuple<void>`. But to do so you need to suppress ADL in the -* invocation of `forEach()`, since ADL would try to instanciate complete -* types in the template argument list of `TypeList` in order to find the -* associated namespaces. To suppress ADL you can either use a qualified -* lookup: -* \code -* Hybrid::forEach(TypeList<std::tuple<void> >{}, -* [] (auto metaType) { ... }); -* }); -* \endcode -* or you can enclose the name `forEach` in parentheses: -* \code -* using namespace Hybrid; -* (forEach)(TypeList<std::tuple<void> >{}, [] (auto metaType) { ... }); -* \endcode -*/ -template<class... T> -using TypeList = std::tuple<MetaType<T>...>; - - - -/** -* \brief Check if given type is a TypeList -* -* \ingroup TypeUtilities -* -* The result of the check is encoded in the -* base class of type std::integral_constant<bool, result>. -*/ -template<class T> -struct IsTypeList : std::false_type {}; - -/** -* \copydoc IsTypeList -* -* \ingroup TypeUtilities -*/ -template<class... T> -struct IsTypeList<TypeList<T...> > : std::true_type {}; - - - -/** -* \brief Check if given type is an empty TypeList -* -* \ingroup TypeUtilities -* -* The result of the check is encoded in the -* base class of type std::integral_constant<bool, result>. -*/ -template<class T> -struct IsEmptyTypeList : std::is_same<T, TypeList<> > {}; - - - -template<class T> -struct TypeListSize {}; - -/** -* \brief Get size of TypeList -* -* \ingroup TypeUtilities -* -* The result of is encoded in the base class of -* type std::integral_constant<std::size_t, result>. -*/ -template<class... T> -struct TypeListSize<TypeList<T...>> : std::integral_constant<std::size_t, sizeof...(T)> {}; - - - -template<std::size_t i, class T> -struct TypeListElement {}; - -/** -* \brief Get element of TypeList -* -* \ingroup TypeUtilities -*/ -template<std::size_t i, class... T> -struct TypeListElement<i, TypeList<T...>> -{ -/** -* \brief Export type of i-th element in TypeList -* -* \todo Implement without using std::tuple. -*/ -using type = typename std::tuple_element<i, std::tuple<T...>>::type; - -/** -* \brief Export type of i-th element in TypeList -* -* \todo Implement without using std::tuple. -*/ -using Type = type; -}; - -/** -* \brief Shortcut for TypeListElement<i, T>::type; -*/ -template<std::size_t i, class T> -using TypeListEntry_t = typename TypeListElement<i, T>::type; - -namespace Impl { - -template<template<class...> class Target, class ToDoList, class... Processed> -struct UniqueTypesHelper; - -template<template<class...> class Target, class... Processed> -struct UniqueTypesHelper<Target, TypeList<>, Processed...> -{ -using type = Target<Processed...>; -}; - -template<template<class...> class Target, class T0, class... T, class... Processed> -struct UniqueTypesHelper<Target, TypeList<T0, T...>, Processed...> -{ -using type = std::conditional_t< -Dune::Std::disjunction<std::is_same<T0, Processed>...>::value, -typename UniqueTypesHelper<Target, TypeList<T...>, Processed...>::type, -typename UniqueTypesHelper<Target, TypeList<T...>, T0, Processed...>::type>; -}; - -// Helper for unpacking Dune::TypeList -template<template<class...> class Target, class TL> -struct UnpackTypeList; - -template<template<class...> class Target, class... T> -struct UnpackTypeList<Target, Dune::TypeList<T...>> -{ -using type = Target<T...>; -}; - -} // namespace Impl - -/** \brief Unpack Dune::TypeList -* -* For a given Dune::TypeList<T...> this is an alias for Target<T...>. -*/ -template<template<class...> class Target, class TL> -using UnpackTypeList_t = typename Impl::UnpackTypeList<Target, TL>::type; - -/** \brief Remove duplicates from a list of types -* -* For a given list of types T... instantiate Target<S...>, where -* S... is generated by removing duplicate types from T... . This -* is useful for std::variant which does not like to be instantiated -* with duplicate types. -*/ -template<template<class...> class Target, class... T> -using UniqueTypes_t = typename Impl::UniqueTypesHelper<Target, TypeList<T...>>::type; - -/** \brief Remove duplicates from a Dune::TypeList -* -* For a given Dune::TypeList<T...> this is an alias for Dune::TypeList<S...>, where -* S... is generated by removing duplicate types from T... . -*/ -template<class NonUniqueTypeList> -using UniqueTypeList_t = typename Impl::UniqueTypesHelper<TypeList, NonUniqueTypeList>::type; - -/** \brief Remove duplicates from a Dune::TypeList -* -* For a given Dune::TypeList<T...> this return a Dune::TypeList<S...>, where -* S... is generated by removing duplicate types from T... . -*/ -template<class... T> -constexpr auto uniqueTypeList(TypeList<T...> list) -{ -return typename Impl::UniqueTypesHelper<TypeList, TypeList<T...>>::type{}; -} + /** + * \brief A type that refers to another type + * + * \ingroup TypeUtilities + * + * The referred-to type can be accessed using the contained typedef `type` + * or, if you have a `MetaType` object by using the dereferencing operator. + * + * MetaType<T> is an empty literal class. Objects of type `MetaType<T>` can + * still be used even if `T` is incomplete or non-constructible. They can + * even be used if `T` is complete but non-instatiable + * (e.g. `std::tuple<void>`), although you need to be extra careful to avoid + * attempts to instantiate the template parameter `T` due to + * argument-dependent lookup (ADL). + * + * Objects of type `MetaType` are passed to the generic lambda when + * iterating over a `TypeList` using `Hybrid::forEach()`. + */ + template<class T> + struct MetaType { + //! The referred-to type + using type = T; + }; + + /** + * \brief A simple type list + * + * \ingroup TypeUtilities + * + * The purpose of this is to encapsulate a list of types. + * This allows, e.g., to pack an argument-pack into one type. + * In contrast to a std::tuple a TypeList can be created + * without creating any object of the stored types. + * + * This can, e.g., be used for overload resolution + * with tag-dispatch where TypeList is used as tag. + * In combination with PriorityTag this allows to emulate + * partial specialization of function templates in + * a sane way, i.e., without the hassle of classic + * specialization of function templates + * + * A `TypeList<T...>` can be iterated over using `Hybrid::forEach()`. For + * the purpose of iterating with `Hybrid::forEach()`, the members of + * `TypeList<T...>{}` are `MetaType<T>{}...`. This allows iteration over + * incomplete and non-constructible types, since no attempt is made to + * create objects of those types: + * \code + * using namespace Hybrid; + * struct NonConstructible { NonConstructible() = delete; }; + * forEach(TypeList<void, NonConstructible, int>{}, [] (auto metaType) { + * std::cout << className<typename decltype(metaType)::type>() + * << std::endl; + * }); + * \endcode + * + * It is also possible to iterate over complete-but-non-instantiable types, + * e.g. `tuple<void>`. But to do so you need to suppress ADL in the + * invocation of `forEach()`, since ADL would try to instanciate complete + * types in the template argument list of `TypeList` in order to find the + * associated namespaces. To suppress ADL you can either use a qualified + * lookup: + * \code + * Hybrid::forEach(TypeList<std::tuple<void> >{}, + * [] (auto metaType) { ... }); + * }); + * \endcode + * or you can enclose the name `forEach` in parentheses: + * \code + * using namespace Hybrid; + * (forEach)(TypeList<std::tuple<void> >{}, [] (auto metaType) { ... }); + * \endcode + */ + template<class... T> + using TypeList = std::tuple<MetaType<T>...>; + + + + /** + * \brief Check if given type is a TypeList + * + * \ingroup TypeUtilities + * + * The result of the check is encoded in the + * base class of type std::integral_constant<bool, result>. + */ + template<class T> + struct IsTypeList : std::false_type {}; + + /** + * \copydoc IsTypeList + * + * \ingroup TypeUtilities + */ + template<class... T> + struct IsTypeList<TypeList<T...> > : std::true_type {}; + + + + /** + * \brief Check if given type is an empty TypeList + * + * \ingroup TypeUtilities + * + * The result of the check is encoded in the + * base class of type std::integral_constant<bool, result>. + */ + template<class T> + struct IsEmptyTypeList : std::is_same<T, TypeList<> > {}; + + + + template<class T> + struct TypeListSize {}; + + /** + * \brief Get size of TypeList + * + * \ingroup TypeUtilities + * + * The result of is encoded in the base class of + * type std::integral_constant<std::size_t, result>. + */ + template<class... T> + struct TypeListSize<TypeList<T...>> : std::integral_constant<std::size_t, sizeof...(T)> {}; + + + + template<std::size_t i, class T> + struct TypeListElement {}; + + /** + * \brief Get element of TypeList + * + * \ingroup TypeUtilities + */ + template<std::size_t i, class... T> + struct TypeListElement<i, TypeList<T...>> + { + /** + * \brief Export type of i-th element in TypeList + * + * \todo Implement without using std::tuple. + */ + using type = typename std::tuple_element<i, std::tuple<T...>>::type; + + /** + * \brief Export type of i-th element in TypeList + * + * \todo Implement without using std::tuple. + */ + using Type = type; + }; + + /** + * \brief Shortcut for TypeListElement<i, T>::type; + */ + template<std::size_t i, class T> + using TypeListEntry_t = typename TypeListElement<i, T>::type; + + namespace Impl { + + template<template<class...> class Target, class ToDoList, class... Processed> + struct UniqueTypesHelper; + + template<template<class...> class Target, class... Processed> + struct UniqueTypesHelper<Target, TypeList<>, Processed...> + { + using type = Target<Processed...>; + }; + + template<template<class...> class Target, class T0, class... T, class... Processed> + struct UniqueTypesHelper<Target, TypeList<T0, T...>, Processed...> + { + using type = std::conditional_t< + Dune::Std::disjunction<std::is_same<T0, Processed>...>::value, + typename UniqueTypesHelper<Target, TypeList<T...>, Processed...>::type, + typename UniqueTypesHelper<Target, TypeList<T...>, T0, Processed...>::type>; + }; + + // Helper for unpacking Dune::TypeList + template<template<class...> class Target, class TL> + struct UnpackTypeList; + + template<template<class...> class Target, class... T> + struct UnpackTypeList<Target, Dune::TypeList<T...>> + { + using type = Target<T...>; + }; + + } // namespace Impl + + /** \brief Unpack Dune::TypeList + * + * For a given Dune::TypeList<T...> this is an alias for Target<T...>. + */ + template<template<class...> class Target, class TL> + using UnpackTypeList_t = typename Impl::UnpackTypeList<Target, TL>::type; + + /** \brief Remove duplicates from a list of types + * + * For a given list of types T... instantiate Target<S...>, where + * S... is generated by removing duplicate types from T... . This + * is useful for std::variant which does not like to be instantiated + * with duplicate types. + */ + template<template<class...> class Target, class... T> + using UniqueTypes_t = typename Impl::UniqueTypesHelper<Target, TypeList<T...>>::type; + + /** \brief Remove duplicates from a Dune::TypeList + * + * For a given Dune::TypeList<T...> this is an alias for Dune::TypeList<S...>, where + * S... is generated by removing duplicate types from T... . + */ + template<class NonUniqueTypeList> + using UniqueTypeList_t = typename Impl::UniqueTypesHelper<TypeList, NonUniqueTypeList>::type; + + /** \brief Remove duplicates from a Dune::TypeList + * + * For a given Dune::TypeList<T...> this return a Dune::TypeList<S...>, where + * S... is generated by removing duplicate types from T... . + */ + template<class... T> + constexpr auto uniqueTypeList(TypeList<T...> list) + { + return typename Impl::UniqueTypesHelper<TypeList, TypeList<T...>>::type{}; + } diff --git a/dune/common/typetraits.hh b/dune/common/typetraits.hh index 280cfa2946aa7526a8b875994d8302887769b604..34109bfe8ba06b70905eeb4723919d646613f385 100644 --- a/dune/common/typetraits.hh +++ b/dune/common/typetraits.hh @@ -12,718 +12,718 @@ namespace Dune { -namespace Impl -{ -/// -/** -* @internal -* @brief Helper to make void_t work with gcc versions prior to gcc 5.0. -* -* This was not a compiler bug, but an accidental omission in the C++11 standard (see N3911, CWG issue 1558). -* It is not clearly specified what happens -* with unused template arguments in template aliases. The developers of GCC decided to ignore them, thus making void_t equivalent to void. -* With gcc 5.0 this was changed and the voider-hack is no longer needed. -*/ -template <class...> -struct voider -{ -using type = void; -}; -} - -//! Is void for all valid input types (see N3911). The workhorse for C++11 SFINAE-techniques. -/** -* \ingroup CxxUtilities -*/ -template <class... Types> -using void_t = typename Impl::voider<Types...>::type; - -/** -* @file -* @brief Traits for type conversions and type information. -* @author Markus Blatt, Christian Engwer -*/ -/** @addtogroup CxxUtilities -* -* @{ -*/ - -/** -* @brief Just an empty class -*/ -struct Empty {}; - -/** -* @brief Checks whether two types are interoperable. -* -* Two types are interoperable if conversions in either directions -* exists. -*/ -template<class T1, class T2> -struct IsInteroperable -{ -enum { -/** -* @brief True if either a conversion from T1 to T2 or vice versa -* exists. -*/ -value = std::is_convertible<T1,T2>::value || std::is_convertible<T2,T1>::value -}; -}; - -/** -* @brief Enable typedef if two types are interoperable. -* -* (also see IsInteroperable) -*/ -template<class T1, class T2, class Type> -struct EnableIfInterOperable -: public std::enable_if<IsInteroperable<T1,T2>::value, Type> -{}; - -/** -\brief template which always yields a false value -\tparam T Some type. It should be a type expression involving template -parameters of the class or function using AlwaysFalse. - -Suppose you have a template class. You want to document the required -members of this class in the non-specialized template, but you know that -actually instantiating the non-specialized template is an error. You -can try something like this: -\code -template<typename T> -struct Traits { -static_assert(false, -"Instanciating this non-specialized template is an " -"error. You should use one of the specializations " -"instead."); -//! The type used to frobnicate T -typedef void FrobnicateType; -}; -\endcode -This will trigger static_assert() as soon as the compiler reads the -definition for the Traits template, since it knows that "false" can -never become true, no matter what the template parameters of Traits are. -As a workaround you can use AlwaysFalse: replace <tt>false</tt> by -<tt>AlwaysFalse<T>::value</tt>, like this: -\code -template<typename T> -struct Traits { -static_assert(AlwaysFalse<T>::value, -"Instanciating this non-specialized template is an " -"error. You should use one of the specializations " -"instead."); -//! The type used to frobnicate T -typedef void FrobnicateType; -}; -\endcode -Since there might be an specialization of AlwaysFalse for template -parameter T, the compiler cannot trigger static_assert() until the -type of T is known, that is, until Traits<T> is instantiated. -*/ -template<typename T> -struct AlwaysFalse { -//! always a false value -static const bool value = false; -}; - -/** -\brief template which always yields a true value -\tparam T Some type. It should be a type expression involving template -parameters of the class or function using AlwaysTrue. - -\note This class exists mostly for consistency with AlwaysFalse. -*/ -template<typename T> -struct AlwaysTrue { -//! always a true value -static const bool value = true; -}; - -//! \brief Whether this type acts as a scalar in the context of -//! (hierarchically blocked) containers -/** -All types `T` for which `IsNumber<T>::value` is `true` will act as a -scalar when used with possibly hierarchically blocked containers, such as -`FieldMatrix`, `FieldVector`, `BCRSMatrix`, `BlockVector`, -`MultiTypeBlockVector`, etc. This enables earlier error reporting when -implementing binary container-scalar operators, such as `=` or `*=`. - -By default is `true` for all arithmetic types (as per -`std::is_arithmetic`), and for `T=std::complex<U>`, iff -`IsNumber<U>::value` itself is `true`. - -Should be specialized to `true` for e.g. extended precision types or -automatic differentiation types, or anything else that might sensibly be -an element of a matrix or vector. -*/ -template <typename T> -struct IsNumber -: public std::integral_constant<bool, std::is_arithmetic<T>::value> { -}; + namespace Impl + { + /// + /** + * @internal + * @brief Helper to make void_t work with gcc versions prior to gcc 5.0. + * + * This was not a compiler bug, but an accidental omission in the C++11 standard (see N3911, CWG issue 1558). + * It is not clearly specified what happens + * with unused template arguments in template aliases. The developers of GCC decided to ignore them, thus making void_t equivalent to void. + * With gcc 5.0 this was changed and the voider-hack is no longer needed. + */ + template <class...> + struct voider + { + using type = void; + }; + } + + //! Is void for all valid input types (see N3911). The workhorse for C++11 SFINAE-techniques. + /** + * \ingroup CxxUtilities + */ + template <class... Types> + using void_t = typename Impl::voider<Types...>::type; + + /** + * @file + * @brief Traits for type conversions and type information. + * @author Markus Blatt, Christian Engwer + */ + /** @addtogroup CxxUtilities + * + * @{ + */ + + /** + * @brief Just an empty class + */ + struct Empty {}; + + /** + * @brief Checks whether two types are interoperable. + * + * Two types are interoperable if conversions in either directions + * exists. + */ + template<class T1, class T2> + struct IsInteroperable + { + enum { + /** + * @brief True if either a conversion from T1 to T2 or vice versa + * exists. + */ + value = std::is_convertible<T1,T2>::value || std::is_convertible<T2,T1>::value + }; + }; + + /** + * @brief Enable typedef if two types are interoperable. + * + * (also see IsInteroperable) + */ + template<class T1, class T2, class Type> + struct EnableIfInterOperable + : public std::enable_if<IsInteroperable<T1,T2>::value, Type> + {}; + + /** + \brief template which always yields a false value + \tparam T Some type. It should be a type expression involving template + parameters of the class or function using AlwaysFalse. + + Suppose you have a template class. You want to document the required + members of this class in the non-specialized template, but you know that + actually instantiating the non-specialized template is an error. You + can try something like this: + \code + template<typename T> + struct Traits { + static_assert(false, + "Instanciating this non-specialized template is an " + "error. You should use one of the specializations " + "instead."); + //! The type used to frobnicate T + typedef void FrobnicateType; + }; + \endcode + This will trigger static_assert() as soon as the compiler reads the + definition for the Traits template, since it knows that "false" can + never become true, no matter what the template parameters of Traits are. + As a workaround you can use AlwaysFalse: replace <tt>false</tt> by + <tt>AlwaysFalse<T>::value</tt>, like this: + \code + template<typename T> + struct Traits { + static_assert(AlwaysFalse<T>::value, + "Instanciating this non-specialized template is an " + "error. You should use one of the specializations " + "instead."); + //! The type used to frobnicate T + typedef void FrobnicateType; + }; + \endcode + Since there might be an specialization of AlwaysFalse for template + parameter T, the compiler cannot trigger static_assert() until the + type of T is known, that is, until Traits<T> is instantiated. + */ + template<typename T> + struct AlwaysFalse { + //! always a false value + static const bool value = false; + }; + + /** + \brief template which always yields a true value + \tparam T Some type. It should be a type expression involving template + parameters of the class or function using AlwaysTrue. + + \note This class exists mostly for consistency with AlwaysFalse. + */ + template<typename T> + struct AlwaysTrue { + //! always a true value + static const bool value = true; + }; + + //! \brief Whether this type acts as a scalar in the context of + //! (hierarchically blocked) containers + /** + All types `T` for which `IsNumber<T>::value` is `true` will act as a + scalar when used with possibly hierarchically blocked containers, such as + `FieldMatrix`, `FieldVector`, `BCRSMatrix`, `BlockVector`, + `MultiTypeBlockVector`, etc. This enables earlier error reporting when + implementing binary container-scalar operators, such as `=` or `*=`. + + By default is `true` for all arithmetic types (as per + `std::is_arithmetic`), and for `T=std::complex<U>`, iff + `IsNumber<U>::value` itself is `true`. + + Should be specialized to `true` for e.g. extended precision types or + automatic differentiation types, or anything else that might sensibly be + an element of a matrix or vector. + */ + template <typename T> + struct IsNumber + : public std::integral_constant<bool, std::is_arithmetic<T>::value> { + }; #ifndef DOXYGEN -template <typename T> -struct IsNumber<std::complex<T>> -: public std::integral_constant<bool, IsNumber<T>::value> { -}; + template <typename T> + struct IsNumber<std::complex<T>> + : public std::integral_constant<bool, IsNumber<T>::value> { + }; #endif // DOXYGEN -//! \brief Whether this type has a value of NaN. -/** -* Internally, this is just a forward to `std::is_floating_point<T>`. -*/ -template <typename T> -struct HasNaN -: public std::integral_constant<bool, std::is_floating_point<T>::value> { -}; + //! \brief Whether this type has a value of NaN. + /** + * Internally, this is just a forward to `std::is_floating_point<T>`. + */ + template <typename T> + struct HasNaN + : public std::integral_constant<bool, std::is_floating_point<T>::value> { + }; #ifndef DOXYGEN -template <typename T> -struct HasNaN<std::complex<T>> -: public std::integral_constant<bool, std::is_floating_point<T>::value> { -}; + template <typename T> + struct HasNaN<std::complex<T>> + : public std::integral_constant<bool, std::is_floating_point<T>::value> { + }; #endif // DOXYGEN -//! \brief Whether this type has a value of NaN. -//! \deprecated has_nan is deprecated, use `Dune::HasNaN` instead -/** -* Internally, this is just a forward to `std::is_floating_point<T>`. -*/ -template <typename T> -struct [[deprecated("Has been renamed to 'HasNaN'.")]] has_nan -: HasNaN<T> {}; + //! \brief Whether this type has a value of NaN. + //! \deprecated has_nan is deprecated, use `Dune::HasNaN` instead + /** + * Internally, this is just a forward to `std::is_floating_point<T>`. + */ + template <typename T> + struct [[deprecated("Has been renamed to 'HasNaN'.")]] has_nan + : HasNaN<T> {}; #if defined(DOXYGEN) or HAVE_IS_INDEXABLE_SUPPORT #ifndef DOXYGEN -namespace Impl { + namespace Impl { -template<typename T, typename I, typename = int> -struct IsIndexable -: public std::false_type -{}; + template<typename T, typename I, typename = int> + struct IsIndexable + : public std::false_type + {}; -template<typename T, typename I> -struct IsIndexable<T,I,typename std::enable_if<(sizeof(std::declval<T>()[std::declval<I>()]) > 0),int>::type> -: public std::true_type -{}; + template<typename T, typename I> + struct IsIndexable<T,I,typename std::enable_if<(sizeof(std::declval<T>()[std::declval<I>()]) > 0),int>::type> + : public std::true_type + {}; -} + } #endif // DOXYGEN -//! Type trait to determine whether an instance of T has an operator[](I), i.e. whether it can be indexed with an index of type I. -/** -* \warning Not all compilers support testing for arbitrary index types. In particular, there -* are problems with GCC 4.4 and 4.5. -*/ -template<typename T, typename I = std::size_t> -struct IsIndexable -: public Impl::IsIndexable<T,I> -{}; + //! Type trait to determine whether an instance of T has an operator[](I), i.e. whether it can be indexed with an index of type I. + /** + * \warning Not all compilers support testing for arbitrary index types. In particular, there + * are problems with GCC 4.4 and 4.5. + */ + template<typename T, typename I = std::size_t> + struct IsIndexable + : public Impl::IsIndexable<T,I> + {}; #else // defined(DOXYGEN) or HAVE_IS_INDEXABLE_SUPPORT -// okay, here follows a mess of compiler bug workarounds... -// GCC 4.4 dies if we try to subscript a simple type like int and -// both GCC 4.4 and 4.5 don't like using arbitrary types as subscripts -// for macros. -// So we make sure to only ever attempt the SFINAE for operator[] for -// class types, and to make sure the compiler doesn't become overly eager -// we have to do some lazy evaluation tricks with nested templates and -// stuff. -// Let's get rid of GCC 4.4 ASAP! - - -namespace Impl { - -// simple wrapper template to support the lazy evaluation required -// in _is_indexable -template<typename T> -struct _lazy -{ -template<typename U> -struct evaluate -{ -typedef T type; -}; -}; - -// default version, gets picked if SFINAE fails -template<typename T, typename = int> -struct IsIndexable -: public std::false_type -{}; - -// version for types supporting the subscript operation -template<typename T> -struct IsIndexable<T,decltype(std::declval<T>()[0],0)> -: public std::true_type -{}; - -// helper struct for delaying the evaluation until we are sure -// that T is a class (i.e. until we are outside std::conditional -// below) -struct _check_for_index_operator -{ - -template<typename T> -struct evaluate -: public IsIndexable<T> -{}; - -}; - -} - -// The rationale here is as follows: -// 1) If we have an array, we assume we can index into it. That isn't -// true if I isn't an integral type, but that's why we have the static assertion -// in the body - we could of course try and check whether I is integral, but I -// can't be arsed and want to provide a motivation to switch to a newer compiler... -// 2) If we have a class, we use SFINAE to check for operator[] -// 3) Otherwise, we assume that T does not support indexing -// -// In order to make sure that the compiler doesn't accidentally try the SFINAE evaluation -// on an array or a scalar, we have to resort to lazy evaluation. -template<typename T, typename I = std::size_t> -struct IsIndexable -: public std::conditional< -std::is_array<T>::value, -Impl::_lazy<std::true_type>, -typename std::conditional< -std::is_class<T>::value, -Impl::_check_for_index_operator, -Impl::_lazy<std::false_type> ->::type ->::type::template evaluate<T>::type -{ -static_assert(std::is_same<I,std::size_t>::value,"Your compiler is broken and does not support checking for arbitrary index types"); -}; + // okay, here follows a mess of compiler bug workarounds... + // GCC 4.4 dies if we try to subscript a simple type like int and + // both GCC 4.4 and 4.5 don't like using arbitrary types as subscripts + // for macros. + // So we make sure to only ever attempt the SFINAE for operator[] for + // class types, and to make sure the compiler doesn't become overly eager + // we have to do some lazy evaluation tricks with nested templates and + // stuff. + // Let's get rid of GCC 4.4 ASAP! + + + namespace Impl { + + // simple wrapper template to support the lazy evaluation required + // in _is_indexable + template<typename T> + struct _lazy + { + template<typename U> + struct evaluate + { + typedef T type; + }; + }; + + // default version, gets picked if SFINAE fails + template<typename T, typename = int> + struct IsIndexable + : public std::false_type + {}; + + // version for types supporting the subscript operation + template<typename T> + struct IsIndexable<T,decltype(std::declval<T>()[0],0)> + : public std::true_type + {}; + + // helper struct for delaying the evaluation until we are sure + // that T is a class (i.e. until we are outside std::conditional + // below) + struct _check_for_index_operator + { + + template<typename T> + struct evaluate + : public IsIndexable<T> + {}; + + }; + + } + + // The rationale here is as follows: + // 1) If we have an array, we assume we can index into it. That isn't + // true if I isn't an integral type, but that's why we have the static assertion + // in the body - we could of course try and check whether I is integral, but I + // can't be arsed and want to provide a motivation to switch to a newer compiler... + // 2) If we have a class, we use SFINAE to check for operator[] + // 3) Otherwise, we assume that T does not support indexing + // + // In order to make sure that the compiler doesn't accidentally try the SFINAE evaluation + // on an array or a scalar, we have to resort to lazy evaluation. + template<typename T, typename I = std::size_t> + struct IsIndexable + : public std::conditional< + std::is_array<T>::value, + Impl::_lazy<std::true_type>, + typename std::conditional< + std::is_class<T>::value, + Impl::_check_for_index_operator, + Impl::_lazy<std::false_type> + >::type + >::type::template evaluate<T>::type + { + static_assert(std::is_same<I,std::size_t>::value,"Your compiler is broken and does not support checking for arbitrary index types"); + }; #endif // defined(DOXYGEN) or HAVE_IS_INDEXABLE_SUPPORT -//! Type trait to determine whether an instance of T has an operator[](I), i.e. whether it can be indexed with an index of type I. -//! \deprecated is_indexable is deprecated, use `Dune::IsIndexable` instead -/** -* \warning Not all compilers support testing for arbitrary index types. In particular, there -* are problems with GCC 4.4 and 4.5. -*/ -template<typename T, typename I = std::size_t> -struct [[deprecated("Has been renamed to 'IsIndexable'.")]] is_indexable -: public IsIndexable<T,I> {}; + //! Type trait to determine whether an instance of T has an operator[](I), i.e. whether it can be indexed with an index of type I. + //! \deprecated is_indexable is deprecated, use `Dune::IsIndexable` instead + /** + * \warning Not all compilers support testing for arbitrary index types. In particular, there + * are problems with GCC 4.4 and 4.5. + */ + template<typename T, typename I = std::size_t> + struct [[deprecated("Has been renamed to 'IsIndexable'.")]] is_indexable + : public IsIndexable<T,I> {}; #ifndef DOXYGEN -namespace Impl { -// This function does nothing. -// By passing expressions to this function one can avoid -// "value computed is not used" warnings that may show up -// in a comma expression. -template<class...T> -void ignore(T&&... /*t*/) -{} -} + namespace Impl { + // This function does nothing. + // By passing expressions to this function one can avoid + // "value computed is not used" warnings that may show up + // in a comma expression. + template<class...T> + void ignore(T&&... /*t*/) + {} + } #endif // DOXYGEN -/** -\brief typetrait to check that a class has begin() and end() members -*/ -// default version, gets picked if SFINAE fails -template<typename T, typename = void> -struct IsIterable -: public std::false_type -{}; + /** + \brief typetrait to check that a class has begin() and end() members + */ + // default version, gets picked if SFINAE fails + template<typename T, typename = void> + struct IsIterable + : public std::false_type + {}; #ifndef DOXYGEN -// version for types with begin() and end() -template<typename T> -struct IsIterable<T, decltype(Impl::ignore( -std::declval<T>().begin(), -std::declval<T>().end(), -std::declval<T>().begin() != std::declval<T>().end(), -decltype(std::declval<T>().begin()){std::declval<T>().end()}, -++(std::declval<std::add_lvalue_reference_t<decltype(std::declval<T>().begin())>>()), -*(std::declval<T>().begin()) -))> -: public std::true_type -{}; + // version for types with begin() and end() + template<typename T> + struct IsIterable<T, decltype(Impl::ignore( + std::declval<T>().begin(), + std::declval<T>().end(), + std::declval<T>().begin() != std::declval<T>().end(), + decltype(std::declval<T>().begin()){std::declval<T>().end()}, + ++(std::declval<std::add_lvalue_reference_t<decltype(std::declval<T>().begin())>>()), + *(std::declval<T>().begin()) + ))> + : public std::true_type + {}; #endif -/** -\brief typetrait to check that a class has begin() and end() members -\deprecated is_range is deprecated, use `Dune::IsIterable` instead -*/ -template<typename T, typename = void> -struct [[deprecated("Has been renamed to 'IsIterable'.")]] is_range -: public IsIterable<T> {}; + /** + \brief typetrait to check that a class has begin() and end() members + \deprecated is_range is deprecated, use `Dune::IsIterable` instead + */ + template<typename T, typename = void> + struct [[deprecated("Has been renamed to 'IsIterable'.")]] is_range + : public IsIterable<T> {}; #ifndef DOXYGEN -// this is just a forward declaration -template <class> struct FieldTraits; + // this is just a forward declaration + template <class> struct FieldTraits; #endif -//! Convenient access to FieldTraits<Type>::field_type. -template <class Type> -using field_t = typename FieldTraits<Type>::field_type; + //! Convenient access to FieldTraits<Type>::field_type. + template <class Type> + using field_t = typename FieldTraits<Type>::field_type; -//! Convenient access to FieldTraits<Type>::real_type. -template <class Type> -using real_t = typename FieldTraits<Type>::real_type; + //! Convenient access to FieldTraits<Type>::real_type. + template <class Type> + using real_t = typename FieldTraits<Type>::real_type; #ifndef DOXYGEN -// Implementation of IsTuple -namespace Impl { + // Implementation of IsTuple + namespace Impl { -template<class T> -struct IsTuple : public std::false_type -{}; + template<class T> + struct IsTuple : public std::false_type + {}; -template<class... T> -struct IsTuple<std::tuple<T...>> : public std::true_type -{}; + template<class... T> + struct IsTuple<std::tuple<T...>> : public std::true_type + {}; -} // namespace Impl + } // namespace Impl #endif // DOXYGEN -/** -* \brief Check if T is a std::tuple<...> -* -* The result is exported by deriving from std::true_type or std::false_type. -*/ -template<class T> -struct IsTuple : -public Impl::IsTuple<T> -{}; + /** + * \brief Check if T is a std::tuple<...> + * + * The result is exported by deriving from std::true_type or std::false_type. + */ + template<class T> + struct IsTuple : + public Impl::IsTuple<T> + {}; #ifndef DOXYGEN -// Implementation of IsTupleOrDerived -namespace Impl { + // Implementation of IsTupleOrDerived + namespace Impl { -template<class... T, class Dummy> -std::true_type isTupleOrDerived(const std::tuple<T...>*, Dummy) -{ return {}; } + template<class... T, class Dummy> + std::true_type isTupleOrDerived(const std::tuple<T...>*, Dummy) + { return {}; } -template<class Dummy> -std::false_type isTupleOrDerived(const void*, Dummy) -{ return {}; } + template<class Dummy> + std::false_type isTupleOrDerived(const void*, Dummy) + { return {}; } -} // namespace Impl + } // namespace Impl #endif // DOXYGEN -/** -* \brief Check if T derived from a std::tuple<...> -* -* The result is exported by deriving from std::true_type or std::false_type. -*/ -template<class T> -struct IsTupleOrDerived : -public decltype(Impl::isTupleOrDerived(std::declval<T*>(), true)) -{}; + /** + * \brief Check if T derived from a std::tuple<...> + * + * The result is exported by deriving from std::true_type or std::false_type. + */ + template<class T> + struct IsTupleOrDerived : + public decltype(Impl::isTupleOrDerived(std::declval<T*>(), true)) + {}; #ifndef DOXYGEN -// Implementation of is IsIntegralConstant -namespace Impl { + // Implementation of is IsIntegralConstant + namespace Impl { -template<class T> -struct IsIntegralConstant : public std::false_type -{}; + template<class T> + struct IsIntegralConstant : public std::false_type + {}; -template<class T, T t> -struct IsIntegralConstant<std::integral_constant<T, t>> : public std::true_type -{}; + template<class T, T t> + struct IsIntegralConstant<std::integral_constant<T, t>> : public std::true_type + {}; -} // namespace Impl + } // namespace Impl #endif // DOXYGEN -/** -* \brief Check if T is an std::integral_constant<I, i> -* -* The result is exported by deriving from std::true_type or std::false_type. -*/ -template<class T> -struct IsIntegralConstant : public Impl::IsIntegralConstant<std::decay_t<T>> -{}; - - - -/** -* \brief Compute size of variadic type list -* -* \tparam T Variadic type list -* -* The ::value member gives the size of the variadic type list T... -* This should be equivalent to sizeof...(T). However, with clang -* the latter may produce wrong results if used in template aliases -* due to clang bug 14858 (https://llvm.org/bugs/show_bug.cgi?id=14858). -* -* As a workaround one can use SizeOf<T...>::value instead of sizeof...(T) -* in template aliases for any code that should work with clang < 3.8. -*/ -template<typename... T> -struct SizeOf -: public std::integral_constant<std::size_t,sizeof...(T)> -{}; + /** + * \brief Check if T is an std::integral_constant<I, i> + * + * The result is exported by deriving from std::true_type or std::false_type. + */ + template<class T> + struct IsIntegralConstant : public Impl::IsIntegralConstant<std::decay_t<T>> + {}; + + + + /** + * \brief Compute size of variadic type list + * + * \tparam T Variadic type list + * + * The ::value member gives the size of the variadic type list T... + * This should be equivalent to sizeof...(T). However, with clang + * the latter may produce wrong results if used in template aliases + * due to clang bug 14858 (https://llvm.org/bugs/show_bug.cgi?id=14858). + * + * As a workaround one can use SizeOf<T...>::value instead of sizeof...(T) + * in template aliases for any code that should work with clang < 3.8. + */ + template<typename... T> + struct SizeOf + : public std::integral_constant<std::size_t,sizeof...(T)> + {}; #ifndef DOXYGEN -namespace Impl { - -template<class T, T...> -struct IntegerSequenceHelper; - -// Helper struct to compute the i-th entry of a std::integer_sequence -// -// This could also be implemented using std::get<index>(std::make_tuple(t...)). -// However, the gcc-6 implementation of std::make_tuple increases the instantiation -// depth by 15 levels for each argument, such that the maximal instantiation depth -// is easily hit, especially with clang where it is set to 256. -template<class T, T head, T... tail> -struct IntegerSequenceHelper<T, head, tail...> -{ - -// get first entry -static constexpr auto get(std::integral_constant<std::size_t, 0>) -{ -return std::integral_constant<T, head>(); -} - -// call get with first entry cut off and decremented index -template<std::size_t index, -std::enable_if_t<(index > 0) and (index < sizeof...(tail)+1), int> = 0> -static constexpr auto get(std::integral_constant<std::size_t, index>) -{ -return IntegerSequenceHelper<T, tail...>::get(std::integral_constant<std::size_t, index-1>()); -} - -// use static assertion if index exceeds size -template<std::size_t index, -std::enable_if_t<(index >= sizeof...(tail)+1), int> = 0> -static constexpr auto get(std::integral_constant<std::size_t, index>) -{ -static_assert(index < sizeof...(tail)+1, "index used in IntegerSequenceEntry exceed size"); -} -}; - -} // end namespace Impl + namespace Impl { + + template<class T, T...> + struct IntegerSequenceHelper; + + // Helper struct to compute the i-th entry of a std::integer_sequence + // + // This could also be implemented using std::get<index>(std::make_tuple(t...)). + // However, the gcc-6 implementation of std::make_tuple increases the instantiation + // depth by 15 levels for each argument, such that the maximal instantiation depth + // is easily hit, especially with clang where it is set to 256. + template<class T, T head, T... tail> + struct IntegerSequenceHelper<T, head, tail...> + { + + // get first entry + static constexpr auto get(std::integral_constant<std::size_t, 0>) + { + return std::integral_constant<T, head>(); + } + + // call get with first entry cut off and decremented index + template<std::size_t index, + std::enable_if_t<(index > 0) and (index < sizeof...(tail)+1), int> = 0> + static constexpr auto get(std::integral_constant<std::size_t, index>) + { + return IntegerSequenceHelper<T, tail...>::get(std::integral_constant<std::size_t, index-1>()); + } + + // use static assertion if index exceeds size + template<std::size_t index, + std::enable_if_t<(index >= sizeof...(tail)+1), int> = 0> + static constexpr auto get(std::integral_constant<std::size_t, index>) + { + static_assert(index < sizeof...(tail)+1, "index used in IntegerSequenceEntry exceed size"); + } + }; + + } // end namespace Impl #endif // DOXYGEN -/** -* \brief Get entry of std::integer_sequence -* -* \param seq An object of type std::integer_sequence<...> -* \param i Index -* -* \return The i-th entry of the integer_sequence encoded as std::integral_constant<std::size_t, entry>. -* -*/ -template<class T, T... t, std::size_t index> -constexpr auto integerSequenceEntry(std::integer_sequence<T, t...> /*seq*/, std::integral_constant<std::size_t, index> i) -{ -static_assert(index < sizeof...(t), "index used in IntegerSequenceEntry exceed size"); -return Impl::IntegerSequenceHelper<T, t...>::get(i); -} - - -/** -* \brief Get entry of std::integer_sequence -* -* Computes the i-th entry of the integer_sequence. The result -* is exported as ::value by deriving form std::integral_constant<std::size_t, entry>. -*/ -template<class IntegerSequence, std::size_t index> -struct IntegerSequenceEntry; + /** + * \brief Get entry of std::integer_sequence + * + * \param seq An object of type std::integer_sequence<...> + * \param i Index + * + * \return The i-th entry of the integer_sequence encoded as std::integral_constant<std::size_t, entry>. + * + */ + template<class T, T... t, std::size_t index> + constexpr auto integerSequenceEntry(std::integer_sequence<T, t...> /*seq*/, std::integral_constant<std::size_t, index> i) + { + static_assert(index < sizeof...(t), "index used in IntegerSequenceEntry exceed size"); + return Impl::IntegerSequenceHelper<T, t...>::get(i); + } + + + /** + * \brief Get entry of std::integer_sequence + * + * Computes the i-th entry of the integer_sequence. The result + * is exported as ::value by deriving form std::integral_constant<std::size_t, entry>. + */ + template<class IntegerSequence, std::size_t index> + struct IntegerSequenceEntry; #ifndef DOXYGEN -template<class T, T... t, std::size_t i> -struct IntegerSequenceEntry<std::integer_sequence<T, t...>, i> -: public decltype(Impl::IntegerSequenceHelper<T, t...>::get(std::integral_constant<std::size_t, i>())) -{}; + template<class T, T... t, std::size_t i> + struct IntegerSequenceEntry<std::integer_sequence<T, t...>, i> + : public decltype(Impl::IntegerSequenceHelper<T, t...>::get(std::integral_constant<std::size_t, i>())) + {}; #endif // DOXYGEN -/** -* \brief Type free of internal references that `T` can be converted to. -* -* This is the specialization point for `AutonomousValue` and `autoCopy()`. -* -* If you need to specialize for a proxy type or similar, just specialize -* for the plain type. There are already specializations for -* reference-qualified and cv-qualified types that will just forward to your -* specailixszation. -* -* \note For all specializations, the member type `type` should be -* constructible from `T`. -*/ -template<class T> -struct AutonomousValueType { using type = T; }; - -//! Specialization to remove lvalue references -template<class T> -struct AutonomousValueType<T&> : AutonomousValueType<T> {}; - -//! Specialization to remove rvalue references -template<class T> -struct AutonomousValueType<T&&> : AutonomousValueType<T> {}; - -//! Specialization to remove const qualifiers -template<class T> -struct AutonomousValueType<const T> : AutonomousValueType<T> {}; - -//! Specialization to remove volatile qualifiers -template<class T> -struct AutonomousValueType<volatile T> : AutonomousValueType<T> {}; - -//! Specialization for the proxies of `vector<bool>` -template<> -struct AutonomousValueType<std::vector<bool>::reference> -{ -using type = bool; -}; - -//! Specialization to remove both const and volatile qualifiers -template<class T> -struct AutonomousValueType<volatile const T> : AutonomousValueType<T> {}; - -/** -* \brief Type free of internal references that `T` can be converted to. -* -* Specialize `AutonomousValueType` to add your own mapping. Use -* `autoCopy()` to convert an expression of type `T` to -* `AutonomousValue<T>`. -* -* This type alias determines a type that `T` can be converted to, but that -* will be free of references to other objects that it does not manage. In -* practice it will act like `std::decay_t`, but in addition to removing -* references it will also determine the types that proxies stand in for, -* and the types that expression templates will evaluate to. -* -* "Free of references" means that the converted object will always be valid -* and does not alias any other objects directly or indirectly. The "other -* objects that it does not manage" restriction means that the converted -* object may still contain internal references, but they must be to -* resources that it manages itself. So, an `std::vector` would be an -* autonomous value even though it contains internal references to the -* storage for the elements since it manages that storage itself. -* -* \note For pointers, iterators, and the like the "value" for the purpose -* of `AutonomousValue` is considered to be the identity of the -* pointed-to object, so that object should not be cloned. But then -* you should hopefully never need an autonomous value for those -* anyway... -*/ -template<class T> -using AutonomousValue = typename AutonomousValueType<T>::type; - -/** -* \brief Autonomous copy of an expression's value for use in `auto` type -* deduction -* -* This function is an unproxyfier or an expression evaluator or a fancy -* cast to ensure an expression can be used in `auto` type deduction. It -* ensures two things: -* -* 1. The return value is a prvalue, -* 2. the returned value is self-sufficient, or "autonomous". -* -* The latter means that there will be no references into other objects -* (like containers) which are not guaranteed to be kept alive during the -* lifetime of the returned value. -* -* An example usage would be -* ```c++ -* std::vector<bool> bitvector{24}; -* auto value = autoCopy(bitvector[23]); -* bitvector.resize(42); -* // value still valid -* ``` -* Since `vector<bool>` may use proxies, `auto value = bitvector[23];` would -* mean that the type of `value` is such a proxy. The proxy keeps internal -* references into the vector, and thus can be invalidated by anything that -* modifies the vector -- such as a later call to `resize()`. `autoCopy()` -* lets you work around that problem by copying the value referenced by the -* proxy and converting it to a `bool`. -* -* Another example would be an automatic differentiation library that lets -* you track the operations in a computation, and later ask for derivatives. -* Imagine that your operation involves a parameter function, and you want -* to use that function both with plain types and with automatic -* differentiation types. You might write the parameter function as -* follows: -* ```c++ -* template<class NumberType> -* auto param(NumberType v) -* { -* return 2*v; -* } -* ``` -* If the automatic differentiation library is Adept, this would lead to -* use-after-end-of-life-bugs. The reason is that for efficiency reasons -* Adept does not immidiately evaluate the expression, but instead it -* constructs an expression object that records the kind of expression and -* references to the operands. The expression object is only evaluated when -* it is assigned to an object of some number type -- which will only happen -* after the operands (`v` and the temporary object representing `2`) have -* gone out of scope and been destroyed. Basically, Adept was invented -* before `auto` and rvalue-references were a thing. -* -* This can be handled with `autoCopy()`: -* ```c++ -* template<class NumberType> -* auto param(NumberType v) -* { -* return autoCopy(2*v); -* } -* ``` -* Of course, `autoCopy()` needs to be taught about the expression -* objects of Adept for this to work. -* -* `autoCopy()` will by default simply return the argument as a prvalue of -* the same type with cv-qualifiers removed. This involves one or more -* copy/move operation, so it will only work with types that are in fact -* copyable. And it will incur one copy if the compiler cannot use a move, -* such as when the type of the expression is a `std::array` or a -* `FieldMatrix`. (Any second copy that may semantically be necessary will -* be elided.) -* -* To teach `autoCopy()` about a particular proxy type, specialize -* `Dune::AutonomousValueType`. -* -* \note Do not overload `Dune::autoCopy()` directly. It is meant to be -* found by unqualified or qualified lookup, not by ADL. There is -* little guarantee that your overload will be declared before the -* definition of internal Dune functions that use `autoCopy()`. They -* would need the lazy binding provided by ADL to find your overload, -* but they will probably use unqualified lookup. -*/ -template<class T> -constexpr AutonomousValue<T> autoCopy(T &&v) -{ -return v; -} - -/** @} */ + /** + * \brief Type free of internal references that `T` can be converted to. + * + * This is the specialization point for `AutonomousValue` and `autoCopy()`. + * + * If you need to specialize for a proxy type or similar, just specialize + * for the plain type. There are already specializations for + * reference-qualified and cv-qualified types that will just forward to your + * specailixszation. + * + * \note For all specializations, the member type `type` should be + * constructible from `T`. + */ + template<class T> + struct AutonomousValueType { using type = T; }; + + //! Specialization to remove lvalue references + template<class T> + struct AutonomousValueType<T&> : AutonomousValueType<T> {}; + + //! Specialization to remove rvalue references + template<class T> + struct AutonomousValueType<T&&> : AutonomousValueType<T> {}; + + //! Specialization to remove const qualifiers + template<class T> + struct AutonomousValueType<const T> : AutonomousValueType<T> {}; + + //! Specialization to remove volatile qualifiers + template<class T> + struct AutonomousValueType<volatile T> : AutonomousValueType<T> {}; + + //! Specialization for the proxies of `vector<bool>` + template<> + struct AutonomousValueType<std::vector<bool>::reference> + { + using type = bool; + }; + + //! Specialization to remove both const and volatile qualifiers + template<class T> + struct AutonomousValueType<volatile const T> : AutonomousValueType<T> {}; + + /** + * \brief Type free of internal references that `T` can be converted to. + * + * Specialize `AutonomousValueType` to add your own mapping. Use + * `autoCopy()` to convert an expression of type `T` to + * `AutonomousValue<T>`. + * + * This type alias determines a type that `T` can be converted to, but that + * will be free of references to other objects that it does not manage. In + * practice it will act like `std::decay_t`, but in addition to removing + * references it will also determine the types that proxies stand in for, + * and the types that expression templates will evaluate to. + * + * "Free of references" means that the converted object will always be valid + * and does not alias any other objects directly or indirectly. The "other + * objects that it does not manage" restriction means that the converted + * object may still contain internal references, but they must be to + * resources that it manages itself. So, an `std::vector` would be an + * autonomous value even though it contains internal references to the + * storage for the elements since it manages that storage itself. + * + * \note For pointers, iterators, and the like the "value" for the purpose + * of `AutonomousValue` is considered to be the identity of the + * pointed-to object, so that object should not be cloned. But then + * you should hopefully never need an autonomous value for those + * anyway... + */ + template<class T> + using AutonomousValue = typename AutonomousValueType<T>::type; + + /** + * \brief Autonomous copy of an expression's value for use in `auto` type + * deduction + * + * This function is an unproxyfier or an expression evaluator or a fancy + * cast to ensure an expression can be used in `auto` type deduction. It + * ensures two things: + * + * 1. The return value is a prvalue, + * 2. the returned value is self-sufficient, or "autonomous". + * + * The latter means that there will be no references into other objects + * (like containers) which are not guaranteed to be kept alive during the + * lifetime of the returned value. + * + * An example usage would be + * ```c++ + * std::vector<bool> bitvector{24}; + * auto value = autoCopy(bitvector[23]); + * bitvector.resize(42); + * // value still valid + * ``` + * Since `vector<bool>` may use proxies, `auto value = bitvector[23];` would + * mean that the type of `value` is such a proxy. The proxy keeps internal + * references into the vector, and thus can be invalidated by anything that + * modifies the vector -- such as a later call to `resize()`. `autoCopy()` + * lets you work around that problem by copying the value referenced by the + * proxy and converting it to a `bool`. + * + * Another example would be an automatic differentiation library that lets + * you track the operations in a computation, and later ask for derivatives. + * Imagine that your operation involves a parameter function, and you want + * to use that function both with plain types and with automatic + * differentiation types. You might write the parameter function as + * follows: + * ```c++ + * template<class NumberType> + * auto param(NumberType v) + * { + * return 2*v; + * } + * ``` + * If the automatic differentiation library is Adept, this would lead to + * use-after-end-of-life-bugs. The reason is that for efficiency reasons + * Adept does not immidiately evaluate the expression, but instead it + * constructs an expression object that records the kind of expression and + * references to the operands. The expression object is only evaluated when + * it is assigned to an object of some number type -- which will only happen + * after the operands (`v` and the temporary object representing `2`) have + * gone out of scope and been destroyed. Basically, Adept was invented + * before `auto` and rvalue-references were a thing. + * + * This can be handled with `autoCopy()`: + * ```c++ + * template<class NumberType> + * auto param(NumberType v) + * { + * return autoCopy(2*v); + * } + * ``` + * Of course, `autoCopy()` needs to be taught about the expression + * objects of Adept for this to work. + * + * `autoCopy()` will by default simply return the argument as a prvalue of + * the same type with cv-qualifiers removed. This involves one or more + * copy/move operation, so it will only work with types that are in fact + * copyable. And it will incur one copy if the compiler cannot use a move, + * such as when the type of the expression is a `std::array` or a + * `FieldMatrix`. (Any second copy that may semantically be necessary will + * be elided.) + * + * To teach `autoCopy()` about a particular proxy type, specialize + * `Dune::AutonomousValueType`. + * + * \note Do not overload `Dune::autoCopy()` directly. It is meant to be + * found by unqualified or qualified lookup, not by ADL. There is + * little guarantee that your overload will be declared before the + * definition of internal Dune functions that use `autoCopy()`. They + * would need the lazy binding provided by ADL to find your overload, + * but they will probably use unqualified lookup. + */ + template<class T> + constexpr AutonomousValue<T> autoCopy(T &&v) + { + return v; + } + + /** @} */ } #endif diff --git a/dune/common/typeutilities.hh b/dune/common/typeutilities.hh index d5f57233b60296607de0a90492dc4c79055c79a1..2c462d8ef1036375648edbe440175172288ab292 100644 --- a/dune/common/typeutilities.hh +++ b/dune/common/typeutilities.hh @@ -11,81 +11,81 @@ namespace Dune { -/** -* \file -* \brief Utilities for type computations, constraining overloads, ... -* \author Carsten Gräser -*/ - - -namespace Impl -{ - -template<class This, class... T> -struct disableCopyMoveHelper : public std::is_base_of<This, std::tuple_element_t<0, std::tuple<std::decay_t<T>...>>> -{}; - -template<class This> -struct disableCopyMoveHelper<This> : public std::false_type -{}; - -} // namespace Impl - - -/** -* \brief Helper to disable constructor as copy and move constructor -* -* \ingroup TypeUtilities -* -* Helper typedef to remove constructor with forwarding reference from -* overload set for copy and move constructor or assignment. -*/ -template<class This, class... T> -using disableCopyMove = std::enable_if_t< not Impl::disableCopyMoveHelper<This, T...>::value, int>; - - - -/** -* \brief Helper class for tagging priorities. -* -* \ingroup TypeUtilities -* -* When using multiple overloads of a function -* where some are removed from the overload set -* via SFINAE, the remaining overloads may be ambiguous. -* A prototypic example would be a default overload -* that should be used if the others do not apply. -* -* By adding additional arguments of type PriorityTag<k> -* with increasing priority k to all overloads and calling -* the method with PriorityTag<m> where m is larger or equal -* to the maximal used priority, those can be made unambiguous. -* -* In this case the matching overload with highest priority -* will be used. This is achieved by the fact that PriorityTag<k> -* derives from all types PriorityTag<i> with i less than k. -* -* \tparam priority The priority of this tag. -*/ -template<std::size_t priority> -struct PriorityTag : public PriorityTag<priority-1> -{ -static constexpr std::size_t value = priority; -}; - -/** -* \brief Helper class for tagging priorities. -* -* \ingroup TypeUtilities -* -* PriorityTag<0> does not derive from any -* other PriorityTag. -*/ -template<> -struct PriorityTag<0> -{ -static constexpr std::size_t value = 0; -}; + /** + * \file + * \brief Utilities for type computations, constraining overloads, ... + * \author Carsten Gräser + */ + + + namespace Impl + { + + template<class This, class... T> + struct disableCopyMoveHelper : public std::is_base_of<This, std::tuple_element_t<0, std::tuple<std::decay_t<T>...>>> + {}; + + template<class This> + struct disableCopyMoveHelper<This> : public std::false_type + {}; + + } // namespace Impl + + + /** + * \brief Helper to disable constructor as copy and move constructor + * + * \ingroup TypeUtilities + * + * Helper typedef to remove constructor with forwarding reference from + * overload set for copy and move constructor or assignment. + */ + template<class This, class... T> + using disableCopyMove = std::enable_if_t< not Impl::disableCopyMoveHelper<This, T...>::value, int>; + + + + /** + * \brief Helper class for tagging priorities. + * + * \ingroup TypeUtilities + * + * When using multiple overloads of a function + * where some are removed from the overload set + * via SFINAE, the remaining overloads may be ambiguous. + * A prototypic example would be a default overload + * that should be used if the others do not apply. + * + * By adding additional arguments of type PriorityTag<k> + * with increasing priority k to all overloads and calling + * the method with PriorityTag<m> where m is larger or equal + * to the maximal used priority, those can be made unambiguous. + * + * In this case the matching overload with highest priority + * will be used. This is achieved by the fact that PriorityTag<k> + * derives from all types PriorityTag<i> with i less than k. + * + * \tparam priority The priority of this tag. + */ + template<std::size_t priority> + struct PriorityTag : public PriorityTag<priority-1> + { + static constexpr std::size_t value = priority; + }; + + /** + * \brief Helper class for tagging priorities. + * + * \ingroup TypeUtilities + * + * PriorityTag<0> does not derive from any + * other PriorityTag. + */ + template<> + struct PriorityTag<0> + { + static constexpr std::size_t value = 0; + }; diff --git a/dune/common/unused.hh b/dune/common/unused.hh index 5104ebf3abe02ce233bc1fcab9604369db915a08..d1e833e24f138e01af8a80f92f50940af8968367 100644 --- a/dune/common/unused.hh +++ b/dune/common/unused.hh @@ -5,15 +5,15 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Definition of the DUNE_UNUSED macro for the case that config.h -* is not available -*/ + * \brief Definition of the DUNE_UNUSED macro for the case that config.h + * is not available + */ #ifndef HAS_ATTRIBUTE_UNUSED //! A macro for marking variables that the compiler mistakenly flags as unused, which sometimes happens due to templates. /** -* \ingroup CxxUtilities -*/ + * \ingroup CxxUtilities + */ #define DUNE_UNUSED #else #define DUNE_UNUSED __attribute__((unused)) @@ -21,7 +21,7 @@ /// A macro to mark intentionally unused function parameters with. /** -* \ingroup CxxUtilities -*/ + * \ingroup CxxUtilities + */ #define DUNE_UNUSED_PARAMETER(parm) static_cast<void>(parm) #endif diff --git a/dune/common/vc.hh b/dune/common/vc.hh index 0ebaa21deadd3b6bfc4fed23e96be49c70a62440..8cc7a6b51c0fb431068e7ca8040de70c451d82b5 100644 --- a/dune/common/vc.hh +++ b/dune/common/vc.hh @@ -3,13 +3,13 @@ #include <dune/internal/dune-common.hh> /** -\file + \file -\brief Compatibility header for including <Vc/Vc> + \brief Compatibility header for including <Vc/Vc> -Certain versions (1.3.2) of Vc (https://github.com/VcDevel/Vc) have a -problem with certain compiler versions (g++ 7.2.0) in c++17 mode, see #88. -*/ + Certain versions (1.3.2) of Vc (https://github.com/VcDevel/Vc) have a + problem with certain compiler versions (g++ 7.2.0) in c++17 mode, see #88. + */ #if HAVE_VC diff --git a/dune/common/version.hh b/dune/common/version.hh index c9d842baadaa739f9a058b24f78cf3d8e8cb52bd..b7918b377682a5c36e75f43990d23d041192aa6a 100644 --- a/dune/common/version.hh +++ b/dune/common/version.hh @@ -5,275 +5,275 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Various macros to work with %Dune module version numbers -*/ + * \brief Various macros to work with %Dune module version numbers + */ /** \brief Constructs the preprocessor name used in config.h to hold version numbers -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -* -* \param module The name of the Dune module -* \param type The version number type, one of MAJOR, MINOR, or REVISION -*/ + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + * + * \param module The name of the Dune module + * \param type The version number type, one of MAJOR, MINOR, or REVISION + */ #define DUNE_VERSION_JOIN(module,type) module ## _VERSION_ ## type /** -* \brief True if 'module' has the version major.minor -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has the version major.minor + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_EQUAL(module,major,minor) \ -((DUNE_VERSION_JOIN(module,MAJOR) == major) && \ -(DUNE_VERSION_JOIN(module,MINOR) == minor)) + ((DUNE_VERSION_JOIN(module,MAJOR) == major) && \ + (DUNE_VERSION_JOIN(module,MINOR) == minor)) /** -* \brief True if 'module' has the version major.minor.revision -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has the version major.minor.revision + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_EQUAL_REV(module,major,minor,revision) \ -( DUNE_VERSION_EQUAL(module,major,minor) && \ -(DUNE_VERSION_JOIN(module,REVISION) == revision)) + ( DUNE_VERSION_EQUAL(module,major,minor) && \ + (DUNE_VERSION_JOIN(module,REVISION) == revision)) /** -* \brief True if 'module' has the version major.minor or greater -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has the version major.minor or greater + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_GTE(module,major,minor) \ -((DUNE_VERSION_JOIN(module,MAJOR) > major) \ -|| ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) >= minor))) + ((DUNE_VERSION_JOIN(module,MAJOR) > major) \ + || ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) >= minor))) /** -* \brief True if 'module' has a version less than major.minor -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has a version less than major.minor + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_LT(module,major,minor) \ -! DUNE_VERSION_GTE(module,major,minor) + ! DUNE_VERSION_GTE(module,major,minor) /** -* \brief True if 'module' has the version major.minor or newer -* \note Deprecated, use DUNE_VERSION_GTE instead. -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has the version major.minor or newer + * \note Deprecated, use DUNE_VERSION_GTE instead. + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_NEWER(module,major,minor) \ -DUNE_VERSION_GTE(module,major,minor) + DUNE_VERSION_GTE(module,major,minor) /** -* \brief True if 'module' has a version greater than major.minor -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has a version greater than major.minor + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_GT(module,major,minor) \ -((DUNE_VERSION_JOIN(module,MAJOR) > major) \ -|| ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) > minor))) + ((DUNE_VERSION_JOIN(module,MAJOR) > major) \ + || ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) > minor))) /** -* \brief True if 'module' has a version less than or equal to major.minor -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has a version less than or equal to major.minor + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_LTE(module,major,minor) \ -! DUNE_VERSION_GT(module,major,minor) + ! DUNE_VERSION_GT(module,major,minor) /** -* \brief True if 'module' has the version major.minor.revision or greater -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has the version major.minor.revision or greater + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_GTE_REV(module,major,minor,revision) \ -((DUNE_VERSION_JOIN(module,MAJOR) > major) \ -|| ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) > minor)) \ -|| ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) == minor) \ -&& (DUNE_VERSION_JOIN(module,REVISION) >= revision))) + ((DUNE_VERSION_JOIN(module,MAJOR) > major) \ + || ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) > minor)) \ + || ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) == minor) \ + && (DUNE_VERSION_JOIN(module,REVISION) >= revision))) /** -* \brief True if 'module' has a version lower than major.minor.revision -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has a version lower than major.minor.revision + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_LT_REV(module,major,minor,revision) \ -! DUNE_VERSION_GTE_REV(module,major,minor,revision) + ! DUNE_VERSION_GTE_REV(module,major,minor,revision) /** -* \brief True if 'module' has the version major.minor.revision or newer -* \note Deprecated, use DUNE_VERSION_GTE_REV instead. -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has the version major.minor.revision or newer + * \note Deprecated, use DUNE_VERSION_GTE_REV instead. + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_NEWER_REV(module,major,minor,revision) \ -DUNE_VERSION_GTE_REV(module,major,minor,revision) + DUNE_VERSION_GTE_REV(module,major,minor,revision) /** -* \brief True if 'module' has a greater version than major.minor.revision -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has a greater version than major.minor.revision + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_GT_REV(module,major,minor,revision) \ -((DUNE_VERSION_JOIN(module,MAJOR) > major) \ -|| ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) > minor)) \ -|| ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) == minor) \ -&& (DUNE_VERSION_JOIN(module,REVISION) > revision))) + ((DUNE_VERSION_JOIN(module,MAJOR) > major) \ + || ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) > minor)) \ + || ((DUNE_VERSION_JOIN(module,MAJOR) == major) && (DUNE_VERSION_JOIN(module,MINOR) == minor) \ + && (DUNE_VERSION_JOIN(module,REVISION) > revision))) /** -* \brief True if 'module' has a version lower or equal to major.minor.revision -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief True if 'module' has a version lower or equal to major.minor.revision + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_LTE_REV(module,major,minor,revision) \ -! DUNE_VERSION_GT_REV(module,major,minor,revision) + ! DUNE_VERSION_GT_REV(module,major,minor,revision) /** -* \brief Compute a unique uint id from the major, minor, and revision numbers -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief Compute a unique uint id from the major, minor, and revision numbers + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_VERSION_ID(major,minor,revision) \ -((unsigned int)((major << 24) + (minor << 16) + revision)) + ((unsigned int)((major << 24) + (minor << 16) + revision)) /** -* \brief Compute a unique uint id for the given module -* -* For the DUNE core modules you need to use the following module names: -* - DUNE_COMMON for dune-common -* - DUNE_GRID for dune-grid -* - DUNE_GEOMETRY for dune-geometry -* - DUNE_ISTL for dune-istl -* - DUNE_LOCALFUNCTIONS for dune-localfunctions -* -* For external DUNE modules, you should capitalize the name and -* replace '-' by underscores. For example for the module foo-bar you -* need to use FOO_BAR as module name in the context of this macro. -*/ + * \brief Compute a unique uint id for the given module + * + * For the DUNE core modules you need to use the following module names: + * - DUNE_COMMON for dune-common + * - DUNE_GRID for dune-grid + * - DUNE_GEOMETRY for dune-geometry + * - DUNE_ISTL for dune-istl + * - DUNE_LOCALFUNCTIONS for dune-localfunctions + * + * For external DUNE modules, you should capitalize the name and + * replace '-' by underscores. For example for the module foo-bar you + * need to use FOO_BAR as module name in the context of this macro. + */ #define DUNE_MODULE_VERSION_ID(module) \ -DUNE_VERSION_ID( DUNE_VERSION_JOIN(module,MAJOR), DUNE_VERSION_JOIN(module,MINOR), DUNE_VERSION_JOIN(module,REVISION) ) + DUNE_VERSION_ID( DUNE_VERSION_JOIN(module,MAJOR), DUNE_VERSION_JOIN(module,MINOR), DUNE_VERSION_JOIN(module,REVISION) ) #endif diff --git a/dune/common/visibility.hh b/dune/common/visibility.hh index d4f697d804adfc3a84058e52ae8738ca22df2746..a479c5f5e2123afd28d61f39d312efb841c6a7f2 100644 --- a/dune/common/visibility.hh +++ b/dune/common/visibility.hh @@ -5,25 +5,25 @@ #include <dune/internal/dune-common.hh> /** \file -* \brief Definition of macros controlling symbol visibility at the ABI level. -*/ + * \brief Definition of macros controlling symbol visibility at the ABI level. + */ #ifdef DOXYGEN //! Export a symbol as part of the public ABI. /** -* Mark a class, function or static variable as visible outside the current DSO. -* For now, this is mostly important for templated global variables and functions -* that contain static variables. -*/ + * Mark a class, function or static variable as visible outside the current DSO. + * For now, this is mostly important for templated global variables and functions + * that contain static variables. + */ #define DUNE_EXPORT implementation_defined //! Mark a symbol as being for internal use within the current DSO only. /** -* Mark a class, function or static variable as inaccessible from outside the current DSO. -* Doing so will decrease the size of the symbol table, but you have to be sure that the -* symbol will never have to be accessed from another library or the main executable! -*/ + * Mark a class, function or static variable as inaccessible from outside the current DSO. + * Doing so will decrease the size of the symbol table, but you have to be sure that the + * symbol will never have to be accessed from another library or the main executable! + */ #define DUNE_PRIVATE implementation_defined #else // DOXYGEN diff --git a/dune/python/common/densematrix.hh b/dune/python/common/densematrix.hh index 0bca1ecc9d8ec0a9b2db955a1b379cd42b91415e..4b6e8193e04801fc04f71be551768cb8c48b556e 100644 --- a/dune/python/common/densematrix.hh +++ b/dune/python/common/densematrix.hh @@ -14,61 +14,61 @@ namespace Dune { -namespace Python -{ - -// registerDenseMatrix -// ------------------- - -template< class Matrix > -void registerDenseMatrix ( pybind11::class_< Matrix > cls ) -{ -typedef typename Matrix::field_type field_type; -typedef typename Matrix::row_type row_type; -typedef typename Matrix::row_reference row_reference; - -cls.def( "__getitem__", [] ( Matrix &self, std::size_t i ) -> row_reference { -if( i < self.mat_rows() ) -return self[ i ]; -else -throw pybind11::index_error(); -}, (std::is_reference< row_reference >::value ? pybind11::return_value_policy::reference : pybind11::return_value_policy::move), pybind11::keep_alive< 0, 1 >() ); - -cls.def( "__setitem__", [] ( Matrix &self, std::size_t i, pybind11::object l ) { -if( i < self.mat_rows() ) -{ -row_type v = l.cast< row_type >(); -std::size_t size = std::min( self.mat_cols(), v.size() ); - -for( std::size_t j = 0; j < size; ++j ) -self[ i ][ j ] = v[ j ]; -} -else -throw pybind11::index_error(); -} ); - -cls.def( "__len__", [] ( const Matrix &self ) -> std::size_t { return self.size(); } ); - -cls.def( "invert", [] ( Matrix &self ) { self.invert(); } ); - -cls.def( pybind11::self += pybind11::self ); -cls.def( pybind11::self -= pybind11::self ); -cls.def( pybind11::self *= field_type() ); -cls.def( pybind11::self /= field_type() ); - -cls.def( pybind11::self == pybind11::self ); -cls.def( pybind11::self != pybind11::self ); - -cls.def_property_readonly( "frobenius_norm", [] ( const Matrix &self ) { return self.frobenius_norm(); } ); -cls.def_property_readonly( "frobenius_norm2", [] ( const Matrix &self ) { return self.frobenius_norm2(); } ); -cls.def_property_readonly( "infinity_norm", [] ( const Matrix &self ) { return self.infinity_norm(); } ); -cls.def_property_readonly( "infinity_norm_real", [] ( const Matrix &self ) { return self.infinity_norm_real(); } ); - -cls.def_property_readonly( "rows", [] ( const Matrix &self ) { return self.mat_rows(); } ); -cls.def_property_readonly( "cols", [] ( const Matrix &self ) { return self.mat_cols(); } ); -} - -} // namespace Python + namespace Python + { + + // registerDenseMatrix + // ------------------- + + template< class Matrix > + void registerDenseMatrix ( pybind11::class_< Matrix > cls ) + { + typedef typename Matrix::field_type field_type; + typedef typename Matrix::row_type row_type; + typedef typename Matrix::row_reference row_reference; + + cls.def( "__getitem__", [] ( Matrix &self, std::size_t i ) -> row_reference { + if( i < self.mat_rows() ) + return self[ i ]; + else + throw pybind11::index_error(); + }, (std::is_reference< row_reference >::value ? pybind11::return_value_policy::reference : pybind11::return_value_policy::move), pybind11::keep_alive< 0, 1 >() ); + + cls.def( "__setitem__", [] ( Matrix &self, std::size_t i, pybind11::object l ) { + if( i < self.mat_rows() ) + { + row_type v = l.cast< row_type >(); + std::size_t size = std::min( self.mat_cols(), v.size() ); + + for( std::size_t j = 0; j < size; ++j ) + self[ i ][ j ] = v[ j ]; + } + else + throw pybind11::index_error(); + } ); + + cls.def( "__len__", [] ( const Matrix &self ) -> std::size_t { return self.size(); } ); + + cls.def( "invert", [] ( Matrix &self ) { self.invert(); } ); + + cls.def( pybind11::self += pybind11::self ); + cls.def( pybind11::self -= pybind11::self ); + cls.def( pybind11::self *= field_type() ); + cls.def( pybind11::self /= field_type() ); + + cls.def( pybind11::self == pybind11::self ); + cls.def( pybind11::self != pybind11::self ); + + cls.def_property_readonly( "frobenius_norm", [] ( const Matrix &self ) { return self.frobenius_norm(); } ); + cls.def_property_readonly( "frobenius_norm2", [] ( const Matrix &self ) { return self.frobenius_norm2(); } ); + cls.def_property_readonly( "infinity_norm", [] ( const Matrix &self ) { return self.infinity_norm(); } ); + cls.def_property_readonly( "infinity_norm_real", [] ( const Matrix &self ) { return self.infinity_norm_real(); } ); + + cls.def_property_readonly( "rows", [] ( const Matrix &self ) { return self.mat_rows(); } ); + cls.def_property_readonly( "cols", [] ( const Matrix &self ) { return self.mat_cols(); } ); + } + + } // namespace Python } // namespace Dune diff --git a/dune/python/common/densevector.hh b/dune/python/common/densevector.hh index 3b581962333d24f077d371a80e8d919de0c395b7..eecad8b163d894e2a6bfbe4bf4399ab49b1386e8 100644 --- a/dune/python/common/densevector.hh +++ b/dune/python/common/densevector.hh @@ -16,165 +16,165 @@ namespace Dune { -namespace Python -{ + namespace Python + { + + namespace detail + { + + // registerScalarCopyingDenseVectorMethods + // --------------------------------------- + + template< class T, class... options > + inline static std::enable_if_t< std::is_copy_constructible< T >::value && (T::dimension == 1) > + registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 2 > ) + { + using ValueType = typename T::value_type; -namespace detail -{ + cls.def( "__add__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] += ValueType( a ); return copy; } ); + cls.def( "__add__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] += a; return copy; } ); + cls.def( "__sub__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] -= ValueType( a ); return copy; } ); + cls.def( "__sub__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] -= a; return copy; } ); -// registerScalarCopyingDenseVectorMethods -// --------------------------------------- + cls.def( "__radd__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] = ValueType( a ) + (*copy)[ 0 ]; return copy; } ); + cls.def( "__radd__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] = a + (*copy)[ 0 ]; return copy; } ); + cls.def( "__rsub__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] = ValueType( a ) - (*copy)[ 0 ]; return copy; } ); + cls.def( "__rsub__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] = a - (*copy)[ 0 ]; return copy; } ); + } -template< class T, class... options > -inline static std::enable_if_t< std::is_copy_constructible< T >::value && (T::dimension == 1) > -registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 2 > ) -{ -using ValueType = typename T::value_type; - -cls.def( "__add__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] += ValueType( a ); return copy; } ); -cls.def( "__add__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] += a; return copy; } ); -cls.def( "__sub__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] -= ValueType( a ); return copy; } ); -cls.def( "__sub__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] -= a; return copy; } ); - -cls.def( "__radd__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] = ValueType( a ) + (*copy)[ 0 ]; return copy; } ); -cls.def( "__radd__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] = a + (*copy)[ 0 ]; return copy; } ); -cls.def( "__rsub__", [] ( const T &self, int a ) { T *copy = new T( self ); (*copy)[ 0 ] = ValueType( a ) - (*copy)[ 0 ]; return copy; } ); -cls.def( "__rsub__", [] ( const T &self, const ValueType &a ) { T *copy = new T( self ); (*copy)[ 0 ] = a - (*copy)[ 0 ]; return copy; } ); -} - -template< class T, class... options > -inline static std::enable_if_t< std::is_copy_constructible< T >::value > -registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 1 > ) -{ -using ValueType = typename T::value_type; - -cls.def( "__add__", [] ( pybind11::object self, int a ) { -if( a != 0 ) -throw pybind11::value_error( "Cannot add " + std::to_string( a ) + " to multidimensional dense vector." ); -return self; -} ); -cls.def( "__sub__", [] ( pybind11::object self, int a ) { -if( a != 0 ) -throw pybind11::value_error( "Cannot subtract " + std::to_string( a ) + " from multidimensional dense vector." ); -return self; -} ); - -cls.def( "__radd__", [] ( pybind11::object self, int a ) { -if( a != 0 ) -throw pybind11::value_error( "Cannot add multidimensional dense vector to " + std::to_string( a ) + "." ); -return self; -} ); -cls.def( "__rsub__", [] ( const T &self, int a ) { -if( a != 0 ) -throw pybind11::value_error( "Cannot subtract multidimensional dense vector from " + std::to_string( a ) + "." ); -T *copy = new T( self ); *copy *= ValueType( -1 ); return copy; -} ); -} - -template< class T, class... options > -inline static void registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 0 > ) -{} - -template< class T, class... options > -inline static void registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls ) -{ -registerScalarCopyingDenseVectorMethods ( cls, PriorityTag< 42 >() ); -} + template< class T, class... options > + inline static std::enable_if_t< std::is_copy_constructible< T >::value > + registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 1 > ) + { + using ValueType = typename T::value_type; + cls.def( "__add__", [] ( pybind11::object self, int a ) { + if( a != 0 ) + throw pybind11::value_error( "Cannot add " + std::to_string( a ) + " to multidimensional dense vector." ); + return self; + } ); + cls.def( "__sub__", [] ( pybind11::object self, int a ) { + if( a != 0 ) + throw pybind11::value_error( "Cannot subtract " + std::to_string( a ) + " from multidimensional dense vector." ); + return self; + } ); + cls.def( "__radd__", [] ( pybind11::object self, int a ) { + if( a != 0 ) + throw pybind11::value_error( "Cannot add multidimensional dense vector to " + std::to_string( a ) + "." ); + return self; + } ); + cls.def( "__rsub__", [] ( const T &self, int a ) { + if( a != 0 ) + throw pybind11::value_error( "Cannot subtract multidimensional dense vector from " + std::to_string( a ) + "." ); + T *copy = new T( self ); *copy *= ValueType( -1 ); return copy; + } ); + } + template< class T, class... options > + inline static void registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 0 > ) + {} -// registerCopyingDenseVectorMethods -// --------------------------------- + template< class T, class... options > + inline static void registerScalarCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls ) + { + registerScalarCopyingDenseVectorMethods ( cls, PriorityTag< 42 >() ); + } -template< class T, class... options > -inline static std::enable_if_t< std::is_copy_constructible< T >::value > -registerCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 1 > ) -{ -using ValueType = typename T::value_type; -using pybind11::operator""_a; -cls.def( "__pos__", [] ( pybind11::object self ) { return self; } ); -cls.def( "__neg__", [] ( T &self ) { T *copy = new T( self ); *copy *= ValueType( -1 ); return copy; } ); -cls.def( pybind11::self + pybind11::self ); -cls.def( pybind11::self - pybind11::self ); + // registerCopyingDenseVectorMethods + // --------------------------------- -cls.def( "__add__", [] ( T &self, pybind11::list x ) { return self + x.cast< T >(); }, "x"_a ); -cls.def( "__sub__", [] ( T &self, pybind11::list x ) { return self - x.cast< T >(); }, "x"_a ); + template< class T, class... options > + inline static std::enable_if_t< std::is_copy_constructible< T >::value > + registerCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 1 > ) + { + using ValueType = typename T::value_type; -cls.def( "__radd__", [] ( T &self, pybind11::list x ) { return x.cast< T >() + self; }, "x"_a ); -cls.def( "__rsub__", [] ( T &self, pybind11::list x ) { return x.cast< T >() - self; }, "x"_a ); + using pybind11::operator""_a; -cls.def( "__mul__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy *= x; return copy; }, "x"_a ); -cls.def( "__div__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy /= x; return copy; }, "x"_a ); -cls.def( "__truediv__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy /= x; return copy; }, "x"_a ); + cls.def( "__pos__", [] ( pybind11::object self ) { return self; } ); + cls.def( "__neg__", [] ( T &self ) { T *copy = new T( self ); *copy *= ValueType( -1 ); return copy; } ); -cls.def( "__rmul__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy *= x; return copy; }, "x"_a ); -} + cls.def( pybind11::self + pybind11::self ); + cls.def( pybind11::self - pybind11::self ); -template< class T, class... options > -inline static void registerCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 0 > ) -{} + cls.def( "__add__", [] ( T &self, pybind11::list x ) { return self + x.cast< T >(); }, "x"_a ); + cls.def( "__sub__", [] ( T &self, pybind11::list x ) { return self - x.cast< T >(); }, "x"_a ); -template< class T, class... options > -inline static void registerCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls ) -{ -registerCopyingDenseVectorMethods ( cls, PriorityTag< 42 >() ); -} + cls.def( "__radd__", [] ( T &self, pybind11::list x ) { return x.cast< T >() + self; }, "x"_a ); + cls.def( "__rsub__", [] ( T &self, pybind11::list x ) { return x.cast< T >() - self; }, "x"_a ); -} // namespace detail + cls.def( "__mul__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy *= x; return copy; }, "x"_a ); + cls.def( "__div__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy /= x; return copy; }, "x"_a ); + cls.def( "__truediv__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy /= x; return copy; }, "x"_a ); + cls.def( "__rmul__", [] ( const T &self, ValueType x ) { T *copy = new T( self ); *copy *= x; return copy; }, "x"_a ); + } + template< class T, class... options > + inline static void registerCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls, PriorityTag< 0 > ) + {} -// registerDenseVector -// ------------------- + template< class T, class... options > + inline static void registerCopyingDenseVectorMethods ( pybind11::class_< T, options... > cls ) + { + registerCopyingDenseVectorMethods ( cls, PriorityTag< 42 >() ); + } -template< class T, class... options > -inline static void registerDenseVector ( pybind11::class_< T, options... > cls ) -{ -using ValueType = typename T::value_type; + } // namespace detail + + + + // registerDenseVector + // ------------------- + + template< class T, class... options > + inline static void registerDenseVector ( pybind11::class_< T, options... > cls ) + { + using ValueType = typename T::value_type; -using pybind11::operator""_a; + using pybind11::operator""_a; -cls.def( "assign", [] ( T &self, const T &x ) { self = x; }, "x"_a ); + cls.def( "assign", [] ( T &self, const T &x ) { self = x; }, "x"_a ); -cls.def( "__getitem__", [] ( const T &self, std::size_t i ) -> ValueType { -if( i < self.size() ) -return self[ i ]; -else -throw pybind11::index_error(); -}, "i"_a ); + cls.def( "__getitem__", [] ( const T &self, std::size_t i ) -> ValueType { + if( i < self.size() ) + return self[ i ]; + else + throw pybind11::index_error(); + }, "i"_a ); -cls.def( "__setitem__", [] ( T &self, std::size_t i, ValueType x ) { -if( i < self.size() ) -self[ i ] = x; -else -throw pybind11::index_error(); -}, "i"_a, "x"_a ); + cls.def( "__setitem__", [] ( T &self, std::size_t i, ValueType x ) { + if( i < self.size() ) + self[ i ] = x; + else + throw pybind11::index_error(); + }, "i"_a, "x"_a ); -cls.def( "__len__", [] ( const T &self ) -> std::size_t { return self.size(); } ); + cls.def( "__len__", [] ( const T &self ) -> std::size_t { return self.size(); } ); -cls.def( pybind11::self += pybind11::self ); -cls.def( pybind11::self -= pybind11::self ); + cls.def( pybind11::self += pybind11::self ); + cls.def( pybind11::self -= pybind11::self ); -cls.def( pybind11::self == pybind11::self ); -cls.def( pybind11::self != pybind11::self ); + cls.def( pybind11::self == pybind11::self ); + cls.def( pybind11::self != pybind11::self ); -cls.def( pybind11::self += ValueType() ); -cls.def( pybind11::self -= ValueType() ); -cls.def( pybind11::self *= ValueType() ); -cls.def( pybind11::self /= ValueType() ); + cls.def( pybind11::self += ValueType() ); + cls.def( pybind11::self -= ValueType() ); + cls.def( pybind11::self *= ValueType() ); + cls.def( pybind11::self /= ValueType() ); -detail::registerOneTensorInterface( cls ); -detail::registerCopyingDenseVectorMethods( cls ); -detail::registerScalarCopyingDenseVectorMethods( cls ); + detail::registerOneTensorInterface( cls ); + detail::registerCopyingDenseVectorMethods( cls ); + detail::registerScalarCopyingDenseVectorMethods( cls ); -pybind11::implicitly_convertible< pybind11::list, T >(); -} + pybind11::implicitly_convertible< pybind11::list, T >(); + } -} // namespace Python + } // namespace Python } // namespace Dune diff --git a/dune/python/common/dimrange.hh b/dune/python/common/dimrange.hh index 186d408994d9fc3ba5c65f66d692a1436c88a46a..9ada1d7f814eb9531c114da7304db01096817c46 100644 --- a/dune/python/common/dimrange.hh +++ b/dune/python/common/dimrange.hh @@ -19,87 +19,87 @@ namespace Dune { -namespace Python -{ - -namespace detail -{ - -template< class T > -inline static constexpr T sum () noexcept -{ -return static_cast< T >( 0 ); -} - -template< class T > -inline static constexpr T sum ( T a ) noexcept -{ -return a; -} - -template< class T, class... U > -inline static constexpr T sum ( T a, T b, U... c ) noexcept -{ -return sum( a+b, c... ); -} - - -template< class T, class Enable = void > -struct DimRange; - -template< class T > -struct DimRange< T, std::enable_if_t< std::is_arithmetic< T >::value > > -: public std::integral_constant< std::size_t, 1 > -{}; - -template< class... T > -struct DimRange< std::tuple< T... >, void > -: public std::integral_constant< std::size_t, sum< std::size_t >( DimRange< T >::value... ) > -{}; - -template< class K, int n > -struct DimRange< FieldVector< K, n >, void > -: public std::integral_constant< std::size_t, n > -{}; - -template< class K, int m, int n > -struct DimRange< FieldMatrix< K, m, n >, void > -: public std::integral_constant< std::size_t, m*n > -{}; + namespace Python + { + + namespace detail + { + + template< class T > + inline static constexpr T sum () noexcept + { + return static_cast< T >( 0 ); + } + + template< class T > + inline static constexpr T sum ( T a ) noexcept + { + return a; + } + + template< class T, class... U > + inline static constexpr T sum ( T a, T b, U... c ) noexcept + { + return sum( a+b, c... ); + } + + + template< class T, class Enable = void > + struct DimRange; + + template< class T > + struct DimRange< T, std::enable_if_t< std::is_arithmetic< T >::value > > + : public std::integral_constant< std::size_t, 1 > + {}; + + template< class... T > + struct DimRange< std::tuple< T... >, void > + : public std::integral_constant< std::size_t, sum< std::size_t >( DimRange< T >::value... ) > + {}; + + template< class K, int n > + struct DimRange< FieldVector< K, n >, void > + : public std::integral_constant< std::size_t, n > + {}; + + template< class K, int m, int n > + struct DimRange< FieldMatrix< K, m, n >, void > + : public std::integral_constant< std::size_t, m*n > + {}; #if HAVE_DUNE_TYPETREE -template< class T > -struct DimRange< T, std::enable_if_t< std::is_same< typename T::NodeTag, Dune::TypeTree::CompositeNodeTag >::value > > -: public DimRange< typename T::ChildTypes > -{}; - -template< class T > -struct DimRange< T, std::enable_if_t< std::is_same< typename T::NodeTag, Dune::TypeTree::PowerNodeTag >::value > > -: public std::integral_constant< std::size_t, sum< int >( T::CHILDREN * DimRange< typename T::ChildType >::value ) > -{}; + template< class T > + struct DimRange< T, std::enable_if_t< std::is_same< typename T::NodeTag, Dune::TypeTree::CompositeNodeTag >::value > > + : public DimRange< typename T::ChildTypes > + {}; + + template< class T > + struct DimRange< T, std::enable_if_t< std::is_same< typename T::NodeTag, Dune::TypeTree::PowerNodeTag >::value > > + : public std::integral_constant< std::size_t, sum< int >( T::CHILDREN * DimRange< typename T::ChildType >::value ) > + {}; #endif // #if HAVE_DUNE_TYPETREE -template< class T > -struct DimRange< T, std::enable_if_t< std::is_class< typename T::FiniteElement >::value > > -: public DimRange< std::decay_t< decltype( std::declval< typename T::FiniteElement >().localBasis() ) > > -{}; + template< class T > + struct DimRange< T, std::enable_if_t< std::is_class< typename T::FiniteElement >::value > > + : public DimRange< std::decay_t< decltype( std::declval< typename T::FiniteElement >().localBasis() ) > > + {}; -template< class T > -struct DimRange< T, std::enable_if_t< std::is_same< std::size_t, decltype( static_cast< std::size_t >( T::Traits::dimRange ) ) >::value > > -: public std::integral_constant< std::size_t, static_cast< std::size_t >( T::Traits::dimRange ) > -{}; + template< class T > + struct DimRange< T, std::enable_if_t< std::is_same< std::size_t, decltype( static_cast< std::size_t >( T::Traits::dimRange ) ) >::value > > + : public std::integral_constant< std::size_t, static_cast< std::size_t >( T::Traits::dimRange ) > + {}; -} // namespace detail + } // namespace detail -// DimRange -// -------- + // DimRange + // -------- -template< class T > -using DimRange = detail::DimRange< T >; + template< class T > + using DimRange = detail::DimRange< T >; -} // namespace Python + } // namespace Python } // namespace Dune diff --git a/dune/python/common/dynmatrix.hh b/dune/python/common/dynmatrix.hh index 9f0caa4954155908a91af2840d4d0dc68ca17c15..53ff330dc42bc3193f2d014cee087ab0192d5259 100644 --- a/dune/python/common/dynmatrix.hh +++ b/dune/python/common/dynmatrix.hh @@ -19,57 +19,57 @@ namespace Dune { -namespace Python -{ - -template< class K > -static void registerDynamicMatrix ( pybind11::handle scope ) -{ -typedef Dune::DynamicMatrix< K > DM; - -auto cls = insertClass< DM >( scope, "DynamicMatrix", -GenerateTypeName("Dune::DynamicMatrix",MetaType<K>()), -IncludeFiles{"dune/common/dynmatrix.hh"} ).first; - -cls.def( pybind11::init( [] () { return new DM(); } ) ); - -cls.def( pybind11::init( [] ( pybind11::list rows ) { -std::size_t numRows = rows.size(); -std::size_t numCols = (numRows > 0 ? rows[ 0 ].cast< pybind11::list >().size() : 0); - -DM *self = new DM( numRows, numCols, K( 0 ) ); -for( std::size_t i = 0; i < numRows; ++i ) -{ -pybind11::list row = rows[ i ].cast< pybind11::list >(); -std::size_t numCol = row.size(); -assert(numCols >= numCol); // dense matrix constructed with list having entries of different length -for( std::size_t j = 0; j < numCol; ++j ) -(*self)[ i ][ j ] = row[ j ].template cast< K >(); -} -return self; -} ) ); - -cls.def("__repr__", -[] (const DM& m) { -std::string repr = "Dune::DynamicMatrix:\n("; - -for(unsigned int r = 0; r < m.rows(); r++) -{ -repr += "("; -for (unsigned int c = 0; c < m.cols(); c++) -repr += (c > 0 ? ", " : "") + std::to_string(m[r][c]); -repr += std::string(")") + (r < m.rows() - 1 ? "\n" : ""); -} - -repr += ")"; - -return repr; -}); - -registerDenseMatrix<DM>(cls); -} - -} // namespace Python + namespace Python + { + + template< class K > + static void registerDynamicMatrix ( pybind11::handle scope ) + { + typedef Dune::DynamicMatrix< K > DM; + + auto cls = insertClass< DM >( scope, "DynamicMatrix", + GenerateTypeName("Dune::DynamicMatrix",MetaType<K>()), + IncludeFiles{"dune/common/dynmatrix.hh"} ).first; + + cls.def( pybind11::init( [] () { return new DM(); } ) ); + + cls.def( pybind11::init( [] ( pybind11::list rows ) { + std::size_t numRows = rows.size(); + std::size_t numCols = (numRows > 0 ? rows[ 0 ].cast< pybind11::list >().size() : 0); + + DM *self = new DM( numRows, numCols, K( 0 ) ); + for( std::size_t i = 0; i < numRows; ++i ) + { + pybind11::list row = rows[ i ].cast< pybind11::list >(); + std::size_t numCol = row.size(); + assert(numCols >= numCol); // dense matrix constructed with list having entries of different length + for( std::size_t j = 0; j < numCol; ++j ) + (*self)[ i ][ j ] = row[ j ].template cast< K >(); + } + return self; + } ) ); + + cls.def("__repr__", + [] (const DM& m) { + std::string repr = "Dune::DynamicMatrix:\n("; + + for(unsigned int r = 0; r < m.rows(); r++) + { + repr += "("; + for (unsigned int c = 0; c < m.cols(); c++) + repr += (c > 0 ? ", " : "") + std::to_string(m[r][c]); + repr += std::string(")") + (r < m.rows() - 1 ? "\n" : ""); + } + + repr += ")"; + + return repr; + }); + + registerDenseMatrix<DM>(cls); + } + + } // namespace Python } // namespace Dune diff --git a/dune/python/common/dynvector.hh b/dune/python/common/dynvector.hh index 554341abcbbdf952e5959c1af86b5dc06d28e6a3..fb47f469c014455b00960c5464b704ec23ac94a1 100644 --- a/dune/python/common/dynvector.hh +++ b/dune/python/common/dynvector.hh @@ -19,46 +19,46 @@ namespace Dune { -namespace Python -{ + namespace Python + { -template< class K > -void registerDynamicVector ( pybind11::handle scope ) -{ -using pybind11::operator""_a; + template< class K > + void registerDynamicVector ( pybind11::handle scope ) + { + using pybind11::operator""_a; -typedef Dune::DynamicVector< K > DV; + typedef Dune::DynamicVector< K > DV; -auto cls = insertClass< DV >( scope, "DynamicVector", -GenerateTypeName("Dune::DynamicVector",MetaType<K>()), -IncludeFiles{"dune/common/dynvector.hh"} ).first; + auto cls = insertClass< DV >( scope, "DynamicVector", + GenerateTypeName("Dune::DynamicVector",MetaType<K>()), + IncludeFiles{"dune/common/dynvector.hh"} ).first; -cls.def( pybind11::init( [] () { return new DV(); } ) ); + cls.def( pybind11::init( [] () { return new DV(); } ) ); -cls.def( pybind11::init( [] ( pybind11::list x ) { -std::size_t size = x.size(); -DV *self = new DV( size, K( 0 ) ); -for( std::size_t i = 0; i < size; ++i ) -(*self)[ i ] = x[ i ].template cast< K >(); -return self; -} ), "x"_a ); + cls.def( pybind11::init( [] ( pybind11::list x ) { + std::size_t size = x.size(); + DV *self = new DV( size, K( 0 ) ); + for( std::size_t i = 0; i < size; ++i ) + (*self)[ i ] = x[ i ].template cast< K >(); + return self; + } ), "x"_a ); -cls.def("__repr__", -[] (const DV &v) { -std::string repr = "Dune::DynamicVector: ("; + cls.def("__repr__", + [] (const DV &v) { + std::string repr = "Dune::DynamicVector: ("; -for (std::size_t i = 0; i < v.size(); ++i) -repr += (i > 0 ? ", " : "") + std::to_string(v[i]); + for (std::size_t i = 0; i < v.size(); ++i) + repr += (i > 0 ? ", " : "") + std::to_string(v[i]); -repr += ")"; + repr += ")"; -return repr; -}); + return repr; + }); -registerDenseVector<DV>(cls); -} + registerDenseVector<DV>(cls); + } -} // namespace Python + } // namespace Python } // namespace Dune diff --git a/dune/python/common/fmatrix.hh b/dune/python/common/fmatrix.hh index 37a5e39300108c63551ce27015a002b4bb3c0f64..5993162f8921f89e816f279a35f9a8adbf9819e1 100644 --- a/dune/python/common/fmatrix.hh +++ b/dune/python/common/fmatrix.hh @@ -21,86 +21,86 @@ namespace Dune { -namespace Python -{ - -// registerFieldMatrix -// ------------------- - -template< class K, int m, int n, class ...options > -void registerFieldMatrix ( pybind11::handle scope, pybind11::class_<Dune::FieldMatrix<K,m,n>, options...> cls ) -{ -typedef Dune::FieldMatrix< K, m, n > FM; -using pybind11::operator""_a; - -if( (m == 1) && (n == 1) ) -{ -cls.def( pybind11::init( [] ( int a ) { return new FM( K( a ) ); } ) ); -cls.def( pybind11::init( [] ( K a ) { return new FM( a ); } ) ); -cls.def( "__float__", [] ( const FM &self ) { return self[ 0 ][ 0 ]; } ); -pybind11::implicitly_convertible< int, FM >(); -pybind11::implicitly_convertible< K, FM >(); -} - -cls.def( pybind11::init( [] () { return new FM( K( 0 ) ); } ) ); - -cls.def( pybind11::init( [] ( pybind11::list rows ) { -FM *self = new FM( K( 0 ) ); - -const std::size_t numRows = std::min( static_cast< std::size_t >( m ), rows.size() ); -for( std::size_t i = 0; i < numRows; ++i ) -(*self)[ i ] = pybind11::cast< Dune::FieldVector< K, n > >( rows[ i ] ); -return self; -} ) ); - -pybind11::implicitly_convertible< pybind11::list, FM >(); - -cls.def( "__str__", [] ( const FM &self ) { -std::string s = "("; -for( int i = 0; i < m; ++i ) -{ -s += (i > 0 ? "\n(" : "("); -for( int j = 0; j < n; ++j ) -s += (j > 0 ? ", " : "") + std::to_string( self[ i ][ j ] ); -s += std::string( ") "); -} -return s += ")"; -}); -cls.def( "__repr__", [] ( const FM &self ) { -return "Dune::FieldMatrix<"+to_string(m)+","+to_string(n)+">(...)"; -} ); - -cls.def_buffer( [] ( FM &self ) -> pybind11::buffer_info { -return pybind11::buffer_info( -&self[ 0 ][ 0 ], /* Pointer to buffer */ -sizeof( K ), /* Size of one scalar */ -pybind11::format_descriptor< K >::value, /* Python struct-style format descriptor */ -2, /* Number of dimensions */ -{ m, n }, /* Buffer dimensions */ -/* Strides (in bytes) for each index */ -{ -static_cast< std::size_t >( reinterpret_cast< char * >( &self[ 1 ][ 0 ] ) - reinterpret_cast< char * >( &self[ 0 ][ 0 ] ) ), -static_cast< std::size_t >( reinterpret_cast< char * >( &self[ 0 ][ 1 ] ) - reinterpret_cast< char * >( &self[ 0 ][ 0 ] ) ) -} -); -} ); - -registerDenseMatrix< FM >( cls ); -} - -template< class K, int m, int n > -inline static void registerFieldMatrix ( pybind11::handle scope ) -{ -typedef Dune::FieldMatrix< K, m, n > FM; - -auto entry = insertClass<FM>( scope, "FieldMatrix_"+std::to_string(m)+"_"+std::to_string(n), pybind11::buffer_protocol(), -GenerateTypeName("Dune::FieldMatrix",Dune::MetaType<K>(),m,n), IncludeFiles{"dune/common/fmatrix.hh"} -); -if (!entry.second) -return; -registerFieldMatrix( scope, entry.first ); -} -} // namespace Python + namespace Python + { + + // registerFieldMatrix + // ------------------- + + template< class K, int m, int n, class ...options > + void registerFieldMatrix ( pybind11::handle scope, pybind11::class_<Dune::FieldMatrix<K,m,n>, options...> cls ) + { + typedef Dune::FieldMatrix< K, m, n > FM; + using pybind11::operator""_a; + + if( (m == 1) && (n == 1) ) + { + cls.def( pybind11::init( [] ( int a ) { return new FM( K( a ) ); } ) ); + cls.def( pybind11::init( [] ( K a ) { return new FM( a ); } ) ); + cls.def( "__float__", [] ( const FM &self ) { return self[ 0 ][ 0 ]; } ); + pybind11::implicitly_convertible< int, FM >(); + pybind11::implicitly_convertible< K, FM >(); + } + + cls.def( pybind11::init( [] () { return new FM( K( 0 ) ); } ) ); + + cls.def( pybind11::init( [] ( pybind11::list rows ) { + FM *self = new FM( K( 0 ) ); + + const std::size_t numRows = std::min( static_cast< std::size_t >( m ), rows.size() ); + for( std::size_t i = 0; i < numRows; ++i ) + (*self)[ i ] = pybind11::cast< Dune::FieldVector< K, n > >( rows[ i ] ); + return self; + } ) ); + + pybind11::implicitly_convertible< pybind11::list, FM >(); + + cls.def( "__str__", [] ( const FM &self ) { + std::string s = "("; + for( int i = 0; i < m; ++i ) + { + s += (i > 0 ? "\n(" : "("); + for( int j = 0; j < n; ++j ) + s += (j > 0 ? ", " : "") + std::to_string( self[ i ][ j ] ); + s += std::string( ") "); + } + return s += ")"; + }); + cls.def( "__repr__", [] ( const FM &self ) { + return "Dune::FieldMatrix<"+to_string(m)+","+to_string(n)+">(...)"; + } ); + + cls.def_buffer( [] ( FM &self ) -> pybind11::buffer_info { + return pybind11::buffer_info( + &self[ 0 ][ 0 ], /* Pointer to buffer */ + sizeof( K ), /* Size of one scalar */ + pybind11::format_descriptor< K >::value, /* Python struct-style format descriptor */ + 2, /* Number of dimensions */ + { m, n }, /* Buffer dimensions */ + /* Strides (in bytes) for each index */ + { + static_cast< std::size_t >( reinterpret_cast< char * >( &self[ 1 ][ 0 ] ) - reinterpret_cast< char * >( &self[ 0 ][ 0 ] ) ), + static_cast< std::size_t >( reinterpret_cast< char * >( &self[ 0 ][ 1 ] ) - reinterpret_cast< char * >( &self[ 0 ][ 0 ] ) ) + } + ); + } ); + + registerDenseMatrix< FM >( cls ); + } + + template< class K, int m, int n > + inline static void registerFieldMatrix ( pybind11::handle scope ) + { + typedef Dune::FieldMatrix< K, m, n > FM; + + auto entry = insertClass<FM>( scope, "FieldMatrix_"+std::to_string(m)+"_"+std::to_string(n), pybind11::buffer_protocol(), + GenerateTypeName("Dune::FieldMatrix",Dune::MetaType<K>(),m,n), IncludeFiles{"dune/common/fmatrix.hh"} + ); + if (!entry.second) + return; + registerFieldMatrix( scope, entry.first ); + } + } // namespace Python } // namespace Dune diff --git a/dune/python/common/fvecmatregistry.hh b/dune/python/common/fvecmatregistry.hh index 56da6be4f0d81f121dc5ef35faea6edecef823f4..dae5b2125165edb86fda7709ec64f5b4a5fed282 100644 --- a/dune/python/common/fvecmatregistry.hh +++ b/dune/python/common/fvecmatregistry.hh @@ -7,31 +7,31 @@ #include <dune/python/common/fmatrix.hh> namespace Dune { -namespace Python -{ -template <class Type> -struct registerFieldVecMat; + namespace Python + { + template <class Type> + struct registerFieldVecMat; -template <class K, int size> -struct registerFieldVecMat<Dune::FieldVector<K,size>> -{ -static void apply() -{ -pybind11::module scope = pybind11::module::import("dune.common"); -registerFieldVector<K,size>(scope); -} -}; -template< class K, int row, int col > -struct registerFieldVecMat<Dune::FieldMatrix<K,row,col>> -{ -static void apply() -{ -pybind11::module scope = pybind11::module::import("dune.common"); -registerFieldMatrix<K,row,col>(scope); -registerFieldVector<K,col>(scope); -registerFieldVector<K,row>(scope); -} -}; -} + template <class K, int size> + struct registerFieldVecMat<Dune::FieldVector<K,size>> + { + static void apply() + { + pybind11::module scope = pybind11::module::import("dune.common"); + registerFieldVector<K,size>(scope); + } + }; + template< class K, int row, int col > + struct registerFieldVecMat<Dune::FieldMatrix<K,row,col>> + { + static void apply() + { + pybind11::module scope = pybind11::module::import("dune.common"); + registerFieldMatrix<K,row,col>(scope); + registerFieldVector<K,col>(scope); + registerFieldVector<K,row>(scope); + } + }; + } } #endif // DUNE_PYTHON_COMMON_FVECMATREG_HH diff --git a/dune/python/common/fvector.hh b/dune/python/common/fvector.hh index 9581464aa19161821cf8b22d1e89f62fc1d1e97e..c8cc7a052b27956521e84a0f4d000664a1ac854a 100644 --- a/dune/python/common/fvector.hh +++ b/dune/python/common/fvector.hh @@ -23,134 +23,134 @@ namespace Dune { -namespace Python -{ - -// to_string -// --------- - -template< class K, int size > -inline static std::string to_string ( const FieldVector< K, size > &x ) -{ -return "(" + join( ", ", [] ( auto &&x ) { return to_string( x ); }, x.begin(), x.end() ) + ")"; -} - - - -// registerFieldVector -// ------------------- -template< class K, int size, class ...options > -void registerFieldVector ( pybind11::handle scope, pybind11::class_<Dune::FieldVector<K,size>, options...> cls ) -{ -using pybind11::operator""_a; - -typedef Dune::FieldVector<K, size> FV; -cls.def( pybind11::init( [] () { return new FV(); } ) ); - -if( size == 1 ) -{ -cls.def( pybind11::init( [] ( int a ) { return new FV( K( a ) ); } ) ); -cls.def( pybind11::init( [] ( K a ) { return new FV( a ); } ) ); -cls.def( "__float__", [] ( const FV &self ) { return self[ 0 ]; } ); -pybind11::implicitly_convertible< int, FV >(); -pybind11::implicitly_convertible< K, FV >(); -} - -cls.def( pybind11::init( [] ( pybind11::buffer x ) { -pybind11::buffer_info info = x.request(); -if( info.format != pybind11::format_descriptor< K >::format() ) -throw pybind11::value_error( "Incompatible buffer format." ); -if( info.ndim != 1 ) -throw pybind11::value_error( "Only one-dimensional buffers can be converted into FieldVector." ); -const ssize_t stride = info.strides[ 0 ] / sizeof( K ); -const ssize_t sz = std::min( static_cast< ssize_t >( size ), info.shape[ 0 ] ); - -FV *self = new FV( K( 0 ) ); -for( ssize_t i = 0; i < sz; ++i ) -(*self)[ i ] = static_cast< K * >( info.ptr )[ i*stride ]; -return self; -} ), "x"_a ); - -cls.def( pybind11::init( [] ( pybind11::tuple x ) { -FV *self = new FV( K( 0 ) ); -const std::size_t sz = std::min( static_cast< std::size_t >( size ), x.size() ); -// should this fail in case the sizes do not match? -for( std::size_t i = 0; i < sz; ++i ) -(*self)[ i ] = x[ i ].template cast< K >(); -return self; -} ), "x"_a ); - -cls.def( pybind11::init( [] ( pybind11::list x ) { -FV *self = new FV( K( 0 ) ); -const std::size_t sz = std::min( static_cast< std::size_t >( size ), x.size() ); -// should this fail in case the sizes do not match? -for( std::size_t i = 0; i < sz; ++i ) -(*self)[ i ] = x[ i ].template cast< K >(); -return self; -} ), "x"_a ); - -cls.def( pybind11::init( [] ( pybind11::args args ) { -FV *self = new FV( K( 0 ) ); -const std::size_t sz = std::min( static_cast< std::size_t >( size ), args.size() ); -// should this fail in case the sizes do not match? -for( std::size_t i = 0; i < sz; ++i ) -(*self)[ i ] = args[ i ].template cast< K >(); -return self; -} ) ); - -pybind11::implicitly_convertible< pybind11::args, FV >(); -pybind11::implicitly_convertible< pybind11::buffer, FV >(); - -cls.def("copy", [](FV& , pybind11::args l) { -FV v(K(0)); -const std::size_t sz = std::min(v.size(), l.size()); -// should this fail in case the sizes do not match? -for (std::size_t i = 0; i < sz; ++i) -v[i] = l[i].template cast<K>(); -return v; -}); - -cls.def( "__str__", [] ( const FV &self ) { return to_string( self ); } ); -cls.def( "__repr__", [] ( const FV &self ) { -return "Dune::FieldVector<"+to_string(size)+">"+to_string(self); -} ); - -cls.def_buffer( [] ( FV &self ) -> pybind11::buffer_info { -return pybind11::buffer_info( -&self[ 0 ], /* Pointer to buffer */ -sizeof( K ), /* Size of one scalar */ -pybind11::format_descriptor< K >::format(), /* Python struct-style format descriptor */ -1, /* Number of dimensions */ -{ size }, /* Buffer dimensions */ -/* Strides (in bytes) for each index */ -{ -static_cast< std::size_t >( reinterpret_cast< char * >( &self[ 1 ] ) - reinterpret_cast< char * >( &self[ 0 ] ) ) } ); -} -); - -registerDenseVector< FV >( cls ); -} - -template< class K, int size > -void registerFieldVector ( pybind11::handle scope, std::integral_constant< int, size > = {} ) -{ -typedef Dune::FieldVector<K, size> FV; - -auto entry = insertClass<FV>(scope, "FieldVector_"+std::to_string(size), pybind11::buffer_protocol(), -GenerateTypeName("Dune::FieldVector",MetaType<K>(),size),IncludeFiles{"dune/common/fvector.hh"} -); -if (!entry.second) -return; -registerFieldVector(scope, entry.first); -} - -template<class K, int... size> -void registerFieldVector(pybind11::handle scope, std::integer_sequence<int, size...>) -{ -std::ignore = std::make_tuple((registerFieldVector<K>(scope, std::integral_constant<int, size>()), size)...); -} - -} // namespace Python + namespace Python + { + + // to_string + // --------- + + template< class K, int size > + inline static std::string to_string ( const FieldVector< K, size > &x ) + { + return "(" + join( ", ", [] ( auto &&x ) { return to_string( x ); }, x.begin(), x.end() ) + ")"; + } + + + + // registerFieldVector + // ------------------- + template< class K, int size, class ...options > + void registerFieldVector ( pybind11::handle scope, pybind11::class_<Dune::FieldVector<K,size>, options...> cls ) + { + using pybind11::operator""_a; + + typedef Dune::FieldVector<K, size> FV; + cls.def( pybind11::init( [] () { return new FV(); } ) ); + + if( size == 1 ) + { + cls.def( pybind11::init( [] ( int a ) { return new FV( K( a ) ); } ) ); + cls.def( pybind11::init( [] ( K a ) { return new FV( a ); } ) ); + cls.def( "__float__", [] ( const FV &self ) { return self[ 0 ]; } ); + pybind11::implicitly_convertible< int, FV >(); + pybind11::implicitly_convertible< K, FV >(); + } + + cls.def( pybind11::init( [] ( pybind11::buffer x ) { + pybind11::buffer_info info = x.request(); + if( info.format != pybind11::format_descriptor< K >::format() ) + throw pybind11::value_error( "Incompatible buffer format." ); + if( info.ndim != 1 ) + throw pybind11::value_error( "Only one-dimensional buffers can be converted into FieldVector." ); + const ssize_t stride = info.strides[ 0 ] / sizeof( K ); + const ssize_t sz = std::min( static_cast< ssize_t >( size ), info.shape[ 0 ] ); + + FV *self = new FV( K( 0 ) ); + for( ssize_t i = 0; i < sz; ++i ) + (*self)[ i ] = static_cast< K * >( info.ptr )[ i*stride ]; + return self; + } ), "x"_a ); + + cls.def( pybind11::init( [] ( pybind11::tuple x ) { + FV *self = new FV( K( 0 ) ); + const std::size_t sz = std::min( static_cast< std::size_t >( size ), x.size() ); + // should this fail in case the sizes do not match? + for( std::size_t i = 0; i < sz; ++i ) + (*self)[ i ] = x[ i ].template cast< K >(); + return self; + } ), "x"_a ); + + cls.def( pybind11::init( [] ( pybind11::list x ) { + FV *self = new FV( K( 0 ) ); + const std::size_t sz = std::min( static_cast< std::size_t >( size ), x.size() ); + // should this fail in case the sizes do not match? + for( std::size_t i = 0; i < sz; ++i ) + (*self)[ i ] = x[ i ].template cast< K >(); + return self; + } ), "x"_a ); + + cls.def( pybind11::init( [] ( pybind11::args args ) { + FV *self = new FV( K( 0 ) ); + const std::size_t sz = std::min( static_cast< std::size_t >( size ), args.size() ); + // should this fail in case the sizes do not match? + for( std::size_t i = 0; i < sz; ++i ) + (*self)[ i ] = args[ i ].template cast< K >(); + return self; + } ) ); + + pybind11::implicitly_convertible< pybind11::args, FV >(); + pybind11::implicitly_convertible< pybind11::buffer, FV >(); + + cls.def("copy", [](FV& , pybind11::args l) { + FV v(K(0)); + const std::size_t sz = std::min(v.size(), l.size()); + // should this fail in case the sizes do not match? + for (std::size_t i = 0; i < sz; ++i) + v[i] = l[i].template cast<K>(); + return v; + }); + + cls.def( "__str__", [] ( const FV &self ) { return to_string( self ); } ); + cls.def( "__repr__", [] ( const FV &self ) { + return "Dune::FieldVector<"+to_string(size)+">"+to_string(self); + } ); + + cls.def_buffer( [] ( FV &self ) -> pybind11::buffer_info { + return pybind11::buffer_info( + &self[ 0 ], /* Pointer to buffer */ + sizeof( K ), /* Size of one scalar */ + pybind11::format_descriptor< K >::format(), /* Python struct-style format descriptor */ + 1, /* Number of dimensions */ + { size }, /* Buffer dimensions */ + /* Strides (in bytes) for each index */ + { + static_cast< std::size_t >( reinterpret_cast< char * >( &self[ 1 ] ) - reinterpret_cast< char * >( &self[ 0 ] ) ) } ); + } + ); + + registerDenseVector< FV >( cls ); + } + + template< class K, int size > + void registerFieldVector ( pybind11::handle scope, std::integral_constant< int, size > = {} ) + { + typedef Dune::FieldVector<K, size> FV; + + auto entry = insertClass<FV>(scope, "FieldVector_"+std::to_string(size), pybind11::buffer_protocol(), + GenerateTypeName("Dune::FieldVector",MetaType<K>(),size),IncludeFiles{"dune/common/fvector.hh"} + ); + if (!entry.second) + return; + registerFieldVector(scope, entry.first); + } + + template<class K, int... size> + void registerFieldVector(pybind11::handle scope, std::integer_sequence<int, size...>) + { + std::ignore = std::make_tuple((registerFieldVector<K>(scope, std::integral_constant<int, size>()), size)...); + } + + } // namespace Python } // namespace Dune diff --git a/dune/python/common/getdimension.hh b/dune/python/common/getdimension.hh index 49f3ecd05a2eb572611ede7e585e997893308ed9..5b3f6eb5c9fe303eff8669c7755d135432488104 100644 --- a/dune/python/common/getdimension.hh +++ b/dune/python/common/getdimension.hh @@ -12,12 +12,12 @@ struct GetDimension; template< class T > struct GetDimension< T, std::enable_if_t<std::is_arithmetic<T>::value>> -: public std::integral_constant< int, 1 > {}; + : public std::integral_constant< int, 1 > {}; template< class FT, int dim > struct GetDimension<Dune::FieldVector<FT,dim>> -: public std::integral_constant< int, dim > {}; + : public std::integral_constant< int, dim > {}; template< class FT, int dimr, int dimc > struct GetDimension<Dune::FieldMatrix<FT,dimr,dimc>> -: public std::integral_constant< int, dimr*dimc > {}; + : public std::integral_constant< int, dimr*dimc > {}; #endif // DUNE_PYTHON_COMMON_GETDIMENSION_HH diff --git a/dune/python/common/logger.hh b/dune/python/common/logger.hh index 108daba67b4c442675c6ed936b62d967d9dc320a..0624438121054d489e8edd69787eac44961ba6a5 100644 --- a/dune/python/common/logger.hh +++ b/dune/python/common/logger.hh @@ -12,84 +12,84 @@ namespace Dune { -namespace Python -{ - -// Logger -// ------ - -struct DUNE_PRIVATE Logger -{ -enum class Level : int -{ -critical = 50, -error = 40, -warning = 30, -info = 20, -debug = 10, -notSet = 0 -}; - -explicit Logger ( const std::string &name ) -: logging_( pybind11::module::import( "logging" ) ), -logger_( logging_.attr( "getLogger" )( name ) ) -{} - -template< class... Args > -void critical ( const std::string &msg, Args &&... args ) const -{ -log( Level::critical, msg, std::forward< Args >( args )... ); -} - -template< class... Args > -void error ( const std::string &msg, Args &&... args ) const -{ -log( Level::error, msg, std::forward< Args >( args )... ); -} - -template< class... Args > -void warning ( const std::string &msg, Args &&... args ) const -{ -log( Level::warning, msg, std::forward< Args >( args )... ); -} - -template< class... Args > -void info ( const std::string &msg, Args &&... args ) const -{ -log( Level::info, msg, std::forward< Args >( args )... ); -} - -template< class... Args > -void debug ( const std::string &msg, Args &&... args ) const -{ -log( Level::debug, msg, std::forward< Args >( args )... ); -} - -template< class... Args > -void log ( int level, const std::string &msg, Args &&... args ) const -{ -pybind11::object pyLevel = pybind11::int_( level ); -logger_.attr( "log" )( pyLevel, msg, *pybind11::make_tuple( std::forward< Args >( args )... ) ); -} - -template< class... Args > -void log ( Level level, const std::string &msg, Args &&... args ) const -{ -log( static_cast< int >( level ), msg, std::forward< Args >( args )... ); -} - -void setLevel ( int level ) { logger_.attr( "setLevel" )( level ); } - -bool isEnabledFor ( int level ) { return pybind11::cast< bool >( logger_.attr( "isEnabledFor" )( level ) ); } - -int getEffectiveLevel () { return pybind11::cast< int >( logger_.attr( "getEffectiveLevel" )() ); } - -private: -pybind11::module logging_; -pybind11::object logger_; -}; - -} // namespace Python + namespace Python + { + + // Logger + // ------ + + struct DUNE_PRIVATE Logger + { + enum class Level : int + { + critical = 50, + error = 40, + warning = 30, + info = 20, + debug = 10, + notSet = 0 + }; + + explicit Logger ( const std::string &name ) + : logging_( pybind11::module::import( "logging" ) ), + logger_( logging_.attr( "getLogger" )( name ) ) + {} + + template< class... Args > + void critical ( const std::string &msg, Args &&... args ) const + { + log( Level::critical, msg, std::forward< Args >( args )... ); + } + + template< class... Args > + void error ( const std::string &msg, Args &&... args ) const + { + log( Level::error, msg, std::forward< Args >( args )... ); + } + + template< class... Args > + void warning ( const std::string &msg, Args &&... args ) const + { + log( Level::warning, msg, std::forward< Args >( args )... ); + } + + template< class... Args > + void info ( const std::string &msg, Args &&... args ) const + { + log( Level::info, msg, std::forward< Args >( args )... ); + } + + template< class... Args > + void debug ( const std::string &msg, Args &&... args ) const + { + log( Level::debug, msg, std::forward< Args >( args )... ); + } + + template< class... Args > + void log ( int level, const std::string &msg, Args &&... args ) const + { + pybind11::object pyLevel = pybind11::int_( level ); + logger_.attr( "log" )( pyLevel, msg, *pybind11::make_tuple( std::forward< Args >( args )... ) ); + } + + template< class... Args > + void log ( Level level, const std::string &msg, Args &&... args ) const + { + log( static_cast< int >( level ), msg, std::forward< Args >( args )... ); + } + + void setLevel ( int level ) { logger_.attr( "setLevel" )( level ); } + + bool isEnabledFor ( int level ) { return pybind11::cast< bool >( logger_.attr( "isEnabledFor" )( level ) ); } + + int getEffectiveLevel () { return pybind11::cast< int >( logger_.attr( "getEffectiveLevel" )() ); } + + private: + pybind11::module logging_; + pybind11::object logger_; + }; + + } // namespace Python } // namespace Dune diff --git a/dune/python/common/mpihelper.hh b/dune/python/common/mpihelper.hh index f77993782ae76ad133ae325ed21b03ce323bda3a..2dd5a3d75eec4a5484522391640c5620aa4d9022 100644 --- a/dune/python/common/mpihelper.hh +++ b/dune/python/common/mpihelper.hh @@ -13,41 +13,41 @@ namespace Dune { -namespace Python -{ + namespace Python + { -// registerCollectiveCommunication -// ------------------------------- + // registerCollectiveCommunication + // ------------------------------- -template< class Comm, class... objects > -inline static void registerCollectiveCommunication ( pybind11::class_< Comm, objects... > cls ) -{ -using pybind11::operator""_a; + template< class Comm, class... objects > + inline static void registerCollectiveCommunication ( pybind11::class_< Comm, objects... > cls ) + { + using pybind11::operator""_a; -cls.def_property_readonly( "rank", &Comm::rank ); -cls.def_property_readonly( "size", &Comm::size ); + cls.def_property_readonly( "rank", &Comm::rank ); + cls.def_property_readonly( "size", &Comm::size ); -cls.def( "barrier", &Comm::barrier ); + cls.def( "barrier", &Comm::barrier ); -cls.def( "min", [] ( const Comm &self, double x ) { return self.min( x ); }, "x"_a ); -cls.def( "max", [] ( const Comm &self, double x ) { return self.max( x ); }, "x"_a ); -cls.def( "sum", [] ( const Comm &self, double x ) { return self.sum( x ); }, "x"_a ); -} + cls.def( "min", [] ( const Comm &self, double x ) { return self.min( x ); }, "x"_a ); + cls.def( "max", [] ( const Comm &self, double x ) { return self.max( x ); }, "x"_a ); + cls.def( "sum", [] ( const Comm &self, double x ) { return self.sum( x ); }, "x"_a ); + } -inline static void registerCollectiveCommunication ( pybind11::handle scope ) -{ -typedef Dune::CollectiveCommunication< Dune::MPIHelper::MPICommunicator > Comm; + inline static void registerCollectiveCommunication ( pybind11::handle scope ) + { + typedef Dune::CollectiveCommunication< Dune::MPIHelper::MPICommunicator > Comm; -auto typeName = GenerateTypeName( "Dune::CollectiveCommunication", "Dune::MPIHelper::MPICommunicator" ); -auto includes = IncludeFiles{ "dune/common/parallel/collectivecommunication.hh", "dune/common/parallel/mpihelper.hh" }; -auto clsComm = insertClass< Comm >( scope, "CollectiveCommunication", typeName, includes ); -if( clsComm.second ) -registerCollectiveCommunication( clsComm.first ); + auto typeName = GenerateTypeName( "Dune::CollectiveCommunication", "Dune::MPIHelper::MPICommunicator" ); + auto includes = IncludeFiles{ "dune/common/parallel/collectivecommunication.hh", "dune/common/parallel/mpihelper.hh" }; + auto clsComm = insertClass< Comm >( scope, "CollectiveCommunication", typeName, includes ); + if( clsComm.second ) + registerCollectiveCommunication( clsComm.first ); -scope.attr( "comm" ) = pybind11::cast( Dune::MPIHelper::getCollectiveCommunication() ); -} + scope.attr( "comm" ) = pybind11::cast( Dune::MPIHelper::getCollectiveCommunication() ); + } -} // namespace Python + } // namespace Python } // namespace Dune diff --git a/dune/python/common/numpyvector.hh b/dune/python/common/numpyvector.hh index e2ec6977818cf1e97b9e0691a59ccbd279f3b469..70056bf0f60c67b2851050fc628f9af29ba56a8d 100644 --- a/dune/python/common/numpyvector.hh +++ b/dune/python/common/numpyvector.hh @@ -12,134 +12,134 @@ namespace Dune { -namespace Python -{ - -// Internal Forward Declarations -// ----------------------------- - -template< class T > -class NumPyVector; - -} // namespace Python - - - -// DenseMatVecTraits for NumPyVector -// --------------------------------- - -template< class T > -struct DenseMatVecTraits< Python::NumPyVector< T > > -{ -typedef Python::NumPyVector< T > derived_type; -typedef pybind11::array_t< T > container_type; -typedef T value_type; -typedef std::size_t size_type; -}; - - - -// FieldTraits for NumPyVector -// --------------------------- - -template< class T > -struct FieldTraits< Python::NumPyVector< T > > -{ -typedef typename FieldTraits< T >::field_type field_type; -typedef typename FieldTraits< T >::real_type real_type; -}; - - -namespace Python -{ - -template< class T > -class NumPyVector -: public DenseVector< NumPyVector< T > > -{ -typedef NumPyVector< T > This; -typedef DenseVector< NumPyVector< T > > Base; - -public: -typedef typename Base::size_type size_type; -typedef typename Base::value_type value_type; - -explicit NumPyVector ( size_type size ) -: array_( pybind11::buffer_info( nullptr, sizeof( T ), -pybind11::format_descriptor< T >::value, 1, { size }, { sizeof( T ) } ) -), -size_(size) -{} - -NumPyVector ( pybind11::buffer buf ) -: array_( buf ), -size_( 0 ) -{ -pybind11::buffer_info info = buf.request(); -if (info.ndim != 1) -DUNE_THROW( InvalidStateException, "NumPyVector can only be created from one-dimensional array" ); -size_ = info.shape[0]; -} - -NumPyVector ( const This &other ) = delete; -NumPyVector ( This &&other ) = delete; - -~NumPyVector() {} - -This &operator= ( const This &other ) = delete; -This &operator= ( This &&other ) = delete; - -operator pybind11::array_t< T > () const { return array_; } - -const value_type &operator[] ( size_type index ) const -{ -return data()[ index ]; -} -value_type &operator[] ( size_type index ) -{ -return data()[ index ]; -} -value_type &vec_access ( size_type index ) -{ -return data()[ index ]; -} -const value_type &vec_access ( size_type index ) const -{ -return data()[ index ]; -} - -inline const value_type *data () const -{ -return static_cast< const value_type * >( const_cast<pybind11::array_t< T >&>(array_).request(false).ptr ); -} -inline value_type *data () -{ -return static_cast< value_type * >( array_.request(true).ptr ); -} -pybind11::array_t< T > &coefficients() -{ -return array_; -} -pybind11::array_t< T > &coefficients() const -{ -return array_; -} - -size_type size () const -{ -return size_; -} -size_type vec_size () const -{ -return size_; -} - -private: -pybind11::array_t< T > array_; -size_type size_; -}; - -} // namespace Python + namespace Python + { + + // Internal Forward Declarations + // ----------------------------- + + template< class T > + class NumPyVector; + + } // namespace Python + + + + // DenseMatVecTraits for NumPyVector + // --------------------------------- + + template< class T > + struct DenseMatVecTraits< Python::NumPyVector< T > > + { + typedef Python::NumPyVector< T > derived_type; + typedef pybind11::array_t< T > container_type; + typedef T value_type; + typedef std::size_t size_type; + }; + + + + // FieldTraits for NumPyVector + // --------------------------- + + template< class T > + struct FieldTraits< Python::NumPyVector< T > > + { + typedef typename FieldTraits< T >::field_type field_type; + typedef typename FieldTraits< T >::real_type real_type; + }; + + + namespace Python + { + + template< class T > + class NumPyVector + : public DenseVector< NumPyVector< T > > + { + typedef NumPyVector< T > This; + typedef DenseVector< NumPyVector< T > > Base; + + public: + typedef typename Base::size_type size_type; + typedef typename Base::value_type value_type; + + explicit NumPyVector ( size_type size ) + : array_( pybind11::buffer_info( nullptr, sizeof( T ), + pybind11::format_descriptor< T >::value, 1, { size }, { sizeof( T ) } ) + ), + size_(size) + {} + + NumPyVector ( pybind11::buffer buf ) + : array_( buf ), + size_( 0 ) + { + pybind11::buffer_info info = buf.request(); + if (info.ndim != 1) + DUNE_THROW( InvalidStateException, "NumPyVector can only be created from one-dimensional array" ); + size_ = info.shape[0]; + } + + NumPyVector ( const This &other ) = delete; + NumPyVector ( This &&other ) = delete; + + ~NumPyVector() {} + + This &operator= ( const This &other ) = delete; + This &operator= ( This &&other ) = delete; + + operator pybind11::array_t< T > () const { return array_; } + + const value_type &operator[] ( size_type index ) const + { + return data()[ index ]; + } + value_type &operator[] ( size_type index ) + { + return data()[ index ]; + } + value_type &vec_access ( size_type index ) + { + return data()[ index ]; + } + const value_type &vec_access ( size_type index ) const + { + return data()[ index ]; + } + + inline const value_type *data () const + { + return static_cast< const value_type * >( const_cast<pybind11::array_t< T >&>(array_).request(false).ptr ); + } + inline value_type *data () + { + return static_cast< value_type * >( array_.request(true).ptr ); + } + pybind11::array_t< T > &coefficients() + { + return array_; + } + pybind11::array_t< T > &coefficients() const + { + return array_; + } + + size_type size () const + { + return size_; + } + size_type vec_size () const + { + return size_; + } + + private: + pybind11::array_t< T > array_; + size_type size_; + }; + + } // namespace Python } // namespace Dune diff --git a/dune/python/common/pythonvector.hh b/dune/python/common/pythonvector.hh index eaacaf6a2d0078bf9b60501e0f184d130d955f23..328a2192cad9580e392431461b8289135728e6e0 100644 --- a/dune/python/common/pythonvector.hh +++ b/dune/python/common/pythonvector.hh @@ -11,93 +11,93 @@ namespace Dune { -namespace Python -{ + namespace Python + { -// Internal Forward Declarations -// ----------------------------- + // Internal Forward Declarations + // ----------------------------- -template< class K > -class PythonVector; + template< class K > + class PythonVector; -} // namespace Python + } // namespace Python -// DenseMatVecTraits for PythonVector -// ---------------------------------- + // DenseMatVecTraits for PythonVector + // ---------------------------------- -template< class K > -struct DenseMatVecTraits< Python::PythonVector< K > > -{ -typedef Python::PythonVector< K > derived_type; -typedef K value_type; -typedef std::size_t size_type; -}; + template< class K > + struct DenseMatVecTraits< Python::PythonVector< K > > + { + typedef Python::PythonVector< K > derived_type; + typedef K value_type; + typedef std::size_t size_type; + }; -// FieldTraits for PythonVector -// ---------------------------- + // FieldTraits for PythonVector + // ---------------------------- -template< class K > -struct FieldTraits< Python::PythonVector< K > > -{ -typedef typename FieldTraits< K >::field_type field_type; -typedef typename FieldTraits< K >::real_type real_type; -}; + template< class K > + struct FieldTraits< Python::PythonVector< K > > + { + typedef typename FieldTraits< K >::field_type field_type; + typedef typename FieldTraits< K >::real_type real_type; + }; -namespace Python -{ + namespace Python + { -template< class K > -class PythonVector -: public Dune::DenseVector< PythonVector< K > > -{ -typedef PythonVector< K > This; -typedef Dune::DenseVector< PythonVector< K > > Base; + template< class K > + class PythonVector + : public Dune::DenseVector< PythonVector< K > > + { + typedef PythonVector< K > This; + typedef Dune::DenseVector< PythonVector< K > > Base; -public: -typedef typename Base::size_type size_type; -typedef typename Base::field_type field_type; + public: + typedef typename Base::size_type size_type; + typedef typename Base::field_type field_type; -explicit PythonVector ( pybind11::buffer buffer ) -: buffer_( buffer ), info_( buffer_.request() ) -{ -if( info_.format != pybind11::format_descriptor< field_type >::format() ) -throw std::runtime_error( "Incompatible buffer format." ); -if( info_.ndim != 1 ) -throw std::runtime_error( "PythonVector can only be instantiated from one-dimensional buffers." ); -stride_ = info_.strides[ 0 ] / sizeof( field_type ); -} + explicit PythonVector ( pybind11::buffer buffer ) + : buffer_( buffer ), info_( buffer_.request() ) + { + if( info_.format != pybind11::format_descriptor< field_type >::format() ) + throw std::runtime_error( "Incompatible buffer format." ); + if( info_.ndim != 1 ) + throw std::runtime_error( "PythonVector can only be instantiated from one-dimensional buffers." ); + stride_ = info_.strides[ 0 ] / sizeof( field_type ); + } -PythonVector ( const This & ) = delete; -PythonVector ( This && ) = default; + PythonVector ( const This & ) = delete; + PythonVector ( This && ) = default; -This &operator= ( const This & ) = delete; -This &operator= ( This && ) = default; + This &operator= ( const This & ) = delete; + This &operator= ( This && ) = default; -const field_type &operator[] ( size_type i ) const -{ -return static_cast< const field_type * >( info_.ptr )[ i*stride_ ]; -} + const field_type &operator[] ( size_type i ) const + { + return static_cast< const field_type * >( info_.ptr )[ i*stride_ ]; + } -field_type &operator[] ( size_type i ) -{ -return static_cast< field_type * >( info_.ptr )[ i*stride_ ]; -} + field_type &operator[] ( size_type i ) + { + return static_cast< field_type * >( info_.ptr )[ i*stride_ ]; + } -size_type size () const { return info_.shape[ 0 ]; } + size_type size () const { return info_.shape[ 0 ]; } -private: -pybind11::buffer buffer_; -pybind11::buffer_info info_; -size_type stride_; -}; + private: + pybind11::buffer buffer_; + pybind11::buffer_info info_; + size_type stride_; + }; -} // namespace Python + } // namespace Python } // namespace Dune diff --git a/dune/python/common/string.hh b/dune/python/common/string.hh index 889e480a84e525e5df271a3a09c738d508aeb957..ff4a1a592b79f2bcda1af7832d4d2d289cc8be27 100644 --- a/dune/python/common/string.hh +++ b/dune/python/common/string.hh @@ -8,37 +8,37 @@ namespace Dune { -namespace Python -{ + namespace Python + { -using std::to_string; + using std::to_string; -// join -// ---- + // join + // ---- -template< class Formatter, class Iterator > -inline static auto join ( const std::string &delimiter, Formatter &&formatter, Iterator begin, Iterator end ) --> std::enable_if_t< std::is_same< std::decay_t< decltype( formatter( *begin ) ) >, std::string >::value, std::string > -{ -std::string s; -if( begin != end ) -{ -for( s = formatter( *begin++ ); begin != end; s += formatter( *begin++ ) ) -s += delimiter; -} -return s; -} - -template< class Iterator > -inline static auto join ( const std::string &delimiter, Iterator begin, Iterator end ) --> std::enable_if_t< std::is_same< std::decay_t< decltype( *begin ) >, std::string >::value, std::string > -{ -return join( delimiter, [] ( decltype( *begin ) s ) -> decltype( *begin ) { return s; }, begin, end ); -} + template< class Formatter, class Iterator > + inline static auto join ( const std::string &delimiter, Formatter &&formatter, Iterator begin, Iterator end ) + -> std::enable_if_t< std::is_same< std::decay_t< decltype( formatter( *begin ) ) >, std::string >::value, std::string > + { + std::string s; + if( begin != end ) + { + for( s = formatter( *begin++ ); begin != end; s += formatter( *begin++ ) ) + s += delimiter; + } + return s; + } + + template< class Iterator > + inline static auto join ( const std::string &delimiter, Iterator begin, Iterator end ) + -> std::enable_if_t< std::is_same< std::decay_t< decltype( *begin ) >, std::string >::value, std::string > + { + return join( delimiter, [] ( decltype( *begin ) s ) -> decltype( *begin ) { return s; }, begin, end ); + } -} // namespace Python + } // namespace Python } // namespace Dune diff --git a/dune/python/common/typeregistry.hh b/dune/python/common/typeregistry.hh index 04444b9e36cb3073f6c62aed1cc7bce2e2645ca2..c466efdb8459c0ef095ebe485fb890c6b3562da9 100644 --- a/dune/python/common/typeregistry.hh +++ b/dune/python/common/typeregistry.hh @@ -22,441 +22,441 @@ namespace Dune { -namespace Python -{ - -namespace detail -{ - -struct DUNE_PRIVATE Entry -{ -std::string name; -std::string pyName; -std::vector< std::string > includes; -pybind11::object object; -}; - - -// using an unordered_map directly for the type registry leads to a compilation -// error in the cast used in the typeRegistry function: -// assertion failed: Unable to cast type to reference: value is local to type caster -struct DUNE_PRIVATE TypeRegistry : public std::unordered_map< std::type_index, Entry > -{}; - - -inline static TypeRegistry &typeRegistry () -{ -// BUG: Capturing the pybind11::object in a static variable leads to a -// memory fault in Python 3.6 upon module unloading. -// As a simple fix, we reobtain the reference each time the type -// registry is requested. + namespace Python + { + + namespace detail + { + + struct DUNE_PRIVATE Entry + { + std::string name; + std::string pyName; + std::vector< std::string > includes; + pybind11::object object; + }; + + + // using an unordered_map directly for the type registry leads to a compilation + // error in the cast used in the typeRegistry function: + // assertion failed: Unable to cast type to reference: value is local to type caster + struct DUNE_PRIVATE TypeRegistry : public std::unordered_map< std::type_index, Entry > + {}; + + + inline static TypeRegistry &typeRegistry () + { + // BUG: Capturing the pybind11::object in a static variable leads to a + // memory fault in Python 3.6 upon module unloading. + // As a simple fix, we reobtain the reference each time the type + // registry is requested. #if 0 -static pybind11::object instance; -if( !instance ) -instance = pybind11::module::import( "dune.typeregistry" ).attr( "typeRegistry" ); -return pybind11::cast< TypeRegistry & >( instance ); + static pybind11::object instance; + if( !instance ) + instance = pybind11::module::import( "dune.typeregistry" ).attr( "typeRegistry" ); + return pybind11::cast< TypeRegistry & >( instance ); #endif -return pybind11::cast< TypeRegistry & >( pybind11::module::import( "dune.typeregistry" ).attr( "typeRegistry" ) ); -} - - -template< class T > -inline static auto findInTypeRegistry () -{ -auto pos = typeRegistry().find( typeid(T) ); -return std::make_pair( pos, pos == typeRegistry().end() ); -} - - -template< class T > -inline static auto insertIntoTypeRegistry ( -const std::string &name, -const std::string &pyName, -std::vector< std::string > includes ) -{ -auto ret = typeRegistry().emplace( typeid(T), Entry() ); -if( ret.second ) -{ -Entry &entry = ret.first->second; -entry.name = name; -entry.pyName = pyName; -entry.includes = std::move( includes ); -} -return ret; -} - - - -// TypeRegistryTag -// --------------- - -struct TypeRegistryTag {}; - - - -// GenerateTypeName -// ---------------- - -struct GenerateTypeName -: public TypeRegistryTag -{ -template <class... Templ> -GenerateTypeName(const std::string &main, Templ... templ) -: main_(main) -{ -templates(templ...); -} -template <class T, class... options, class... Templ> -GenerateTypeName(const std::string &outer, const std::string &main, Templ... templ) -: main_(outer+"::"+main) -{ -templates(templ...); -} -template <class... Templ> -GenerateTypeName(pybind11::handle &outer, const std::string &main, Templ... templ) -{ -main_ = getTypeName(outer) + "::" + main; -includes_.push_back(getIncludes(outer)); -std::sort( includes_.begin(), includes_.end() ); -includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); -templates(templ...); -} -template <class Outer, class... Templ> -GenerateTypeName(Dune::MetaType<Outer>, const std::string &main, Templ... templ) -{ -const auto& outerEntry = findInTypeRegistry<Outer>(); -if (outerEntry.second) -throw std::invalid_argument( (std::string("couldn't find outer class ") + -typeid(Outer).name() + " in type registry").c_str() ); -main_ = outerEntry.first->second.name + "::" + main; -includes_.push_back(outerEntry.first->second.includes); -std::sort( includes_.begin(), includes_.end() ); -includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); -templates(templ...); -} -GenerateTypeName(const std::string &main, pybind11::args args) -: main_(main) -{ -const std::size_t sz = args.size(); -for( std::size_t i = 0; i < sz; ++i ) -{ -templates_.insert( templates_.end(), getTypeName( (pybind11::handle)(args[i]) ) ); -includes_.insert( includes_.end(), getIncludes( (pybind11::handle)(args[i]) ) ); -std::sort( includes_.begin(), includes_.end() ); -includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); -} -} - -std::string name () const -{ -std::string fullName = main_; -if( !templates_.empty() ) -{ -const char *delim = "< "; -for( const auto &t : templates_ ) -{ -fullName += delim + t; -delim = ", "; -} -fullName += " >"; -} -return fullName; -} -std::vector<std::string> includes() const -{ -std::vector<std::string> ret; -for (const auto &i : includes_) -ret.insert( ret.end(), i.begin(), i.end() ); -return ret; -} - -private: -template <class... Args> -void templates(Args... args) -{ -templates_.insert(templates_.end(), { getTypeName( std::forward< Args >( args ) )... } ); -includes_.insert( includes_.end(), { getIncludes( std::forward<Args >( args ) )... } ); -std::sort( includes_.begin(), includes_.end() ); -includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); -} -template <class T,class... options> -std::string getTypeName(const pybind11::class_<T,options...> &obj) -{ -return getTypeName(static_cast<pybind11::handle>(obj)); -} -std::string getTypeName(const pybind11::object &obj) -{ -return getTypeName(static_cast<pybind11::handle>(obj)); -} -std::string getTypeName(const pybind11::handle &obj) -{ -try -{ -return obj.attr( "_typeName" ).cast<std::string>(); -} -catch(const pybind11::error_already_set& ) -{ -return pybind11::str(obj); -} -} -static std::string getTypeName ( const GenerateTypeName &name ) { return name.name(); } -std::string getTypeName ( const std::string &name ) { return name; } -std::string getTypeName ( const char *name ) { return name; } -template <class T> -std::string getTypeName ( const Dune::MetaType<T> & ) -{ -auto entry = detail::findInTypeRegistry<T>(); -if (entry.second) -throw std::invalid_argument( (std::string("couldn't find requested type ") + -typeid(T).name() + " in type registry").c_str() ); -return entry.first->second.name; -} -template <class T> -std::string getTypeName ( const T& t ) { return std::to_string(t); } - -static std::vector<std::string> getIncludes(pybind11::handle obj) -{ -try -{ -return obj.attr( "_includes" ).cast<std::vector<std::string>>(); -} -catch(const pybind11::error_already_set& ) -{ -return {}; -} -} -static std::vector< std::string > getIncludes ( const GenerateTypeName &name ) { return name.includes(); } -template <class T> -const std::vector<std::string> &getIncludes ( const Dune::MetaType<T> & ) -{ -auto entry = detail::findInTypeRegistry<T>(); -if (entry.second) -throw std::invalid_argument( (std::string("couldn't find requested type ") + -typeid(T).name() + " in type registry").c_str() ); -return entry.first->second.includes; -} -template <class T> -std::vector<std::string> getIncludes ( const T& ) { return {}; } - -std::string main_; -std::vector<std::string> templates_; -std::vector<std::vector<std::string>> includes_; -}; - - - -// IncludeFiles -// ------------ - -struct IncludeFiles -: public std::vector< std::string >, -TypeRegistryTag -{ -template <class... Args> -IncludeFiles(Args... args) -: std::vector<std::string>({args...}) {} -}; - - -template< class DuneType > -inline static auto _addToTypeRegistry ( std::string pyName, const GenerateTypeName &typeName, const std::vector< std::string > &inc = {} ) -{ -std::vector< std::string > includes = typeName.includes(); -includes.insert( includes.end(), inc.begin(), inc.end() ); -auto entry = detail::insertIntoTypeRegistry< DuneType >( typeName.name(), std::move( pyName ), includes ); -if( !entry.second ) -throw std::invalid_argument( std::string( "adding a class (" ) + typeid( DuneType ).name() + ") twice to the type registry" ); -return entry; -} - - -template <typename S, typename M, typename O = std::index_sequence<>> -struct filter : O {}; -template <std::size_t I, std::size_t... Is, -std::size_t... Js, std::size_t... Ks> -struct filter<std::index_sequence<I, Is...>, std::index_sequence<0, Js...>, -std::index_sequence<Ks...>> -: filter<std::index_sequence<Is...>, std::index_sequence<Js...>, -std::index_sequence<Ks...>> {}; -template <std::size_t I, std::size_t... Is, std::size_t... Js, -std::size_t... Ks> -struct filter<std::index_sequence<I, Is...>, std::index_sequence<1, Js...>, -std::index_sequence<Ks...>> -: filter<std::index_sequence<Is...>, std::index_sequence<Js...>, -std::index_sequence<Ks..., I>> {}; - -template< template< class T> class F, class... Args > -using Filter = filter< std::index_sequence_for< Args... >, std::index_sequence< F< Args >{}... > >; - -template< class DuneType > -inline static auto -_addToTypeRegistry( const std::string &pyName, const IncludeFiles &inc, const GenerateTypeName &typeName ) -{ -return _addToTypeRegistry<DuneType>(std::move(pyName),typeName,inc); -} - -template< class DuneType, class... Args, std::size_t... Is > -inline static auto -_addToTypeRegistry_filter_impl( const std::string &pyName, std::tuple< Args... > &&tuple, std::index_sequence< Is... > ) -{ -return _addToTypeRegistry<DuneType>(std::move(pyName),std::get<Is>(std::move(tuple))...); -} - -template< class DuneType, class... options, class... Args > -inline static auto -generateClass ( pybind11::handle scope, const char *name, Args... args ) -{ -return pybind11::class_<DuneType,options...>(scope,name,args...); -} - -template< class DuneType, class... options, class... Args, std::size_t... Is > -inline static auto -generateClass_filter_impl ( pybind11::handle scope, const char *name, std::tuple<Args...>&& tuple, std::index_sequence< Is... > ) -{ -return generateClass<DuneType,options...>(scope,name,std::get<Is>(std::move(tuple))...); -} - -template<class B> -struct negation : std::integral_constant<bool,!bool(B::value)> { }; -template <class T> -using baseTag = std::is_base_of<TypeRegistryTag,T>; -template <class T> -using notBaseTag = negation<baseTag<T>>; - -} // namespace detail - -/** \brief Generate class name as string given the base part plus a list of the -* template arguments. -* -* This class is used to generate a string storing the full C++ class -* name, i.e., 'Foo<A,B>' given the string 'Foo' and the two types 'A' -* and 'B'. The template arguments can be given as -* - string, bool, or int (anything that works with std::to_string -* basically, so even double...) -* - Dune::MetaType<T>, in which case the type T must be available in -* the type registry -* - pybind::object, in which case the attribute _typeName muse be -* availablea -* . -* -* In the last two cases the include files stored in the type registry -* for this type or attached to the object via the '_includes' -* attribute are collected. -* -* The main constructor is -* template <class... Templ> -* GenerateTypeName(const std::string &main, Templ... templ) -* -* Further constructors are available to handle cases like -* Bar::Foo<A,B> -* The outer type can again be either given as a string, a -* Dune::MetaType, or a pybind11::object. -* -* At the moment constructs like Bar::Traits::Foo or Bar<A>::Foo<B> are -* not possible except in the case where the outer type (e.g. -* Bar::Traits) can be passed in as a string. -* -*/ -using GenerateTypeName = detail::GenerateTypeName; - -/** \brief A class used in the free standing method insertClass to tag -* the list of include files in the argument list -*/ -using IncludeFiles = detail::IncludeFiles; - -using detail::findInTypeRegistry; - -/** \brief add a type to the type registry without it being exported to python -*/ -template <class DuneType> -inline static void addToTypeRegistry ( const GenerateTypeName &typeName, -const std::vector< std::string > &inc = {} -) -{ -std::vector<std::string> includes = typeName.includes(); -includes.insert(includes.end(), inc.begin(), inc.end()); -detail::insertIntoTypeRegistry<DuneType>(typeName.name(),"",includes); -} - - -/** Function used to generate a new pybind11::class_ object and to add -* an entry to the type registry. -* -* If 'DuneType' passed in as first argument will be exported to Python -* by adding it to the given scope. If the type has already been insert -* using this method the correct pybind11::class_ object is added to -* the scope and returned. -* -* Usage: -* \code -* auto entry = insertClass<Foo<A>,py11_toption1,py11_toption2>(scope, "Foo", -* py11_option1(), py11_option2(), -* GenerateTypeName( "Foo", Dune::MetaType<A>() ), -* IncludeFiles{ "file1.hh", "file2.hh" } ); -* if (entry.second) -* registerFoo(entry.first); -* return entry.first); -* \endcode -* -* \tparam Type the type of the dune class to export -* \tparam options variadic template arguments passed on to * pybind11::class_ -* \param scope the scope into which to export the python class -* \param pyName the name to use for the python export -* \param args variadic argument passed on to the constructor of -* pybind11::class_ with the exception of an argument -* the type 'GenerateTypeName' and 'IncludeFiles' -* \return -* \code -* make_pair(pybind11::class_<Type,options...>("pyName",args...), isNew); -* \endcode -* The first argument is the pybind11::class_ -* object (either newly created or extracted from the type -* registry) The second argument is false if the type was -* already registered and otherwise it is true. -*/ -template< class Type, class... options, class... Args > -inline static std::pair< pybind11::class_< Type, options... >, bool > -insertClass ( pybind11::handle scope, std::string pyName, Args... args ) -{ -auto entry = detail::findInTypeRegistry<Type>(); -if( !entry.second ) -{ -if( scope ) -scope.attr( pyName.c_str() ) = entry.first->second.object; -return std::make_pair( static_cast< pybind11::class_< Type, options... > >( entry.first->second.object ), false ); -} -else -{ -auto entry = detail::_addToTypeRegistry_filter_impl< Type >( std::move( pyName ), std::forward_as_tuple( std::forward< Args >( args )... ), detail::Filter< detail::baseTag, std::decay_t< Args >... >{} ); -auto cls = detail::generateClass_filter_impl< Type, options...>( scope, entry.first->second.pyName.c_str(), std::forward_as_tuple( std::forward< Args >( args )... ), detail::Filter< detail::notBaseTag, std::decay_t< Args >... >{} ); -entry.first->second.object = cls; - -cls.def_property_readonly_static( "_typeName", [ entry ] ( pybind11::object ) { return entry.first->second.name; } ); -cls.def_property_readonly_static( "_includes", [ entry ] ( pybind11::object ) { return entry.first->second.includes; } ); - -return std::make_pair( cls, true ); -} -} - - -// registerTypeRegistry -// -------------------- - -inline static void registerTypeRegistry ( pybind11::module scope ) -{ -using pybind11::operator""_a; - -pybind11::class_< detail::TypeRegistry > cls( scope, "TypeRegistry" ); - -scope.attr( "typeRegistry" ) = pybind11::cast( std::make_unique< detail::TypeRegistry >() ); - -scope.def( "generateTypeName", []( std::string className, pybind11::args targs ) { -GenerateTypeName gtn( className, targs ); -return std::make_pair( gtn.name(), gtn.includes() ); -}, "className"_a ); -} - -} // namespace Python + return pybind11::cast< TypeRegistry & >( pybind11::module::import( "dune.typeregistry" ).attr( "typeRegistry" ) ); + } + + + template< class T > + inline static auto findInTypeRegistry () + { + auto pos = typeRegistry().find( typeid(T) ); + return std::make_pair( pos, pos == typeRegistry().end() ); + } + + + template< class T > + inline static auto insertIntoTypeRegistry ( + const std::string &name, + const std::string &pyName, + std::vector< std::string > includes ) + { + auto ret = typeRegistry().emplace( typeid(T), Entry() ); + if( ret.second ) + { + Entry &entry = ret.first->second; + entry.name = name; + entry.pyName = pyName; + entry.includes = std::move( includes ); + } + return ret; + } + + + + // TypeRegistryTag + // --------------- + + struct TypeRegistryTag {}; + + + + // GenerateTypeName + // ---------------- + + struct GenerateTypeName + : public TypeRegistryTag + { + template <class... Templ> + GenerateTypeName(const std::string &main, Templ... templ) + : main_(main) + { + templates(templ...); + } + template <class T, class... options, class... Templ> + GenerateTypeName(const std::string &outer, const std::string &main, Templ... templ) + : main_(outer+"::"+main) + { + templates(templ...); + } + template <class... Templ> + GenerateTypeName(pybind11::handle &outer, const std::string &main, Templ... templ) + { + main_ = getTypeName(outer) + "::" + main; + includes_.push_back(getIncludes(outer)); + std::sort( includes_.begin(), includes_.end() ); + includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); + templates(templ...); + } + template <class Outer, class... Templ> + GenerateTypeName(Dune::MetaType<Outer>, const std::string &main, Templ... templ) + { + const auto& outerEntry = findInTypeRegistry<Outer>(); + if (outerEntry.second) + throw std::invalid_argument( (std::string("couldn't find outer class ") + + typeid(Outer).name() + " in type registry").c_str() ); + main_ = outerEntry.first->second.name + "::" + main; + includes_.push_back(outerEntry.first->second.includes); + std::sort( includes_.begin(), includes_.end() ); + includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); + templates(templ...); + } + GenerateTypeName(const std::string &main, pybind11::args args) + : main_(main) + { + const std::size_t sz = args.size(); + for( std::size_t i = 0; i < sz; ++i ) + { + templates_.insert( templates_.end(), getTypeName( (pybind11::handle)(args[i]) ) ); + includes_.insert( includes_.end(), getIncludes( (pybind11::handle)(args[i]) ) ); + std::sort( includes_.begin(), includes_.end() ); + includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); + } + } + + std::string name () const + { + std::string fullName = main_; + if( !templates_.empty() ) + { + const char *delim = "< "; + for( const auto &t : templates_ ) + { + fullName += delim + t; + delim = ", "; + } + fullName += " >"; + } + return fullName; + } + std::vector<std::string> includes() const + { + std::vector<std::string> ret; + for (const auto &i : includes_) + ret.insert( ret.end(), i.begin(), i.end() ); + return ret; + } + + private: + template <class... Args> + void templates(Args... args) + { + templates_.insert(templates_.end(), { getTypeName( std::forward< Args >( args ) )... } ); + includes_.insert( includes_.end(), { getIncludes( std::forward<Args >( args ) )... } ); + std::sort( includes_.begin(), includes_.end() ); + includes_.erase( std::unique( includes_.begin(), includes_.end() ), includes_.end() ); + } + template <class T,class... options> + std::string getTypeName(const pybind11::class_<T,options...> &obj) + { + return getTypeName(static_cast<pybind11::handle>(obj)); + } + std::string getTypeName(const pybind11::object &obj) + { + return getTypeName(static_cast<pybind11::handle>(obj)); + } + std::string getTypeName(const pybind11::handle &obj) + { + try + { + return obj.attr( "_typeName" ).cast<std::string>(); + } + catch(const pybind11::error_already_set& ) + { + return pybind11::str(obj); + } + } + static std::string getTypeName ( const GenerateTypeName &name ) { return name.name(); } + std::string getTypeName ( const std::string &name ) { return name; } + std::string getTypeName ( const char *name ) { return name; } + template <class T> + std::string getTypeName ( const Dune::MetaType<T> & ) + { + auto entry = detail::findInTypeRegistry<T>(); + if (entry.second) + throw std::invalid_argument( (std::string("couldn't find requested type ") + + typeid(T).name() + " in type registry").c_str() ); + return entry.first->second.name; + } + template <class T> + std::string getTypeName ( const T& t ) { return std::to_string(t); } + + static std::vector<std::string> getIncludes(pybind11::handle obj) + { + try + { + return obj.attr( "_includes" ).cast<std::vector<std::string>>(); + } + catch(const pybind11::error_already_set& ) + { + return {}; + } + } + static std::vector< std::string > getIncludes ( const GenerateTypeName &name ) { return name.includes(); } + template <class T> + const std::vector<std::string> &getIncludes ( const Dune::MetaType<T> & ) + { + auto entry = detail::findInTypeRegistry<T>(); + if (entry.second) + throw std::invalid_argument( (std::string("couldn't find requested type ") + + typeid(T).name() + " in type registry").c_str() ); + return entry.first->second.includes; + } + template <class T> + std::vector<std::string> getIncludes ( const T& ) { return {}; } + + std::string main_; + std::vector<std::string> templates_; + std::vector<std::vector<std::string>> includes_; + }; + + + + // IncludeFiles + // ------------ + + struct IncludeFiles + : public std::vector< std::string >, + TypeRegistryTag + { + template <class... Args> + IncludeFiles(Args... args) + : std::vector<std::string>({args...}) {} + }; + + + template< class DuneType > + inline static auto _addToTypeRegistry ( std::string pyName, const GenerateTypeName &typeName, const std::vector< std::string > &inc = {} ) + { + std::vector< std::string > includes = typeName.includes(); + includes.insert( includes.end(), inc.begin(), inc.end() ); + auto entry = detail::insertIntoTypeRegistry< DuneType >( typeName.name(), std::move( pyName ), includes ); + if( !entry.second ) + throw std::invalid_argument( std::string( "adding a class (" ) + typeid( DuneType ).name() + ") twice to the type registry" ); + return entry; + } + + + template <typename S, typename M, typename O = std::index_sequence<>> + struct filter : O {}; + template <std::size_t I, std::size_t... Is, + std::size_t... Js, std::size_t... Ks> + struct filter<std::index_sequence<I, Is...>, std::index_sequence<0, Js...>, + std::index_sequence<Ks...>> + : filter<std::index_sequence<Is...>, std::index_sequence<Js...>, + std::index_sequence<Ks...>> {}; + template <std::size_t I, std::size_t... Is, std::size_t... Js, + std::size_t... Ks> + struct filter<std::index_sequence<I, Is...>, std::index_sequence<1, Js...>, + std::index_sequence<Ks...>> + : filter<std::index_sequence<Is...>, std::index_sequence<Js...>, + std::index_sequence<Ks..., I>> {}; + + template< template< class T> class F, class... Args > + using Filter = filter< std::index_sequence_for< Args... >, std::index_sequence< F< Args >{}... > >; + + template< class DuneType > + inline static auto + _addToTypeRegistry( const std::string &pyName, const IncludeFiles &inc, const GenerateTypeName &typeName ) + { + return _addToTypeRegistry<DuneType>(std::move(pyName),typeName,inc); + } + + template< class DuneType, class... Args, std::size_t... Is > + inline static auto + _addToTypeRegistry_filter_impl( const std::string &pyName, std::tuple< Args... > &&tuple, std::index_sequence< Is... > ) + { + return _addToTypeRegistry<DuneType>(std::move(pyName),std::get<Is>(std::move(tuple))...); + } + + template< class DuneType, class... options, class... Args > + inline static auto + generateClass ( pybind11::handle scope, const char *name, Args... args ) + { + return pybind11::class_<DuneType,options...>(scope,name,args...); + } + + template< class DuneType, class... options, class... Args, std::size_t... Is > + inline static auto + generateClass_filter_impl ( pybind11::handle scope, const char *name, std::tuple<Args...>&& tuple, std::index_sequence< Is... > ) + { + return generateClass<DuneType,options...>(scope,name,std::get<Is>(std::move(tuple))...); + } + + template<class B> + struct negation : std::integral_constant<bool,!bool(B::value)> { }; + template <class T> + using baseTag = std::is_base_of<TypeRegistryTag,T>; + template <class T> + using notBaseTag = negation<baseTag<T>>; + + } // namespace detail + + /** \brief Generate class name as string given the base part plus a list of the + * template arguments. + * + * This class is used to generate a string storing the full C++ class + * name, i.e., 'Foo<A,B>' given the string 'Foo' and the two types 'A' + * and 'B'. The template arguments can be given as + * - string, bool, or int (anything that works with std::to_string + * basically, so even double...) + * - Dune::MetaType<T>, in which case the type T must be available in + * the type registry + * - pybind::object, in which case the attribute _typeName muse be + * availablea + * . + * + * In the last two cases the include files stored in the type registry + * for this type or attached to the object via the '_includes' + * attribute are collected. + * + * The main constructor is + * template <class... Templ> + * GenerateTypeName(const std::string &main, Templ... templ) + * + * Further constructors are available to handle cases like + * Bar::Foo<A,B> + * The outer type can again be either given as a string, a + * Dune::MetaType, or a pybind11::object. + * + * At the moment constructs like Bar::Traits::Foo or Bar<A>::Foo<B> are + * not possible except in the case where the outer type (e.g. + * Bar::Traits) can be passed in as a string. + * + */ + using GenerateTypeName = detail::GenerateTypeName; + + /** \brief A class used in the free standing method insertClass to tag + * the list of include files in the argument list + */ + using IncludeFiles = detail::IncludeFiles; + + using detail::findInTypeRegistry; + + /** \brief add a type to the type registry without it being exported to python + */ + template <class DuneType> + inline static void addToTypeRegistry ( const GenerateTypeName &typeName, + const std::vector< std::string > &inc = {} + ) + { + std::vector<std::string> includes = typeName.includes(); + includes.insert(includes.end(), inc.begin(), inc.end()); + detail::insertIntoTypeRegistry<DuneType>(typeName.name(),"",includes); + } + + + /** Function used to generate a new pybind11::class_ object and to add + * an entry to the type registry. + * + * If 'DuneType' passed in as first argument will be exported to Python + * by adding it to the given scope. If the type has already been insert + * using this method the correct pybind11::class_ object is added to + * the scope and returned. + * + * Usage: + * \code + * auto entry = insertClass<Foo<A>,py11_toption1,py11_toption2>(scope, "Foo", + * py11_option1(), py11_option2(), + * GenerateTypeName( "Foo", Dune::MetaType<A>() ), + * IncludeFiles{ "file1.hh", "file2.hh" } ); + * if (entry.second) + * registerFoo(entry.first); + * return entry.first); + * \endcode + * + * \tparam Type the type of the dune class to export + * \tparam options variadic template arguments passed on to * pybind11::class_ + * \param scope the scope into which to export the python class + * \param pyName the name to use for the python export + * \param args variadic argument passed on to the constructor of + * pybind11::class_ with the exception of an argument + * the type 'GenerateTypeName' and 'IncludeFiles' + * \return + * \code + * make_pair(pybind11::class_<Type,options...>("pyName",args...), isNew); + * \endcode + * The first argument is the pybind11::class_ + * object (either newly created or extracted from the type + * registry) The second argument is false if the type was + * already registered and otherwise it is true. + */ + template< class Type, class... options, class... Args > + inline static std::pair< pybind11::class_< Type, options... >, bool > + insertClass ( pybind11::handle scope, std::string pyName, Args... args ) + { + auto entry = detail::findInTypeRegistry<Type>(); + if( !entry.second ) + { + if( scope ) + scope.attr( pyName.c_str() ) = entry.first->second.object; + return std::make_pair( static_cast< pybind11::class_< Type, options... > >( entry.first->second.object ), false ); + } + else + { + auto entry = detail::_addToTypeRegistry_filter_impl< Type >( std::move( pyName ), std::forward_as_tuple( std::forward< Args >( args )... ), detail::Filter< detail::baseTag, std::decay_t< Args >... >{} ); + auto cls = detail::generateClass_filter_impl< Type, options...>( scope, entry.first->second.pyName.c_str(), std::forward_as_tuple( std::forward< Args >( args )... ), detail::Filter< detail::notBaseTag, std::decay_t< Args >... >{} ); + entry.first->second.object = cls; + + cls.def_property_readonly_static( "_typeName", [ entry ] ( pybind11::object ) { return entry.first->second.name; } ); + cls.def_property_readonly_static( "_includes", [ entry ] ( pybind11::object ) { return entry.first->second.includes; } ); + + return std::make_pair( cls, true ); + } + } + + + // registerTypeRegistry + // -------------------- + + inline static void registerTypeRegistry ( pybind11::module scope ) + { + using pybind11::operator""_a; + + pybind11::class_< detail::TypeRegistry > cls( scope, "TypeRegistry" ); + + scope.attr( "typeRegistry" ) = pybind11::cast( std::make_unique< detail::TypeRegistry >() ); + + scope.def( "generateTypeName", []( std::string className, pybind11::args targs ) { + GenerateTypeName gtn( className, targs ); + return std::make_pair( gtn.name(), gtn.includes() ); + }, "className"_a ); + } + + } // namespace Python } // namespace Dune diff --git a/dune/python/common/vector.hh b/dune/python/common/vector.hh index 91dc5b1ac76aa763f99fa836a99865d8b31cfd31..9939f0f158d75cd737ee5f717770bc6f17247558 100644 --- a/dune/python/common/vector.hh +++ b/dune/python/common/vector.hh @@ -16,275 +16,275 @@ namespace Dune { -// External Forward Declarations -// ----------------------------- + // External Forward Declarations + // ----------------------------- -template< class V > -class DenseVector; + template< class V > + class DenseVector; -template< class K, int n > -class FieldVector; + template< class K, int n > + class FieldVector; -template< class K, int m, int n > -class FieldMatrix; + template< class K, int m, int n > + class FieldMatrix; -namespace Imp -{ - -template< class B, class A > -class block_vector_unmanaged; - -template< class B, class A > -class compressed_block_vector_unmanaged; - -} // namespace Imp - - - -namespace Python -{ - -// IsDenseVector -// ------------- - -template< class T, class = void > -struct IsDenseVector -: public std::false_type -{}; - -template< class T > -struct IsDenseVector< T, std::enable_if_t< std::is_base_of< Dune::DenseVector< T >, T >::value > > -: public std::true_type -{}; - - - -// IsBlockVector -// ------------- - -template< class T, class = void > -struct IsBlockVector -: public std::false_type -{}; - -template< class T > -struct IsBlockVector< T, std::enable_if_t< std::is_base_of< Imp::block_vector_unmanaged< typename T::block_type, typename T::allocator_type >, T >::value > > -: public std::true_type -{}; - -template< class T > -struct IsBlockVector< T, std::enable_if_t< std::is_base_of< Imp::compressed_block_vector_unmanaged< typename T::block_type, typename T::allocator_type >, T >::value > > -: public std::true_type -{}; - - - -// IsOneTensor -// ----------- - -template< class T, class = void > -struct IsOneTensor -: public std::false_type -{}; + namespace Imp + { -template< class T > -struct IsOneTensor< T, std::enable_if_t< IsDenseVector< T >::value > > -: public std::true_type -{}; + template< class B, class A > + class block_vector_unmanaged; -template< class T > -struct IsOneTensor< T, std::enable_if_t< IsBlockVector< T >::value && IsOneTensor< typename T::block_type >::value > > -: public std::true_type -{}; + template< class B, class A > + class compressed_block_vector_unmanaged; + } // namespace Imp -namespace detail -{ -// registerOneTensorInterface -// -------------------------- + namespace Python + { -template< class T, class... options > -inline static auto registerOneTensorInterface ( pybind11::class_< T, options... > cls, PriorityTag< 1 > ) --> std::enable_if_t< IsOneTensor< T >::value > -{ -cls.def( "__mul__", [] ( const T &self, const T &other ) { return self * other; } ); -cls.def( "__rmul__", [] ( const T &self, const T &other ) { return self * other; } ); - -cls.def_property_readonly( "one_norm", [] ( const T &self ) { return self.one_norm(); } ); -cls.def_property_readonly( "one_norm_real", [] ( const T &self ) { return self.one_norm_real(); } ); -cls.def_property_readonly( "two_norm", [] ( const T &self ) { return self.two_norm(); } ); -cls.def_property_readonly( "two_norm2", [] ( const T &self ) { return self.two_norm2(); } ); -cls.def_property_readonly( "infinity_norm", [] ( const T &self ) { return self.infinity_norm(); } ); -cls.def_property_readonly( "infinity_norm_real", [] ( const T &self ) { return self.infinity_norm_real(); } ); -} - -template< class T, class... options > -inline static void registerOneTensorInterface ( pybind11::class_< T, options... > cls, PriorityTag< 0 > ) -{} - -template< class T, class... options > -inline static void registerOneTensorInterface ( pybind11::class_< T, options... > cls ) -{ -registerOneTensorInterface( cls, PriorityTag< 42 >() ); -} + // IsDenseVector + // ------------- -} // namespace detail + template< class T, class = void > + struct IsDenseVector + : public std::false_type + {}; + template< class T > + struct IsDenseVector< T, std::enable_if_t< std::is_base_of< Dune::DenseVector< T >, T >::value > > + : public std::true_type + {}; -// FixedTensorTraits -// ----------------- -template< class T, class = void > -struct FixedTensorTraits; + // IsBlockVector + // ------------- -template< class T > -struct FixedTensorTraits< T, std::enable_if_t< IsNumber< T >::value > > -{ -static constexpr std::array< ssize_t, 0 > shape () noexcept { return {{}}; } -}; + template< class T, class = void > + struct IsBlockVector + : public std::false_type + {}; -template< class K, int n > -struct FixedTensorTraits< FieldVector< K, n >, void > -{ -static constexpr std::array< ssize_t, 1 > shape () noexcept { return {{ n }}; } -}; + template< class T > + struct IsBlockVector< T, std::enable_if_t< std::is_base_of< Imp::block_vector_unmanaged< typename T::block_type, typename T::allocator_type >, T >::value > > + : public std::true_type + {}; -template< class K, int m, int n > -struct FixedTensorTraits< FieldMatrix< K, m, n >, void > -{ -static constexpr std::array< ssize_t, 2 > shape () noexcept { return {{ m, n }}; } -}; + template< class T > + struct IsBlockVector< T, std::enable_if_t< std::is_base_of< Imp::compressed_block_vector_unmanaged< typename T::block_type, typename T::allocator_type >, T >::value > > + : public std::true_type + {}; -// extendArray -// ----------- + // IsOneTensor + // ----------- -template< std::size_t... i, class T, class... X > -inline static constexpr auto extendArray ( std::index_sequence< i... >, const std::array< T, sizeof...( i ) > &array, X &&... x ) --> std::enable_if_t< std::conjunction< std::is_convertible< X, T >... >::value, std::array< T, sizeof...( i )+sizeof...( X ) > > -{ -return {{ array[ i ]..., std::forward< X >( x )... }}; -} + template< class T, class = void > + struct IsOneTensor + : public std::false_type + {}; -template< class T, std::size_t n, class... X > -inline static constexpr std::array< T, n+sizeof...( X ) > extendArray ( const std::array< T, n > &array, X &&... x ) -{ -return extendArray( std::make_index_sequence< n >(), array, std::forward< X >( x )... ); -} + template< class T > + struct IsOneTensor< T, std::enable_if_t< IsDenseVector< T >::value > > + : public std::true_type + {}; + template< class T > + struct IsOneTensor< T, std::enable_if_t< IsBlockVector< T >::value && IsOneTensor< typename T::block_type >::value > > + : public std::true_type + {}; -// getFixedTensor -// -------------- -template< std::size_t... k, class X, class Y, std::size_t n, class... I > -inline static auto getFixedTensor ( std::index_sequence< k... >, X &x, const Y &y, std::array< ssize_t, n > j, I... i ) --> std::enable_if_t< (sizeof...( k ) == n) > -{ -x = y( j[ k ]..., i... ); -} + namespace detail + { -template< std::size_t... k, class X, class Y, std::size_t n, class... I > -inline static auto getFixedTensor ( std::index_sequence< k... >, X &x, const Y &y, std::array< ssize_t, n > j, I... i ) --> std::enable_if_t< (sizeof...( k ) < n) > -{ -ssize_t &it = j[ sizeof...( k ) ]; -ssize_t end = it; -for( it = 0; it < end; ++it ) -getFixedTensor( std::index_sequence< k..., sizeof...( k ) >(), x[ it ], y, j, i... ); -} - -template< class X, class Y, class... I > -inline static auto getFixedTensor ( X &x, const Y &y, I... i ) -{ -getFixedTensor( std::index_sequence<>(), x, y, FixedTensorTraits< X >::shape(), i... ); -} + // registerOneTensorInterface + // -------------------------- + template< class T, class... options > + inline static auto registerOneTensorInterface ( pybind11::class_< T, options... > cls, PriorityTag< 1 > ) + -> std::enable_if_t< IsOneTensor< T >::value > + { + cls.def( "__mul__", [] ( const T &self, const T &other ) { return self * other; } ); + cls.def( "__rmul__", [] ( const T &self, const T &other ) { return self * other; } ); + cls.def_property_readonly( "one_norm", [] ( const T &self ) { return self.one_norm(); } ); + cls.def_property_readonly( "one_norm_real", [] ( const T &self ) { return self.one_norm_real(); } ); + cls.def_property_readonly( "two_norm", [] ( const T &self ) { return self.two_norm(); } ); + cls.def_property_readonly( "two_norm2", [] ( const T &self ) { return self.two_norm2(); } ); + cls.def_property_readonly( "infinity_norm", [] ( const T &self ) { return self.infinity_norm(); } ); + cls.def_property_readonly( "infinity_norm_real", [] ( const T &self ) { return self.infinity_norm_real(); } ); + } -// setFixedTensor -// -------------- + template< class T, class... options > + inline static void registerOneTensorInterface ( pybind11::class_< T, options... > cls, PriorityTag< 0 > ) + {} -template< std::size_t... k, class X, class Y, std::size_t n, class... I > -inline static auto setFixedTensor ( std::index_sequence< k... >, const X &x, Y &y, std::array< ssize_t, n > j, I... i ) --> std::enable_if_t< (sizeof...( k ) == n) > -{ -y( j[ k ]..., i... ) = x; -} + template< class T, class... options > + inline static void registerOneTensorInterface ( pybind11::class_< T, options... > cls ) + { + registerOneTensorInterface( cls, PriorityTag< 42 >() ); + } -template< std::size_t... k, class X, class Y, std::size_t n, class... I > -inline static auto setFixedTensor ( std::index_sequence< k... >, const X &x, Y &y, std::array< ssize_t, n > j, I... i ) --> std::enable_if_t< (sizeof...( k ) < n) > -{ -ssize_t &it = j[ sizeof...( k ) ]; -ssize_t end = it; -for( it = 0; it < end; ++it ) -setFixedTensor( std::index_sequence< k..., sizeof...( k ) >(), x[ it ], y, j, i... ); -} - -template< class X, class Y, class... I > -inline static auto setFixedTensor ( const X &x, Y &y, I... i ) -{ -setFixedTensor( std::index_sequence<>(), x, y, FixedTensorTraits< X >::shape(), i... ); -} + } // namespace detail -// vectorize -// --------- - -template< class F, class Y, class X > -inline static pybind11::object vectorize ( F &&f, Y (*)( X ), pybind11::array_t< typename FieldTraits< std::decay_t< X > >::field_type > xArray ) -{ -const auto xShape = FixedTensorTraits< std::decay_t< X > >::shape(); + // FixedTensorTraits + // ----------------- -auto x = xArray.unchecked(); -if( (std::size_t)x.ndim() < xShape.size() ) -throw pybind11::value_error( "Tensor has too few dimensions" ); + template< class T, class = void > + struct FixedTensorTraits; -for( auto i : range( xShape.size() ) ) -{ -if( x.shape( i ) != xShape[ i ] ) -throw pybind11::value_error( "Tensor has wrong shape" ); -} + template< class T > + struct FixedTensorTraits< T, std::enable_if_t< IsNumber< T >::value > > + { + static constexpr std::array< ssize_t, 0 > shape () noexcept { return {{}}; } + }; -std::decay_t< X > xi; -if( (xShape.size() > 0) && (x.ndim() == xShape.size()) ) -{ -getFixedTensor( xi, x ); -return pybind11::cast( f( xi ) ); -} -else if( x.ndim() == xShape.size() + 1 ) -{ -const ssize_t size = x.shape( xShape.size() ); -const auto yShape = extendArray( FixedTensorTraits< std::decay_t< Y > >::shape(), size ); + template< class K, int n > + struct FixedTensorTraits< FieldVector< K, n >, void > + { + static constexpr std::array< ssize_t, 1 > shape () noexcept { return {{ n }}; } + }; -pybind11::array_t< typename FieldTraits< std::decay_t< Y > >::field_type > yArray( yShape ); -auto y = yArray.template mutable_unchecked< yShape.size() >(); + template< class K, int m, int n > + struct FixedTensorTraits< FieldMatrix< K, m, n >, void > + { + static constexpr std::array< ssize_t, 2 > shape () noexcept { return {{ m, n }}; } + }; -for( auto i : range( size ) ) -{ -getFixedTensor( xi, x, i ); -setFixedTensor( f( xi ), y, i ); -} -return std::move(yArray); -} -else -throw pybind11::value_error( "Tensor has too many dimensions" ); -} - -template< class F, class X > -inline static auto vectorize ( F &&f, pybind11::array_t< X > xArray ) --> decltype( vectorize( std::forward< F >( f ), static_cast< pybind11::detail::function_signature_t< F > * >( nullptr ), std::move( xArray ) ) ) -{ -return vectorize( std::forward< F >( f ), static_cast< pybind11::detail::function_signature_t< F > * >( nullptr ), std::move( xArray ) ); -} -} // namespace Python + + // extendArray + // ----------- + + template< std::size_t... i, class T, class... X > + inline static constexpr auto extendArray ( std::index_sequence< i... >, const std::array< T, sizeof...( i ) > &array, X &&... x ) + -> std::enable_if_t< std::conjunction< std::is_convertible< X, T >... >::value, std::array< T, sizeof...( i )+sizeof...( X ) > > + { + return {{ array[ i ]..., std::forward< X >( x )... }}; + } + + template< class T, std::size_t n, class... X > + inline static constexpr std::array< T, n+sizeof...( X ) > extendArray ( const std::array< T, n > &array, X &&... x ) + { + return extendArray( std::make_index_sequence< n >(), array, std::forward< X >( x )... ); + } + + + + // getFixedTensor + // -------------- + + template< std::size_t... k, class X, class Y, std::size_t n, class... I > + inline static auto getFixedTensor ( std::index_sequence< k... >, X &x, const Y &y, std::array< ssize_t, n > j, I... i ) + -> std::enable_if_t< (sizeof...( k ) == n) > + { + x = y( j[ k ]..., i... ); + } + + template< std::size_t... k, class X, class Y, std::size_t n, class... I > + inline static auto getFixedTensor ( std::index_sequence< k... >, X &x, const Y &y, std::array< ssize_t, n > j, I... i ) + -> std::enable_if_t< (sizeof...( k ) < n) > + { + ssize_t &it = j[ sizeof...( k ) ]; + ssize_t end = it; + for( it = 0; it < end; ++it ) + getFixedTensor( std::index_sequence< k..., sizeof...( k ) >(), x[ it ], y, j, i... ); + } + + template< class X, class Y, class... I > + inline static auto getFixedTensor ( X &x, const Y &y, I... i ) + { + getFixedTensor( std::index_sequence<>(), x, y, FixedTensorTraits< X >::shape(), i... ); + } + + + + // setFixedTensor + // -------------- + + template< std::size_t... k, class X, class Y, std::size_t n, class... I > + inline static auto setFixedTensor ( std::index_sequence< k... >, const X &x, Y &y, std::array< ssize_t, n > j, I... i ) + -> std::enable_if_t< (sizeof...( k ) == n) > + { + y( j[ k ]..., i... ) = x; + } + + template< std::size_t... k, class X, class Y, std::size_t n, class... I > + inline static auto setFixedTensor ( std::index_sequence< k... >, const X &x, Y &y, std::array< ssize_t, n > j, I... i ) + -> std::enable_if_t< (sizeof...( k ) < n) > + { + ssize_t &it = j[ sizeof...( k ) ]; + ssize_t end = it; + for( it = 0; it < end; ++it ) + setFixedTensor( std::index_sequence< k..., sizeof...( k ) >(), x[ it ], y, j, i... ); + } + + template< class X, class Y, class... I > + inline static auto setFixedTensor ( const X &x, Y &y, I... i ) + { + setFixedTensor( std::index_sequence<>(), x, y, FixedTensorTraits< X >::shape(), i... ); + } + + + + // vectorize + // --------- + + template< class F, class Y, class X > + inline static pybind11::object vectorize ( F &&f, Y (*)( X ), pybind11::array_t< typename FieldTraits< std::decay_t< X > >::field_type > xArray ) + { + const auto xShape = FixedTensorTraits< std::decay_t< X > >::shape(); + + auto x = xArray.unchecked(); + if( (std::size_t)x.ndim() < xShape.size() ) + throw pybind11::value_error( "Tensor has too few dimensions" ); + + for( auto i : range( xShape.size() ) ) + { + if( x.shape( i ) != xShape[ i ] ) + throw pybind11::value_error( "Tensor has wrong shape" ); + } + + std::decay_t< X > xi; + if( (xShape.size() > 0) && (x.ndim() == xShape.size()) ) + { + getFixedTensor( xi, x ); + return pybind11::cast( f( xi ) ); + } + else if( x.ndim() == xShape.size() + 1 ) + { + const ssize_t size = x.shape( xShape.size() ); + const auto yShape = extendArray( FixedTensorTraits< std::decay_t< Y > >::shape(), size ); + + pybind11::array_t< typename FieldTraits< std::decay_t< Y > >::field_type > yArray( yShape ); + auto y = yArray.template mutable_unchecked< yShape.size() >(); + + for( auto i : range( size ) ) + { + getFixedTensor( xi, x, i ); + setFixedTensor( f( xi ), y, i ); + } + return std::move(yArray); + } + else + throw pybind11::value_error( "Tensor has too many dimensions" ); + } + + template< class F, class X > + inline static auto vectorize ( F &&f, pybind11::array_t< X > xArray ) + -> decltype( vectorize( std::forward< F >( f ), static_cast< pybind11::detail::function_signature_t< F > * >( nullptr ), std::move( xArray ) ) ) + { + return vectorize( std::forward< F >( f ), static_cast< pybind11::detail::function_signature_t< F > * >( nullptr ), std::move( xArray ) ); + } + + } // namespace Python } // namespace Dune