[libcxx] r279948 - Implement C++17 std::sample.

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Sun Aug 28 15:14:38 PDT 2016


Author: ericwf
Date: Sun Aug 28 17:14:37 2016
New Revision: 279948

URL: http://llvm.org/viewvc/llvm-project?rev=279948&view=rev
Log:
Implement C++17 std::sample.

This patch implements the std::sample function added to C++17 from LFTS. It
also removes the std::experimental::sample implementation which now forwards
to std::sample.

Added:
    libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/
    libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.fail.cpp
    libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.pass.cpp
    libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.stable.pass.cpp
Modified:
    libcxx/trunk/include/algorithm
    libcxx/trunk/include/experimental/algorithm
    libcxx/trunk/test/std/experimental/algorithms/alg.random.sample/sample.fail.cpp
    libcxx/trunk/test/support/test_iterators.h

Modified: libcxx/trunk/include/algorithm
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/algorithm?rev=279948&r1=279947&r2=279948&view=diff
==============================================================================
--- libcxx/trunk/include/algorithm (original)
+++ libcxx/trunk/include/algorithm Sun Aug 28 17:14:37 2016
@@ -288,6 +288,12 @@ template <class RandomAccessIterator, cl
     random_shuffle(RandomAccessIterator first, RandomAccessIterator last,
                    RandomNumberGenerator& rand);  // deprecated in C++14
 
+template<class PopulationIterator, class SampleIterator,
+         class Distance, class UniformRandomBitGenerator>
+    SampleIterator sample(PopulationIterator first, PopulationIterator last,
+                          SampleIterator out, Distance n,
+                          UniformRandomBitGenerator&& g); // C++17
+
 template<class RandomAccessIterator, class UniformRandomNumberGenerator>
     void shuffle(RandomAccessIterator first, RandomAccessIterator last,
                  UniformRandomNumberGenerator&& g);
@@ -3142,6 +3148,78 @@ random_shuffle(_RandomAccessIterator __f
     }
 }
 
+template <class _PopulationIterator, class _SampleIterator, class _Distance,
+          class _UniformRandomNumberGenerator>
+_LIBCPP_INLINE_VISIBILITY
+_SampleIterator __sample(_PopulationIterator __first,
+                         _PopulationIterator __last, _SampleIterator __out,
+                         _Distance __n,
+                         _UniformRandomNumberGenerator & __g,
+                         input_iterator_tag) {
+
+  _Distance __k = 0;
+  for (; __first != __last && __k < __n; ++__first, (void)++__k)
+    __out[__k] = *__first;
+  _Distance __sz = __k;
+  for (; __first != __last; ++__first, (void)++__k) {
+    _Distance __r = _VSTD::uniform_int_distribution<_Distance>(0, __k)(__g);
+    if (__r < __sz)
+      __out[__r] = *__first;
+  }
+  return __out + _VSTD::min(__n, __k);
+}
+
+template <class _PopulationIterator, class _SampleIterator, class _Distance,
+          class _UniformRandomNumberGenerator>
+_LIBCPP_INLINE_VISIBILITY
+_SampleIterator __sample(_PopulationIterator __first,
+                         _PopulationIterator __last, _SampleIterator __out,
+                         _Distance __n,
+                         _UniformRandomNumberGenerator& __g,
+                         forward_iterator_tag) {
+  _Distance __unsampled_sz = _VSTD::distance(__first, __last);
+  for (__n = _VSTD::min(__n, __unsampled_sz); __n != 0; ++__first) {
+    _Distance __r =
+        _VSTD::uniform_int_distribution<_Distance>(0, --__unsampled_sz)(__g);
+    if (__r < __n) {
+      *__out++ = *__first;
+      --__n;
+    }
+  }
+  return __out;
+}
+
+template <class _PopulationIterator, class _SampleIterator, class _Distance,
+          class _UniformRandomNumberGenerator>
+_LIBCPP_INLINE_VISIBILITY
+_SampleIterator __sample(_PopulationIterator __first,
+                         _PopulationIterator __last, _SampleIterator __out,
+                         _Distance __n, _UniformRandomNumberGenerator& __g) {
+  typedef typename iterator_traits<_PopulationIterator>::iterator_category
+        _PopCategory;
+  typedef typename iterator_traits<_PopulationIterator>::difference_type
+        _Difference;
+  static_assert(__is_forward_iterator<_PopulationIterator>::value ||
+                __is_random_access_iterator<_SampleIterator>::value,
+                "SampleIterator must meet the requirements of RandomAccessIterator");
+  typedef typename common_type<_Distance, _Difference>::type _CommonType;
+  _LIBCPP_ASSERT(__n >= 0, "N must be a positive number.");
+  return _VSTD::__sample(
+      __first, __last, __out, _CommonType(__n),
+      __g, _PopCategory());
+}
+
+#if _LIBCPP_STD_VER > 14
+template <class _PopulationIterator, class _SampleIterator, class _Distance,
+          class _UniformRandomNumberGenerator>
+inline _LIBCPP_INLINE_VISIBILITY
+_SampleIterator sample(_PopulationIterator __first,
+                       _PopulationIterator __last, _SampleIterator __out,
+                       _Distance __n, _UniformRandomNumberGenerator&& __g) {
+    return _VSTD::__sample(__first, __last, __out, __n, __g);
+}
+#endif // _LIBCPP_STD_VER > 14
+
 template<class _RandomAccessIterator, class _UniformRandomNumberGenerator>
     void shuffle(_RandomAccessIterator __first, _RandomAccessIterator __last,
 #ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES

Modified: libcxx/trunk/include/experimental/algorithm
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/experimental/algorithm?rev=279948&r1=279947&r2=279948&view=diff
==============================================================================
--- libcxx/trunk/include/experimental/algorithm (original)
+++ libcxx/trunk/include/experimental/algorithm Sun Aug 28 17:14:37 2016
@@ -58,64 +58,11 @@ _ForwardIterator search(_ForwardIterator
 
 template <class _PopulationIterator, class _SampleIterator, class _Distance,
           class _UniformRandomNumberGenerator>
-_LIBCPP_INLINE_VISIBILITY
-_SampleIterator __sample(_PopulationIterator __first,
-                         _PopulationIterator __last, _SampleIterator __out,
-                         _Distance __n,
-                         _UniformRandomNumberGenerator &&__g,
-                         input_iterator_tag) {
-
-  _Distance __k = 0;
-  for (; __first != __last && __k < __n; ++__first, (void)++__k)
-    __out[__k] = *__first;
-  _Distance __sz = __k;
-  for (; __first != __last; ++__first, (void)++__k) {
-    _Distance __r = _VSTD::uniform_int_distribution<_Distance>(0, __k)(__g);
-    if (__r < __sz)
-      __out[__r] = *__first;
-  }
-  return __out + _VSTD::min(__n, __k);
-}
-
-template <class _PopulationIterator, class _SampleIterator, class _Distance,
-          class _UniformRandomNumberGenerator>
-_LIBCPP_INLINE_VISIBILITY
-_SampleIterator __sample(_PopulationIterator __first,
-                         _PopulationIterator __last, _SampleIterator __out,
-                         _Distance __n,
-                         _UniformRandomNumberGenerator &&__g,
-                         forward_iterator_tag) {
-  _Distance __unsampled_sz = _VSTD::distance(__first, __last);
-  for (__n = _VSTD::min(__n, __unsampled_sz); __n != 0; ++__first) {
-    _Distance __r =
-        _VSTD::uniform_int_distribution<_Distance>(0, --__unsampled_sz)(__g);
-    if (__r < __n) {
-      *__out++ = *__first;
-      --__n;
-    }
-  }
-  return __out;
-}
-
-template <class _PopulationIterator, class _SampleIterator, class _Distance,
-          class _UniformRandomNumberGenerator>
-_LIBCPP_INLINE_VISIBILITY
-_SampleIterator sample(_PopulationIterator __first,
-                         _PopulationIterator __last, _SampleIterator __out,
-                         _Distance __n, _UniformRandomNumberGenerator &&__g) {
-  typedef typename iterator_traits<_PopulationIterator>::iterator_category
-        _PopCategory;
-  typedef typename iterator_traits<_PopulationIterator>::difference_type
-        _Difference;
-  static_assert(__is_forward_iterator<_PopulationIterator>::value ||
-                __is_random_access_iterator<_SampleIterator>::value,
-                "SampleIterator must meet the requirements of RandomAccessIterator");
-  typedef typename common_type<_Distance, _Difference>::type _CommonType;
-  _LIBCPP_ASSERT(__n >= 0, "N must be a positive number.");
-  return _VSTD_LFTS::__sample(
-      __first, __last, __out, _CommonType(__n),
-      _VSTD::forward<_UniformRandomNumberGenerator>(__g),
-      _PopCategory());
+inline _LIBCPP_INLINE_VISIBILITY
+_SampleIterator sample(_PopulationIterator __first, _PopulationIterator __last,
+                       _SampleIterator __out, _Distance __n,
+                       _UniformRandomNumberGenerator &&__g) {
+  return _VSTD::__sample(__first, __last, __out, __n, __g);
 }
 
 _LIBCPP_END_NAMESPACE_LFTS

Added: libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.fail.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.fail.cpp?rev=279948&view=auto
==============================================================================
--- libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.fail.cpp (added)
+++ libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.fail.cpp Sun Aug 28 17:14:37 2016
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11, c++14
+
+// <algorithm>
+
+// template <class PopulationIterator, class SampleIterator, class Distance,
+//           class UniformRandomNumberGenerator>
+// SampleIterator sample(PopulationIterator first, PopulationIterator last,
+//                       SampleIterator out, Distance n,
+//                       UniformRandomNumberGenerator &&g);
+
+#include <algorithm>
+#include <random>
+#include <cassert>
+
+#include "test_iterators.h"
+
+template <class PopulationIterator, class SampleIterator> void test() {
+  int ia[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  const unsigned is = sizeof(ia) / sizeof(ia[0]);
+  const unsigned os = 4;
+  int oa[os];
+  std::minstd_rand g;
+  std::sample(PopulationIterator(ia), PopulationIterator(ia + is),
+                            SampleIterator(oa), os, g);
+}
+
+int main() {
+  // expected-error at algorithm:* {{static_assert failed "SampleIterator must meet the requirements of RandomAccessIterator"}}
+  // expected-error at algorithm:* 2 {{does not provide a subscript operator}}
+  // expected-error at algorithm:* {{invalid operands}}
+  test<input_iterator<int *>, output_iterator<int *> >();
+}

Added: libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.pass.cpp?rev=279948&view=auto
==============================================================================
--- libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.pass.cpp (added)
+++ libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.pass.cpp Sun Aug 28 17:14:37 2016
@@ -0,0 +1,148 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11, c++14
+
+// <algorithm>
+
+// template <class PopulationIterator, class SampleIterator, class Distance,
+//           class UniformRandomNumberGenerator>
+// SampleIterator sample(PopulationIterator first, PopulationIterator last,
+//                       SampleIterator out, Distance n,
+//                       UniformRandomNumberGenerator &&g);
+
+#include <algorithm>
+#include <random>
+#include <cassert>
+
+#include "test_iterators.h"
+
+struct ReservoirSampleExpectations {
+  enum { os = 4 };
+  static int oa1[os];
+  static int oa2[os];
+};
+
+int ReservoirSampleExpectations::oa1[] = {10, 5, 9, 4};
+int ReservoirSampleExpectations::oa2[] = {5, 2, 10, 4};
+
+struct SelectionSampleExpectations {
+  enum { os = 4 };
+  static int oa1[os];
+  static int oa2[os];
+};
+
+int SelectionSampleExpectations::oa1[] = {1, 4, 6, 7};
+int SelectionSampleExpectations::oa2[] = {1, 2, 6, 8};
+
+template <class IteratorCategory> struct TestExpectations
+    : public SelectionSampleExpectations {};
+
+template <>
+struct TestExpectations<std::input_iterator_tag>
+    : public ReservoirSampleExpectations {};
+
+template <template<class...> class PopulationIteratorType, class PopulationItem,
+          template<class...> class SampleIteratorType, class SampleItem>
+void test() {
+  typedef PopulationIteratorType<PopulationItem *> PopulationIterator;
+  typedef SampleIteratorType<SampleItem *> SampleIterator;
+  PopulationItem ia[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  const unsigned is = sizeof(ia) / sizeof(ia[0]);
+  typedef TestExpectations<typename std::iterator_traits<
+      PopulationIterator>::iterator_category> Expectations;
+  const unsigned os = Expectations::os;
+  SampleItem oa[os];
+  const int *oa1 = Expectations::oa1;
+  const int *oa2 = Expectations::oa2;
+  std::minstd_rand g;
+  SampleIterator end;
+  end = std::sample(PopulationIterator(ia),
+                                  PopulationIterator(ia + is),
+                                  SampleIterator(oa), os, g);
+  assert(end.base() - oa == std::min(os, is));
+  assert(std::equal(oa, oa + os, oa1));
+  end = std::sample(PopulationIterator(ia),
+                                  PopulationIterator(ia + is),
+                                  SampleIterator(oa), os, std::move(g));
+  assert(end.base() - oa == std::min(os, is));
+  assert(std::equal(oa, oa + os, oa2));
+}
+
+template <template<class...> class PopulationIteratorType, class PopulationItem,
+          template<class...> class SampleIteratorType, class SampleItem>
+void test_empty_population() {
+  typedef PopulationIteratorType<PopulationItem *> PopulationIterator;
+  typedef SampleIteratorType<SampleItem *> SampleIterator;
+  PopulationItem ia[] = {42};
+  const unsigned os = 4;
+  SampleItem oa[os];
+  std::minstd_rand g;
+  SampleIterator end =
+      std::sample(PopulationIterator(ia), PopulationIterator(ia),
+                                SampleIterator(oa), os, g);
+  assert(end.base() == oa);
+}
+
+template <template<class...> class PopulationIteratorType, class PopulationItem,
+          template<class...> class SampleIteratorType, class SampleItem>
+void test_empty_sample() {
+  typedef PopulationIteratorType<PopulationItem *> PopulationIterator;
+  typedef SampleIteratorType<SampleItem *> SampleIterator;
+  PopulationItem ia[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  const unsigned is = sizeof(ia) / sizeof(ia[0]);
+  SampleItem oa[1];
+  std::minstd_rand g;
+  SampleIterator end =
+      std::sample(PopulationIterator(ia), PopulationIterator(ia + is),
+                                SampleIterator(oa), 0, g);
+  assert(end.base() == oa);
+}
+
+template <template<class...> class PopulationIteratorType, class PopulationItem,
+          template<class...> class SampleIteratorType, class SampleItem>
+void test_small_population() {
+  // The population size is less than the sample size.
+  typedef PopulationIteratorType<PopulationItem *> PopulationIterator;
+  typedef SampleIteratorType<SampleItem *> SampleIterator;
+  PopulationItem ia[] = {1, 2, 3, 4, 5};
+  const unsigned is = sizeof(ia) / sizeof(ia[0]);
+  const unsigned os = 8;
+  SampleItem oa[os];
+  const SampleItem oa1[] = {1, 2, 3, 4, 5};
+  std::minstd_rand g;
+  SampleIterator end;
+  end = std::sample(PopulationIterator(ia),
+                                  PopulationIterator(ia + is),
+                                  SampleIterator(oa), os, g);
+  assert(end.base() - oa == std::min(os, is));
+  assert(std::equal(oa, end.base(), oa1));
+}
+
+int main() {
+  test<input_iterator, int, random_access_iterator, int>();
+  test<forward_iterator, int, output_iterator, int>();
+  test<forward_iterator, int, random_access_iterator, int>();
+
+  test<input_iterator, int, random_access_iterator, double>();
+  test<forward_iterator, int, output_iterator, double>();
+  test<forward_iterator, int, random_access_iterator, double>();
+
+  test_empty_population<input_iterator, int, random_access_iterator, int>();
+  test_empty_population<forward_iterator, int, output_iterator, int>();
+  test_empty_population<forward_iterator, int, random_access_iterator, int>();
+
+  test_empty_sample<input_iterator, int, random_access_iterator, int>();
+  test_empty_sample<forward_iterator, int, output_iterator, int>();
+  test_empty_sample<forward_iterator, int, random_access_iterator, int>();
+
+  test_small_population<input_iterator, int, random_access_iterator, int>();
+  test_small_population<forward_iterator, int, output_iterator, int>();
+  test_small_population<forward_iterator, int, random_access_iterator, int>();
+}

Added: libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.stable.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.stable.pass.cpp?rev=279948&view=auto
==============================================================================
--- libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.stable.pass.cpp (added)
+++ libcxx/trunk/test/std/algorithms/alg.modifying.operations/alg.random.sample/sample.stable.pass.cpp Sun Aug 28 17:14:37 2016
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++98, c++03, c++11, c++14
+
+// <algorithm>
+
+// template <class PopulationIterator, class SampleIterator, class Distance,
+//           class UniformRandomNumberGenerator>
+// SampleIterator sample(PopulationIterator first, PopulationIterator last,
+//                       SampleIterator out, Distance n,
+//                       UniformRandomNumberGenerator &&g);
+
+#include <algorithm>
+#include <random>
+#include <cassert>
+
+#include "test_iterators.h"
+
+// Stable if and only if PopulationIterator meets the requirements of a
+// ForwardIterator type.
+template <class PopulationIterator, class SampleIterator>
+void test_stability(bool expect_stable) {
+  const unsigned kPopulationSize = 100;
+  int ia[kPopulationSize];
+  for (unsigned i = 0; i < kPopulationSize; ++i)
+    ia[i] = i;
+  PopulationIterator first(ia);
+  PopulationIterator last(ia + kPopulationSize);
+
+  const unsigned kSampleSize = 20;
+  int oa[kPopulationSize];
+  SampleIterator out(oa);
+
+  std::minstd_rand g;
+
+  const int kIterations = 1000;
+  bool unstable = false;
+  for (int i = 0; i < kIterations; ++i) {
+    std::sample(first, last, out, kSampleSize, g);
+    unstable |= !std::is_sorted(oa, oa + kSampleSize);
+  }
+  assert(expect_stable == !unstable);
+}
+
+int main() {
+  test_stability<forward_iterator<int *>, output_iterator<int *> >(true);
+  test_stability<input_iterator<int *>, random_access_iterator<int *> >(false);
+}

Modified: libcxx/trunk/test/std/experimental/algorithms/alg.random.sample/sample.fail.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/experimental/algorithms/alg.random.sample/sample.fail.cpp?rev=279948&r1=279947&r2=279948&view=diff
==============================================================================
--- libcxx/trunk/test/std/experimental/algorithms/alg.random.sample/sample.fail.cpp (original)
+++ libcxx/trunk/test/std/experimental/algorithms/alg.random.sample/sample.fail.cpp Sun Aug 28 17:14:37 2016
@@ -7,6 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
+// UNSUPPORTED: c++98, c++03
+
 // <algorithm>
 
 // template <class PopulationIterator, class SampleIterator, class Distance,
@@ -32,8 +34,8 @@ template <class PopulationIterator, clas
 }
 
 int main() {
-  // expected-error at experimental/algorithm:* {{static_assert failed "SampleIterator must meet the requirements of RandomAccessIterator"}}
-  // expected-error at experimental/algorithm:* 2 {{does not provide a subscript operator}}
-  // expected-error at experimental/algorithm:* {{invalid operands}}
+  // expected-error at algorithm:* {{static_assert failed "SampleIterator must meet the requirements of RandomAccessIterator"}}
+  // expected-error at algorithm:* 2 {{does not provide a subscript operator}}
+  // expected-error at algorithm:* {{invalid operands}}
   test<input_iterator<int *>, output_iterator<int *> >();
 }

Modified: libcxx/trunk/test/support/test_iterators.h
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/support/test_iterators.h?rev=279948&r1=279947&r2=279948&view=diff
==============================================================================
--- libcxx/trunk/test/support/test_iterators.h (original)
+++ libcxx/trunk/test/support/test_iterators.h Sun Aug 28 17:14:37 2016
@@ -57,7 +57,7 @@ template <class It,
     class ItTraits = It>
 class input_iterator
 {
-    using Traits = std::iterator_traits<ItTraits>;
+    typedef std::iterator_traits<ItTraits> Traits;
     It it_;
 
     template <class U, class T> friend class input_iterator;




More information about the cfe-commits mailing list