aboutsummaryrefslogtreecommitdiffstats
path: root/LibOVRKernel/Src/Kernel/OVR_Rand.h
blob: 14897c0c996de95159c14bba578cd7f647bf2866 (plain)
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/************************************************************************************

PublicHeader:   OVR_Kernel.h
Filename    :   OVR_Rand.h
Content     :   Random number generator
Created     :   Aug 28, 2014
Author      :   Chris Taylor

Copyright   :   Copyright 2014 Oculus VR, Inc. All Rights reserved.

Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 
you may not use the Oculus VR Rift SDK except in compliance with the License, 
which is provided at the time of installation or download, or which 
otherwise accompanies this software in either electronic or hard copy form.

You may obtain a copy of the License at

http://www.oculusvr.com/licenses/LICENSE-3.2 

Unless required by applicable law or agreed to in writing, the Oculus VR SDK 
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

************************************************************************************/

#ifndef OVR_Rand_h
#define OVR_Rand_h

#include "OVR_Types.h"
#include <math.h>

namespace OVR {


/*
 This is designed to generate up to 2^^32 numbers per seed.

 Its period is about 2^^126 and passes all BigCrush tests.
 It is the fastest simple generator that passes all tests.

 It has a few advantages over the stdlib RNG, including that
 all bits of the output are equally good in quality.
 Furthermore, the input seeds are hashed to avoid linear
 relationships between the input seeds and the low bits of
 the first few outputs.
*/
class RandomNumberGenerator
{
protected:
    uint64_t Rx;
    uint64_t Ry;
    double NextRandN;
    bool Seeded;
    bool HaveRandN;

public:
    RandomNumberGenerator() :
        Seeded(false),
        HaveRandN(false)
        // Other members left uninitialized
    {
    }

    bool IsSeeded() const
    {
        return Seeded;
    }

    // Seed with a random state
    void SeedRandom();

    // Start with a specific seed
    void Seed(uint32_t x, uint32_t y);

    // Returns an unsigned uint32_t uniformly distributed in interval [0..2^32-1]
    OVR_FORCE_INLINE uint32_t Next()
    {
        // If it is not Seeded yet,
        if (!IsSeeded())
        {
            SeedRandom();
        }

        // Sum of two long-period MWC generators
        Rx = (uint64_t)0xfffd21a7 * (uint32_t)Rx + (uint32_t)(Rx >> 32);
        Ry = (uint64_t)0xfffd1361 * (uint32_t)Ry + (uint32_t)(Ry >> 32);
        return (((uint32_t)Rx << 7) | ((uint32_t)Rx >> (32 - 7))) + (uint32_t)Ry; // ROL(x, 7) + y
    }

    // The following functions are inspired by the Matlab functions rand(), randi(), and randn()

    // Uniform distribution over open interval (0..2^32)
    // (i.e. closed interval [1..2^32-1], not including 0)
    OVR_FORCE_INLINE uint32_t NextNonZero()
    {
        uint32_t n;
        do
        {
            n = Next();
        } while (n == 0);
        return n;
    }

    // Double uniformly distributed over open interval (0..1)
    OVR_FORCE_INLINE double Rand()
    {
        return NextNonZero() * (1.0 / 4294967296.0);    // 2^32
    }

    // Double uniformly distributed over open interval (dmin..dmax)
    OVR_FORCE_INLINE double Rand(double dmin, double dmax)
    {
        return dmin + (dmax - dmin) * (1.0 / 4294967296.0) * NextNonZero(); // 2^32
    }

    // Integer uniformly distributed over closed interval [0..imax-1]
    // (NOTE: Matalb randi(imax) returns 1..imax)
    OVR_FORCE_INLINE int RandI(int imax)
    {
        return (int)(Next() % imax);
    }

    // Integer uniformly distributed over closed interval [imin..imax-1]
    // (NOTE: Matlab randi() returns imin..imax)
    OVR_FORCE_INLINE int RandI(int imin, int imax)
    {
        return imin + (int)(Next() % (imax - imin));
    }

    // Coordinate (x,y) uniformly distributed inside unit circle.
    // Returns magnitude squared of (x,y)
    OVR_FORCE_INLINE double RandInUnitCircle(double& x, double& y)
    {
        double r2;
        do
        {
            x = Rand(-1.0, 1.0);
            y = Rand(-1.0, 1.0);
            r2 = x*x + y*y;
        } while (r2 >= 1.0);
        return r2;
    }

    // Standard normal (gaussian) distribution: mean 0.0, stdev 1.0
    double RandN()
    {
        if (HaveRandN)
        {
            HaveRandN = false;
            return NextRandN;
        }
        else
        {
            double x, y;
            double r2 = RandInUnitCircle(x, y);
            // Box-Muller transform
            double f = sqrt(-2 * log(r2) / r2);

            // Return first, save second for next call
            NextRandN = y * f;
            HaveRandN = true;

            return x * f;
        }
    }

    // Uniform coordinate (c,s) ON unit circle.
    // This function computes cos(theta), sin(theta)
    // of rotation uniform in (0..2*pi).
    // [ c s] is a random 2D rotation matrix.
    // [-s c]
    // Reference: Numerical Recipes in C++, chap. 21
    OVR_FORCE_INLINE void RandOnUnitCircle(double& c, double& s)
    {
        double r2 = RandInUnitCircle(c, s);
        double normalize = 1.0 / sqrt(r2);
        c *= normalize;
        s *= normalize;
    }

    // Uniform coordinate (x,y,z,w) on surface of 4D hypersphere.
    // This is a quaternion rotation uniformly distributed across all rotations
    // Reference: Numerical Recipes in C++, chap. 21
    OVR_FORCE_INLINE void RandOnUnitSphere4(double& x, double& y, double& z, double& w)
    {
        double r2xy = RandInUnitCircle(x, y);

        double u, v;
        double r2uv = RandInUnitCircle(u, v);

        double f = sqrt((1.0 - r2xy) / r2uv);
        z = u * f;
        w = v * f;
    }
};

} // namespace OVR

#endif // OVR_Rand_h