Skip to content

Commit 613c077

Browse files
committed
pcg_random.hpp: simplified implementation of operator>>, operator<< and operator==. Moved generate to pcg_core.hpp in an attempt to fix compiler errors.
1 parent 3d00d90 commit 613c077

3 files changed

Lines changed: 216 additions & 263 deletions

File tree

include/pcg/pcg_core.hpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ namespace pcg_extras {
8585
#define PCG_EMULATED_128BIT_MATH 1
8686
#endif
8787

88+
#include <type_traits>
89+
#include <iterator>
8890

8991
namespace pcg_extras {
9092

@@ -227,4 +229,167 @@ namespace pcg_extras {
227229

228230
#endif // PCG_USE_INLINE_ASM
229231

232+
233+
234+
/*
235+
* The C++ SeedSeq concept (modelled by seed_seq) can fill an array of
236+
* 32-bit integers with seed data, but sometimes we want to produce
237+
* larger or smaller integers.
238+
*
239+
* The following code handles this annoyance.
240+
*
241+
* uneven_copy will copy an array of 32-bit ints to an array of larger or
242+
* smaller ints (actually, the code is general it only needing forward
243+
* iterators). The copy is identical to the one that would be performed if
244+
* we just did memcpy on a standard little-endian machine, but works
245+
* regardless of the endian of the machine (or the weirdness of the ints
246+
* involved).
247+
*
248+
* generate_to initializes an array of integers using a SeedSeq
249+
* object. It is given the size as a static constant at compile time and
250+
* tries to avoid memory allocation. If we're filling in 32-bit constants
251+
* we just do it directly. If we need a separate buffer and it's small,
252+
* we allocate it on the stack. Otherwise, we fall back to heap allocation.
253+
* Ugh.
254+
*
255+
* generate_one produces a single value of some integral type using a
256+
* SeedSeq object.
257+
*/
258+
259+
/* uneven_copy helper, case where destination ints are less than 32 bit. */
260+
261+
template<class SrcIter, class DestIter>
262+
SrcIter uneven_copy_impl(
263+
SrcIter src_first, DestIter dest_first, DestIter dest_last,
264+
std::true_type)
265+
{
266+
using src_t = typename std::iterator_traits<SrcIter>::value_type;
267+
using dest_t = typename std::iterator_traits<DestIter>::value_type;
268+
269+
constexpr bitcount_t SRC_SIZE = sizeof(src_t);
270+
constexpr bitcount_t DEST_SIZE = sizeof(dest_t);
271+
constexpr bitcount_t DEST_BITS = DEST_SIZE * 8;
272+
constexpr bitcount_t SCALE = SRC_SIZE / DEST_SIZE;
273+
274+
size_t count = 0;
275+
src_t value = 0;
276+
277+
while (dest_first != dest_last) {
278+
if ((count++ % SCALE) == 0)
279+
value = *src_first++; // Get more bits
280+
else
281+
value >>= DEST_BITS; // Move down bits
282+
283+
*dest_first++ = dest_t(value); // Truncates, ignores high bits.
284+
}
285+
return src_first;
286+
}
287+
288+
/* uneven_copy helper, case where destination ints are more than 32 bit. */
289+
290+
template<class SrcIter, class DestIter>
291+
SrcIter uneven_copy_impl(
292+
SrcIter src_first, DestIter dest_first, DestIter dest_last,
293+
std::false_type)
294+
{
295+
using src_t = typename std::iterator_traits<SrcIter>::value_type;
296+
using dest_t = typename std::iterator_traits<DestIter>::value_type;
297+
298+
constexpr auto SRC_SIZE = sizeof(src_t);
299+
constexpr auto SRC_BITS = SRC_SIZE * 8;
300+
constexpr auto DEST_SIZE = sizeof(dest_t);
301+
constexpr auto SCALE = (DEST_SIZE+SRC_SIZE-1) / SRC_SIZE;
302+
303+
while (dest_first != dest_last) {
304+
dest_t value(0UL);
305+
unsigned int shift = 0;
306+
307+
for (size_t i = 0; i < SCALE; ++i) {
308+
value |= dest_t(*src_first++) << shift;
309+
shift += SRC_BITS;
310+
}
311+
312+
*dest_first++ = value;
313+
}
314+
return src_first;
315+
}
316+
317+
/* uneven_copy, call the right code for larger vs. smaller */
318+
319+
template<class SrcIter, class DestIter>
320+
inline SrcIter uneven_copy(SrcIter src_first,
321+
DestIter dest_first, DestIter dest_last)
322+
{
323+
using src_t = typename std::iterator_traits<SrcIter>::value_type;
324+
using dest_t = typename std::iterator_traits<DestIter>::value_type;
325+
326+
constexpr bool DEST_IS_SMALLER = sizeof(dest_t) < sizeof(src_t);
327+
328+
return uneven_copy_impl(src_first, dest_first, dest_last,
329+
std::integral_constant<bool, DEST_IS_SMALLER>{});
330+
}
331+
332+
/* generate_to, fill in a fixed-size array of integral type using a SeedSeq
333+
* (actually works for any random-access iterator)
334+
*/
335+
336+
template <size_t size, typename SeedSeq, typename DestIter>
337+
inline void generate_to_impl(SeedSeq&& generator, DestIter dest,
338+
std::true_type)
339+
{
340+
generator.generate(dest, dest+size);
341+
}
342+
343+
template <size_t size, typename SeedSeq, typename DestIter>
344+
void generate_to_impl(SeedSeq&& generator, DestIter dest,
345+
std::false_type)
346+
{
347+
using dest_t = typename std::iterator_traits<DestIter>::value_type;
348+
constexpr auto DEST_SIZE = sizeof(dest_t);
349+
constexpr auto GEN_SIZE = sizeof(uint32_t);
350+
351+
constexpr bool GEN_IS_SMALLER = GEN_SIZE < DEST_SIZE;
352+
constexpr size_t FROM_ELEMS =
353+
GEN_IS_SMALLER
354+
? size * ((DEST_SIZE+GEN_SIZE-1) / GEN_SIZE)
355+
: (size + (GEN_SIZE / DEST_SIZE) - 1)
356+
/ ((GEN_SIZE / DEST_SIZE) + GEN_IS_SMALLER);
357+
// this odd code ^^^^^^^^^^^^^^^^^ is work-around for
358+
// a bug: http://llvm.org/bugs/show_bug.cgi?id=21287
359+
360+
if constexpr (FROM_ELEMS <= 1024) {
361+
uint32_t buffer[FROM_ELEMS];
362+
generator.generate(buffer, buffer+FROM_ELEMS);
363+
uneven_copy(buffer, dest, dest+size);
364+
} else {
365+
uint32_t* buffer = static_cast<uint32_t*>(malloc(GEN_SIZE * FROM_ELEMS));
366+
generator.generate(buffer, buffer+FROM_ELEMS);
367+
uneven_copy(buffer, dest, dest+size);
368+
free(static_cast<void*>(buffer));
369+
}
370+
}
371+
372+
template <size_t size, typename SeedSeq, typename DestIter>
373+
inline void generate_to(SeedSeq&& generator, DestIter dest)
374+
{
375+
using dest_t = typename std::iterator_traits<DestIter>::value_type;
376+
constexpr bool IS_32BIT = sizeof(dest_t) == sizeof(uint32_t);
377+
378+
generate_to_impl<size>(std::forward<SeedSeq>(generator), dest,
379+
std::integral_constant<bool, IS_32BIT>{});
380+
}
381+
382+
/* generate_one, produce a value of integral type using a SeedSeq
383+
* (optionally, we can have it produce more than one and pick which one
384+
* we want)
385+
*/
386+
387+
template <typename UInt, size_t i = 0UL, size_t N = i + 1UL, typename SeedSeq>
388+
inline UInt generate_one(SeedSeq&& generator)
389+
{
390+
UInt result[N];
391+
generate_to<N>(std::forward<SeedSeq>(generator), result);
392+
return result[i];
393+
}
394+
230395
}

include/pcg/pcg_extras.hpp

Lines changed: 0 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -184,166 +184,6 @@ inline std::istream& operator>>(std::istream& in, uint8_t& value)
184184
return pcg_extras::operator>> <char>(in, value);
185185
}
186186

187-
/*
188-
* The C++ SeedSeq concept (modelled by seed_seq) can fill an array of
189-
* 32-bit integers with seed data, but sometimes we want to produce
190-
* larger or smaller integers.
191-
*
192-
* The following code handles this annoyance.
193-
*
194-
* uneven_copy will copy an array of 32-bit ints to an array of larger or
195-
* smaller ints (actually, the code is general it only needing forward
196-
* iterators). The copy is identical to the one that would be performed if
197-
* we just did memcpy on a standard little-endian machine, but works
198-
* regardless of the endian of the machine (or the weirdness of the ints
199-
* involved).
200-
*
201-
* generate_to initializes an array of integers using a SeedSeq
202-
* object. It is given the size as a static constant at compile time and
203-
* tries to avoid memory allocation. If we're filling in 32-bit constants
204-
* we just do it directly. If we need a separate buffer and it's small,
205-
* we allocate it on the stack. Otherwise, we fall back to heap allocation.
206-
* Ugh.
207-
*
208-
* generate_one produces a single value of some integral type using a
209-
* SeedSeq object.
210-
*/
211-
212-
/* uneven_copy helper, case where destination ints are less than 32 bit. */
213-
214-
template<class SrcIter, class DestIter>
215-
SrcIter uneven_copy_impl(
216-
SrcIter src_first, DestIter dest_first, DestIter dest_last,
217-
std::true_type)
218-
{
219-
using src_t = typename std::iterator_traits<SrcIter>::value_type;
220-
using dest_t = typename std::iterator_traits<DestIter>::value_type;
221-
222-
constexpr bitcount_t SRC_SIZE = sizeof(src_t);
223-
constexpr bitcount_t DEST_SIZE = sizeof(dest_t);
224-
constexpr bitcount_t DEST_BITS = DEST_SIZE * 8;
225-
constexpr bitcount_t SCALE = SRC_SIZE / DEST_SIZE;
226-
227-
size_t count = 0;
228-
src_t value = 0;
229-
230-
while (dest_first != dest_last) {
231-
if ((count++ % SCALE) == 0)
232-
value = *src_first++; // Get more bits
233-
else
234-
value >>= DEST_BITS; // Move down bits
235-
236-
*dest_first++ = dest_t(value); // Truncates, ignores high bits.
237-
}
238-
return src_first;
239-
}
240-
241-
/* uneven_copy helper, case where destination ints are more than 32 bit. */
242-
243-
template<class SrcIter, class DestIter>
244-
SrcIter uneven_copy_impl(
245-
SrcIter src_first, DestIter dest_first, DestIter dest_last,
246-
std::false_type)
247-
{
248-
using src_t = typename std::iterator_traits<SrcIter>::value_type;
249-
using dest_t = typename std::iterator_traits<DestIter>::value_type;
250-
251-
constexpr auto SRC_SIZE = sizeof(src_t);
252-
constexpr auto SRC_BITS = SRC_SIZE * 8;
253-
constexpr auto DEST_SIZE = sizeof(dest_t);
254-
constexpr auto SCALE = (DEST_SIZE+SRC_SIZE-1) / SRC_SIZE;
255-
256-
while (dest_first != dest_last) {
257-
dest_t value(0UL);
258-
unsigned int shift = 0;
259-
260-
for (size_t i = 0; i < SCALE; ++i) {
261-
value |= dest_t(*src_first++) << shift;
262-
shift += SRC_BITS;
263-
}
264-
265-
*dest_first++ = value;
266-
}
267-
return src_first;
268-
}
269-
270-
/* uneven_copy, call the right code for larger vs. smaller */
271-
272-
template<class SrcIter, class DestIter>
273-
inline SrcIter uneven_copy(SrcIter src_first,
274-
DestIter dest_first, DestIter dest_last)
275-
{
276-
using src_t = typename std::iterator_traits<SrcIter>::value_type;
277-
using dest_t = typename std::iterator_traits<DestIter>::value_type;
278-
279-
constexpr bool DEST_IS_SMALLER = sizeof(dest_t) < sizeof(src_t);
280-
281-
return uneven_copy_impl(src_first, dest_first, dest_last,
282-
std::integral_constant<bool, DEST_IS_SMALLER>{});
283-
}
284-
285-
/* generate_to, fill in a fixed-size array of integral type using a SeedSeq
286-
* (actually works for any random-access iterator)
287-
*/
288-
289-
template <size_t size, typename SeedSeq, typename DestIter>
290-
inline void generate_to_impl(SeedSeq&& generator, DestIter dest,
291-
std::true_type)
292-
{
293-
generator.generate(dest, dest+size);
294-
}
295-
296-
template <size_t size, typename SeedSeq, typename DestIter>
297-
void generate_to_impl(SeedSeq&& generator, DestIter dest,
298-
std::false_type)
299-
{
300-
using dest_t = typename std::iterator_traits<DestIter>::value_type;
301-
constexpr auto DEST_SIZE = sizeof(dest_t);
302-
constexpr auto GEN_SIZE = sizeof(uint32_t);
303-
304-
constexpr bool GEN_IS_SMALLER = GEN_SIZE < DEST_SIZE;
305-
constexpr size_t FROM_ELEMS =
306-
GEN_IS_SMALLER
307-
? size * ((DEST_SIZE+GEN_SIZE-1) / GEN_SIZE)
308-
: (size + (GEN_SIZE / DEST_SIZE) - 1)
309-
/ ((GEN_SIZE / DEST_SIZE) + GEN_IS_SMALLER);
310-
// this odd code ^^^^^^^^^^^^^^^^^ is work-around for
311-
// a bug: http://llvm.org/bugs/show_bug.cgi?id=21287
312-
313-
if constexpr (FROM_ELEMS <= 1024) {
314-
uint32_t buffer[FROM_ELEMS];
315-
generator.generate(buffer, buffer+FROM_ELEMS);
316-
uneven_copy(buffer, dest, dest+size);
317-
} else {
318-
uint32_t* buffer = static_cast<uint32_t*>(malloc(GEN_SIZE * FROM_ELEMS));
319-
generator.generate(buffer, buffer+FROM_ELEMS);
320-
uneven_copy(buffer, dest, dest+size);
321-
free(static_cast<void*>(buffer));
322-
}
323-
}
324-
325-
template <size_t size, typename SeedSeq, typename DestIter>
326-
inline void generate_to(SeedSeq&& generator, DestIter dest)
327-
{
328-
using dest_t = typename std::iterator_traits<DestIter>::value_type;
329-
constexpr bool IS_32BIT = sizeof(dest_t) == sizeof(uint32_t);
330-
331-
generate_to_impl<size>(std::forward<SeedSeq>(generator), dest,
332-
std::integral_constant<bool, IS_32BIT>{});
333-
}
334-
335-
/* generate_one, produce a value of integral type using a SeedSeq
336-
* (optionally, we can have it produce more than one and pick which one
337-
* we want)
338-
*/
339-
340-
template <typename UInt, size_t i = 0UL, size_t N = i+1UL, typename SeedSeq>
341-
inline UInt generate_one(SeedSeq&& generator)
342-
{
343-
UInt result[N];
344-
generate_to<N>(std::forward<SeedSeq>(generator), result);
345-
return result[i];
346-
}
347187

348188
template <typename RngType>
349189
auto bounded_rand(RngType& rng, typename RngType::result_type upper_bound)

0 commit comments

Comments
 (0)