GEOSX
sortedArrayManipulation.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020, Lawrence Livermore National Security, LLC and LvArray contributors.
3  * All rights reserved.
4  * See the LICENSE file for details.
5  * SPDX-License-Identifier: (BSD-3-Clause)
6  */
7 
15 #pragma once
16 
17 // Source includes
18 #include "Macros.hpp"
19 #include "arrayManipulation.hpp"
21 
22 // System includes
23 #include <cstdlib> // for std::malloc and std::free.
24 #include <algorithm> // for std::sort
25 
26 namespace LvArray
27 {
28 
35 namespace sortedArrayManipulation
36 {
37 
43 {
44  SORTED_UNIQUE,
45  UNSORTED_NO_DUPLICATES,
46  SORTED_WITH_DUPLICATES,
47  UNSORTED_WITH_DUPLICATES
48 };
49 
54 LVARRAY_HOST_DEVICE constexpr inline
55 bool isSorted( Description const desc )
56 { return desc == SORTED_UNIQUE || desc == SORTED_WITH_DUPLICATES; }
57 
62 LVARRAY_HOST_DEVICE constexpr inline
63 bool isUnique( Description const desc )
64 { return desc == SORTED_UNIQUE || desc == UNSORTED_NO_DUPLICATES; }
65 
71 template< typename T >
72 class CallBacks
73 {
74 public:
75 
82  LVARRAY_HOST_DEVICE inline
83  T * incrementSize( T * const curPtr,
84  std::ptrdiff_t const nToAdd )
85  {
86  LVARRAY_UNUSED_VARIABLE( nToAdd );
87  return curPtr;
88  }
89 
94  LVARRAY_HOST_DEVICE inline
95  void insert( std::ptrdiff_t const pos )
96  { LVARRAY_UNUSED_VARIABLE( pos ); }
97 
104  LVARRAY_HOST_DEVICE inline
105  void set( std::ptrdiff_t const pos,
106  std::ptrdiff_t const valuePos )
107  {
109  LVARRAY_UNUSED_VARIABLE( valuePos );
110  }
111 
121  LVARRAY_HOST_DEVICE inline
122  void insert( std::ptrdiff_t const nLeftToInsert,
123  std::ptrdiff_t const valuePos,
124  std::ptrdiff_t const pos,
125  std::ptrdiff_t const prevPos )
126  {
127  LVARRAY_UNUSED_VARIABLE( nLeftToInsert );
128  LVARRAY_UNUSED_VARIABLE( valuePos );
130  LVARRAY_UNUSED_VARIABLE( prevPos );
131  }
132 
137  LVARRAY_HOST_DEVICE inline
138  void remove( std::ptrdiff_t const pos )
139  { LVARRAY_UNUSED_VARIABLE( pos ); }
140 
149  LVARRAY_HOST_DEVICE inline
150  void remove( std::ptrdiff_t const nRemoved,
151  std::ptrdiff_t const curPos,
152  std::ptrdiff_t const nextPos )
153  {
154  LVARRAY_UNUSED_VARIABLE( nRemoved );
155  LVARRAY_UNUSED_VARIABLE( curPos );
156  LVARRAY_UNUSED_VARIABLE( nextPos );
157  }
158 };
159 
165 template< typename T >
166 struct less
167 {
174  constexpr LVARRAY_HOST_DEVICE inline
175  bool operator() ( T const & lhs, T const & rhs ) const
176  { return lhs < rhs; }
177 };
178 
184 template< typename T >
185 struct greater
186 {
193  constexpr LVARRAY_HOST_DEVICE inline
194  bool operator() ( T const & lhs, T const & rhs ) const
195  { return lhs > rhs; }
196 };
197 
208 template< typename RandomAccessIterator,
210 LVARRAY_HOST_DEVICE inline void makeSorted( RandomAccessIterator const first,
211  RandomAccessIterator const last,
212  Compare && comp=Compare() )
213 {
214 #ifdef __CUDA_ARCH__
215  if( last - first > internal::INTROSORT_THRESHOLD )
216  {
217  internal::introsortLoop( first, last, comp );
218  }
219 
220  internal::insertionSort( first, last - first, comp );
221 #else
222  std::sort( first, last, std::forward< Compare >( comp ) );
223 #endif
224 }
225 
238 template< typename RandomAccessIteratorA,
239  typename RandomAccessIteratorB,
241 LVARRAY_HOST_DEVICE inline void dualSort( RandomAccessIteratorA valueFirst, RandomAccessIteratorA valueLast,
242  RandomAccessIteratorB dataFirst, Compare && comp=Compare() )
243 {
244  std::ptrdiff_t const size = valueLast - valueFirst;
245  internal::DualIterator< RandomAccessIteratorA, RandomAccessIteratorB > dualIter( valueFirst, dataFirst );
246 
247  auto dualCompare = dualIter.createComparator( std::forward< Compare >( comp ) );
248 
249  if( size > internal::INTROSORT_THRESHOLD )
250  {
251  internal::introsortLoop( dualIter, dualIter + size, dualCompare );
252  }
253 
254  internal::insertionSort( dualIter, size, dualCompare );
255 }
256 
267 template< typename ITER, typename Compare=less< typename std::iterator_traits< ITER >::value_type > >
268 LVARRAY_HOST_DEVICE inline
269 bool isSorted( ITER first, ITER const last, Compare && comp=Compare() )
270 {
271  if( first == last )
272  {
273  return true;
274  }
275 
276  ITER next = first;
277  ++next;
278  while( next != last )
279  {
280  if( comp( *next, *first ) )
281  {
282  return false;
283  }
284 
285  first = next;
286  ++next;
287  }
288 
289  return true;
290 }
291 
305 template< typename ITER, typename Compare=less< typename std::iterator_traits< ITER >::value_type > >
306 LVARRAY_HOST_DEVICE inline
307 std::ptrdiff_t removeDuplicates( ITER first, ITER const last, Compare && comp=Compare() )
308 {
309  LVARRAY_ASSERT( isSorted( first, last, comp ) );
310 
311  if( first == last )
312  {
313  return 0;
314  }
315 
316  std::ptrdiff_t numUnique = 1;
317 
327  ITER next = first;
328  for(; next != last; ++next )
329  {
330  if( comp( *first, *next ) )
331  {
332  ++numUnique;
333  ++first;
334  if( first != next )
335  {
336  *first = std::move( *next );
337  }
338  }
339  }
340 
341  return numUnique;
342 }
343 
354 template< typename ITER, typename Compare=less< typename std::iterator_traits< ITER >::value_type > >
355 LVARRAY_HOST_DEVICE inline
356 std::ptrdiff_t makeSortedUnique( ITER const first, ITER const last, Compare && comp=Compare() )
357 {
358  makeSorted( first, last, comp );
359  return removeDuplicates( first, last, comp );
360 }
361 
372 template< typename ITER, typename Compare=less< typename std::iterator_traits< ITER >::value_type > >
373 LVARRAY_HOST_DEVICE inline
374 bool isSortedUnique( ITER first, ITER const last, Compare && comp=Compare() )
375 {
376  if( first == last )
377  {
378  return true;
379  }
380 
381  ITER next = first;
382  ++next;
383  while( next != last )
384  {
385  if( comp( *next, *first ) || !comp( *first, *next ) )
386  {
387  return false;
388  }
389 
390  first = next;
391  ++next;
392  }
393 
394  return true;
395 }
396 
409 template< typename T, typename Compare=less< T > >
410 LVARRAY_HOST_DEVICE inline
411 std::ptrdiff_t find( T const * const LVARRAY_RESTRICT ptr,
412  std::ptrdiff_t const size,
413  T const & value,
414  Compare && comp=Compare() )
415 {
416  LVARRAY_ASSERT( ptr != nullptr || size == 0 );
418  LVARRAY_ASSERT( isSorted( ptr, ptr + size, comp ) );
419 
420  std::ptrdiff_t lower = 0;
421  std::ptrdiff_t upper = size;
422  while( lower != upper )
423  {
424  std::ptrdiff_t const guess = (lower + upper) / 2;
425  if( comp( ptr[guess], value ) )
426  {
427  lower = guess + 1;
428  }
429  else
430  {
431  upper = guess;
432  }
433  }
434 
435  return lower;
436 }
437 
449 template< typename T, typename Compare=less< T > >
450 LVARRAY_HOST_DEVICE inline
451 bool contains( T const * const LVARRAY_RESTRICT ptr,
452  std::ptrdiff_t const size,
453  T const & value,
454  Compare && comp=Compare() )
455 {
456  std::ptrdiff_t const pos = find( ptr, size, value, std::forward< Compare >( comp ) );
457  return (pos != size) && (ptr[pos] == value);
458 }
459 
471 template< typename T, typename CALLBACKS >
472 LVARRAY_HOST_DEVICE inline
473 bool remove( T * const LVARRAY_RESTRICT ptr,
474  std::ptrdiff_t const size,
475  T const & value,
476  CALLBACKS && callBacks )
477 {
478  LVARRAY_ASSERT( ptr != nullptr || size == 0 );
480 
481  // Find the position of value.
482  std::ptrdiff_t const index = find( ptr, size, value );
483 
484  // If it's not in the array we can return.
485  if( index == size || ptr[index] != value )
486  {
487  return false;
488  }
489 
490  // Remove the value and call the callback.
491  arrayManipulation::erase( ptr, size, index );
492  callBacks.remove( index );
493  return true;
494 }
495 
511 template< typename T, typename ITER, typename CALLBACKS=CallBacks< T > >
512 LVARRAY_HOST_DEVICE inline
513 std::ptrdiff_t remove( T * const LVARRAY_RESTRICT ptr,
514  std::ptrdiff_t const size,
515  ITER const first,
516  ITER const last,
517  CALLBACKS && callBacks=CALLBACKS() )
518 {
519  LVARRAY_ASSERT( ptr != nullptr || size == 0 );
521  LVARRAY_ASSERT( isSortedUnique( ptr, ptr + size ) );
522 
523  LVARRAY_ASSERT( isSortedUnique( first, last ) );
524 
525  // Find the position of the first value to remove and the position it's at in the array.
526  ITER valueToRemove = first;
527  std::ptrdiff_t removalPosition = 0;
528  for(; valueToRemove != last; ++valueToRemove )
529  {
530  removalPosition += find( ptr + removalPosition, size - removalPosition, *valueToRemove );
531 
532  // If the current value is larger than the largest value in the array we can return.
533  if( removalPosition == size )
534  {
535  return 0;
536  }
537 
538  if( ptr[ removalPosition ] == *valueToRemove )
539  {
540  break;
541  }
542  }
543 
544  // If we didn't find a value to remove we can return.
545  if( valueToRemove == last )
546  {
547  return 0;
548  }
549 
550  // Loop over the values
551  std::ptrdiff_t nRemoved = 0;
552  for(; valueToRemove != last; )
553  {
554  // Find the next value to remove
555  ITER nextValueToRemove = valueToRemove;
556  ++nextValueToRemove;
557  std::ptrdiff_t nextRemovalPosition = removalPosition;
558  for(; nextValueToRemove != last; ++nextValueToRemove )
559  {
560  // Find the position
561  nextRemovalPosition += find( ptr + nextRemovalPosition, size - nextRemovalPosition, *nextValueToRemove );
562 
563  // If it's not in the array then neither are any of the rest of the values.
564  if( nextRemovalPosition == size )
565  {
566  nextValueToRemove = last;
567  break;
568  }
569 
570  // If it's in the array then we can exit this loop.
571  if( ptr[ nextRemovalPosition ] == *nextValueToRemove )
572  {
573  break;
574  }
575  }
576 
577  if( nextValueToRemove == last )
578  {
579  nextRemovalPosition = size;
580  }
581 
582  // Shift the values down and call the callback.
583  nRemoved += 1;
584  arrayManipulation::shiftDown( ptr, nextRemovalPosition, removalPosition + 1, nRemoved );
585  callBacks.remove( nRemoved, removalPosition, nextRemovalPosition );
586 
587  valueToRemove = nextValueToRemove;
588  removalPosition = nextRemovalPosition;
589 
590  // If we've reached the end of the array we can exit the loop.
591  if( removalPosition == size )
592  {
593  break;
594  }
595  }
596 
597  // Destroy the values moved out of at the end of the array.
598  for( std::ptrdiff_t i = size - nRemoved; i < size; ++i )
599  {
600  ptr[ i ].~T();
601  }
602 
603  return nRemoved;
604 }
605 
618 template< typename T, typename CALLBACKS=CallBacks< T > >
619 LVARRAY_HOST_DEVICE inline
620 bool insert( T * const LVARRAY_RESTRICT ptr,
621  std::ptrdiff_t const size,
622  T const & value,
623  CALLBACKS && callBacks=CALLBACKS() )
624 {
625  LVARRAY_ASSERT( ptr != nullptr || size == 0 );
627  LVARRAY_ASSERT( isSortedUnique( ptr, ptr + size ) );
628 
629  std::ptrdiff_t insertPos = std::ptrdiff_t( -1 );
630  if( size == 0 || value < ptr[0] ) // Take care of the case of an empty array or inserting at the beginning.
631  {
632  insertPos = 0;
633  }
634  else if( ptr[size - 1] < value ) // The case of inserting at the end.
635  {
636  insertPos = size;
637  }
638  else // otherwise do a binary search
639  {
640  // We've already checked the first and last values.
641  insertPos = find( ptr, size, value );
642  }
643 
644  // If it's in the array we call the incrementSize callback and return false.
645  if( insertPos != size && ptr[insertPos] == value )
646  {
647  callBacks.incrementSize( ptr, 0 );
648  return false;
649  }
650 
651  // Otherwise call the incrementSize callback, get the new pointer, insert and call the insert callback.
652  T * const newPtr = callBacks.incrementSize( ptr, 1 );
653  arrayManipulation::emplace( newPtr, size, insertPos, value );
654  callBacks.insert( insertPos );
655  return true;
656 }
657 
674 template< typename T, typename ITER, typename CALLBACKS=CallBacks< T > >
675 LVARRAY_HOST_DEVICE inline
676 std::ptrdiff_t insert( T * const LVARRAY_RESTRICT ptr,
677  std::ptrdiff_t const size,
678  ITER const first,
679  ITER const last,
680  CALLBACKS && callBacks=CALLBACKS() )
681 {
682  LVARRAY_ASSERT( ptr != nullptr || size == 0 );
684  LVARRAY_ASSERT( isSortedUnique( ptr, ptr + size ) );
685 
686  LVARRAY_ASSERT( isSortedUnique( first, last ) );
687 
688  // Special case for inserting into an empty array.
689  if( size == 0 )
690  {
691  std::ptrdiff_t numInserted = arrayManipulation::iterDistance( first, last );
692  T * const newPtr = callBacks.incrementSize( ptr, numInserted );
693 
694  numInserted = 0;
695  for( ITER iter = first; iter != last; ++iter )
696  {
697  new ( newPtr + numInserted ) T( *iter );
698  callBacks.set( numInserted, numInserted );
699  ++numInserted;
700  }
701 
702  return numInserted;
703  }
704 
705  // Count up the number of values that will actually be inserted.
706  std::ptrdiff_t nVals = 0;
707  std::ptrdiff_t nToInsert = 0;
708  std::ptrdiff_t upperBound = size;
709  ITER iter = last;
710  while( iter != first )
711  {
712  --iter;
713  ++nVals;
714  upperBound = find( ptr, upperBound, *iter );
715  if( upperBound == size || ptr[upperBound] != *iter )
716  {
717  ++nToInsert;
718  }
719  }
720 
721  // Call callBack with the number to insert and get a new pointer back.
722  T * const newPtr = callBacks.incrementSize( ptr, nToInsert );
723 
724  // If there are no values to insert we can return.
725  if( nToInsert == 0 )
726  {
727  return 0;
728  }
729 
730  // Insert the rest of the values.
731  std::ptrdiff_t currentIndex = nVals - 1;
732  std::ptrdiff_t nLeftToInsert = nToInsert;
733  upperBound = size;
734  iter = last;
735  while( iter != first )
736  {
737  --iter;
738  std::ptrdiff_t const pos = find( newPtr, upperBound, *iter );
739 
740  // If *iter is already in the array skip it.
741  if( pos != upperBound && newPtr[pos] == *iter )
742  {
743  --currentIndex;
744  continue;
745  }
746 
747  // Else shift the values up and insert.
748  arrayManipulation::shiftUp( newPtr, upperBound, pos, nLeftToInsert );
749  new ( newPtr + pos + nLeftToInsert - 1 ) T( *iter );
750  callBacks.insert( nLeftToInsert, currentIndex, pos, upperBound );
751 
752  --currentIndex;
753  --nLeftToInsert;
754  upperBound = pos;
755  }
756 
757  return nToInsert;
758 }
759 
760 } // namespace sortedArrayManipulation
761 } // namespace LvArray
#define LVARRAY_UNUSED_VARIABLE(X)
Mark X as an unused variable, used to silence compiler warnings.
Definition: Macros.hpp:51
#define LVARRAY_ASSERT(EXP)
Assert EXP is true with no message.
Definition: Macros.hpp:142
constexpr std::iterator_traits< ITER >::difference_type iterDistance(ITER first, ITER const last, std::input_iterator_tag)
constexpr bool isSorted(Description const desc)
bool isSortedUnique(ITER first, ITER const last, Compare &&comp=Compare())
void insert(std::ptrdiff_t const nLeftToInsert, std::ptrdiff_t const valuePos, std::ptrdiff_t const pos, std::ptrdiff_t const prevPos)
Callback signaling that the that the given value was inserted at the given position. Further information is provided in order to make the insertion efficient.
void shiftUp(T *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size, std::ptrdiff_t const index, std::ptrdiff_t const n)
Shift the values in the array at or above the given position up by the given amount. New uninitialized values take their place.
constexpr std::enable_if< std::is_signed< INDEX_TYPE >::value, bool >::type isPositive(INDEX_TYPE const i)
Contains helper functions for the sortedArrayManipulation routines. Much of this was adapted from stl...
This class provides a no-op callbacks interface for the ArrayManipulation sorted routines.
std::ptrdiff_t find(T const *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size, T const &value, Compare &&comp=Compare())
void emplace(T *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size, std::ptrdiff_t const index, ARGS &&... args)
Insert into the array constructing the new value in place.
std::ptrdiff_t makeSortedUnique(ITER const first, ITER const last, Compare &&comp=Compare())
Sort and remove duplicates from the array, duplicates aren&#39;t destroyed but they&#39;re moved out of...
void insert(std::ptrdiff_t const pos)
Callback signaling that a value was inserted at the given position.
Contains functions for manipulating a contiguous array of values.
bool contains(T const *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size, T const &value, Compare &&comp=Compare())
The top level namespace.
Definition: Array.hpp:24
Contains a bunch of macro definitions.
T * incrementSize(T *const curPtr, std::ptrdiff_t const nToAdd)
Callback signaling that the size of the array has increased.
constexpr bool isUnique(Description const desc)
This class operates as functor similar to std::less.
void erase(T *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size, std::ptrdiff_t const index, std::ptrdiff_t const n=1)
Shift the values in the array at or above the given position down by the given amount overwriting the...
void makeSorted(RandomAccessIterator const first, RandomAccessIterator const last, Compare &&comp=Compare())
Sort the given values in place using the given comparator.
This class operates as functor similar to std::greater.
void shiftDown(T *const LVARRAY_RESTRICT ptr, std::ptrdiff_t const size, std::ptrdiff_t const index, std::ptrdiff_t const n)
Shift the values in the array at or above the given position down by the given amount overwriting the...
#define DISABLE_HD_WARNING
Disable host device warnings.
Definition: Macros.hpp:401
void dualSort(RandomAccessIteratorA valueFirst, RandomAccessIteratorA valueLast, RandomAccessIteratorB dataFirst, Compare &&comp=Compare())
Sort the given values in place using the given comparator and perform the same operations on the data...
std::ptrdiff_t removeDuplicates(ITER first, ITER const last, Compare &&comp=Compare())
Remove duplicates from the array, duplicates aren&#39;t destroyed but they&#39;re moved out of...
Description
Describes an as some combination of sorted/unsorted and unique/with duplicates.
#define LVARRAY_HOST_DEVICE
Mark a function for both host and device usage.
Definition: Macros.hpp:389