File: pcg.cc

package info (click to toggle)
crawl 2%3A0.34.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 100,188 kB
  • sloc: cpp: 363,709; ansic: 27,765; javascript: 9,516; python: 8,463; perl: 3,293; java: 3,132; xml: 2,380; makefile: 1,835; sh: 611; objc: 250; cs: 15; sed: 9; lisp: 3
file content (143 lines) | stat: -rw-r--r-- 4,936 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* This file implements a 32-bit pseudo-random number generator with 64 bit
 * internal state: see http://www.pcg-random.org/.
 *
 * "PCG is a family of simple fast space-efficient statistically good
 * algorithms for random number generation. Unlike many general-purpose RNGs,
 * they are also hard to predict."
 *
 * TODO: should we eventually switch to just directly using the official c++
 * implementation?
 *
 * PcgRNG::PcgRNG() and get_uint32 are derived/modified from M.E. O'Neill's
 * minimal PCG implementation:
 *    https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c
 * Original source is (c) 2014 Melissa O'Neill <oneill@pcg-random.org>
 * Licensed under Apache License 2.0
 *
 * get_bounded_uint32 is derived/modified from an implementation by Melissa
 * O'Neill of an algorithm by Daniel Lemire as part of a comparison of bounded
 * random functions:
 *    http://www.pcg-random.org/posts/bounded-rands.html
 *    https://github.com/imneme/bounded-rands/blob/master/bounded32.cpp
 * Original source is Copyright (c) 2018 Melissa E. O'Neill
 * Licensed under The MIT License
 */

#include "AppHdr.h"

#include "pcg.h"

namespace rng
{
    /**
     * Generate a uniformly distributed 32-bit random number.
     */
    uint32_t PcgRNG::get_uint32()
    {
        count_++;
        uint64_t oldstate = state_;
        // Advance internal state. Use the 'official' multiplier. Don't change
        // this without carefully consulting official sources, as not all
        // multipliers are ok: see
        // http://www.pcg-random.org/posts/critiquing-pcg-streams.html
        state_ = oldstate * static_cast<uint64_t>(6364136223846793005ULL)
                                                                + (inc_|1);
        // Calculate output function (XSH RR), uses old state for max ILP
        uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
        uint32_t rot = oldstate >> 59u;
        return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
    }

    uint64_t
    PcgRNG::get_uint64()
    {
        return static_cast<uint64_t>(get_uint32()) << 32 | get_uint32();
    }

    /**
     * Generate a uniformly distributed number, r, where 0 <= r < range.
     * This uses a technique due to Daniel Lemire, with implementation and
     * additional tweaks from Melissa O'Neil. It's designed to avoid /,% for
     * small values of `range`.
     *
     * See:
     *  http://www.pcg-random.org/posts/bounded-rands.html
     *  https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
     *  https://arxiv.org/abs/1805.10941 or https://dl.acm.org/citation.cfm?id=3230636
     */
    uint32_t PcgRNG::get_bounded_uint32(uint32_t range)
    {
        uint32_t x = get_uint32();
        uint64_t m = uint64_t(x) * uint64_t(range);
        uint32_t l = uint32_t(m);
        if (l < range)
        {
            // TODO: will this generate warnings somewhere? the PCG c++
            // implementation has a different version of this step that may be
            // useful.
            uint32_t t = -range;

            if (t >= range)
            {
                t -= range;
                if (t >= range)
                    t %= range;
            }
            while (l < t)
            {
                x = get_uint32();
                m = uint64_t(x) * uint64_t(range);
                l = uint32_t(m);
            }
        }
        return m >> 32;
    }

    // Initialization values only.
    // don't generate unseeded versions of this. But if you must, go get the
    // constants from the official PCG implementation...
    PcgRNG::PcgRNG()
        : state_(static_cast<uint64_t>(0)),
          inc_(static_cast<uint64_t>(1)),
          count_(0)
    { }

    /**
     * Seed a PCG RNG instance based on a seed (`init_state`) and a sequence/
     * stream id. Both of these can be any value, but the highest bit of
     * `sequence` will be discarded.
     *
     * @param init_state the seed value to initialize PCG state with.
     * @param sequence the sequence number/stream id.
     */
    PcgRNG::PcgRNG(uint64_t init_state, uint64_t sequence)
        : PcgRNG()
    {
        inc_ = sequence << 1u | 1u; // any odd value is ok
        get_uint32();
        state_ += init_state;
        get_uint32();
        count_ = 0;
    }

    PcgRNG::PcgRNG(uint64_t init_state)
        : PcgRNG(init_state, static_cast<uint64_t>(2305843009213693951ULL))
                             // Largest Mersenne prime under 64-bits.
    { }

    PcgRNG::PcgRNG(const CrawlVector &v)
        : PcgRNG()
    {
        ASSERT(v.size() == 2);
        state_ = static_cast<uint64_t>(v[0].get_int64());
        inc_ = static_cast<uint64_t>(v[1].get_int64());
    }

    CrawlVector PcgRNG::to_vector()
    {
        CrawlVector store;
        store.push_back(static_cast<int64_t>(state_));
        store.push_back(static_cast<int64_t>(inc_));
        return store;
    }
}