Skip to content

Commit e856805

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 e856805

File tree

3 files changed

+200
-249
lines changed

3 files changed

+200
-249
lines changed

include/pcg/pcg_core.hpp

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,4 +227,154 @@ namespace pcg_extras {
227227

228228
#endif // PCG_USE_INLINE_ASM
229229

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

include/pcg/pcg_extras.hpp

Lines changed: 0 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -184,153 +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-
}
334187

335188
/* generate_one, produce a value of integral type using a SeedSeq
336189
* (optionally, we can have it produce more than one and pick which one

0 commit comments

Comments
 (0)