1*2abb3134SXin Li /*
2*2abb3134SXin Li Copyright 2014 Google Inc. All rights reserved.
3*2abb3134SXin Li
4*2abb3134SXin Li Licensed under the Apache License, Version 2.0 (the "License");
5*2abb3134SXin Li you may not use this file except in compliance with the License.
6*2abb3134SXin Li You may obtain a copy of the License at
7*2abb3134SXin Li
8*2abb3134SXin Li http://www.apache.org/licenses/LICENSE-2.0
9*2abb3134SXin Li
10*2abb3134SXin Li Unless required by applicable law or agreed to in writing, software
11*2abb3134SXin Li distributed under the License is distributed on an "AS IS" BASIS,
12*2abb3134SXin Li WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*2abb3134SXin Li See the License for the specific language governing permissions and
14*2abb3134SXin Li limitations under the License.
15*2abb3134SXin Li */
16*2abb3134SXin Li
17*2abb3134SXin Li /*
18*2abb3134SXin Li * _fastrand.c -- Python extension module to generate random bit vectors
19*2abb3134SXin Li * quickly.
20*2abb3134SXin Li *
21*2abb3134SXin Li * IMPORTANT: This module does not use crytographically strong randomness. It
22*2abb3134SXin Li * should be used ONLY be used to speed up the simulation. Don't use it in
23*2abb3134SXin Li * production.
24*2abb3134SXin Li *
25*2abb3134SXin Li * If an adversary can predict which random bits are flipped, then RAPPOR's
26*2abb3134SXin Li * privacy is compromised.
27*2abb3134SXin Li *
28*2abb3134SXin Li */
29*2abb3134SXin Li
30*2abb3134SXin Li #include <stdint.h> // uint64_t
31*2abb3134SXin Li #include <stdio.h> // printf
32*2abb3134SXin Li #include <stdlib.h> // srand
33*2abb3134SXin Li #include <time.h> // time
34*2abb3134SXin Li
35*2abb3134SXin Li #include <Python.h>
36*2abb3134SXin Li
randbits(float p1,int num_bits)37*2abb3134SXin Li uint64_t randbits(float p1, int num_bits) {
38*2abb3134SXin Li uint64_t result = 0;
39*2abb3134SXin Li // RAND_MAX is the maximum int returned by rand().
40*2abb3134SXin Li //
41*2abb3134SXin Li // When p1 == 1.0, we want to guarantee that all bits are 1. The threshold
42*2abb3134SXin Li // will be RAND_MAX + 1. In the rare case that rand() returns RAND_MAX, the
43*2abb3134SXin Li // "<" test succeeds, so we get 1.
44*2abb3134SXin Li //
45*2abb3134SXin Li // When p1 == 0.0, we want to guarantee that all bits are 0. The threshold
46*2abb3134SXin Li // will be 0. In the rare case that rand() returns 0, the "<" test fails, so
47*2abb3134SXin Li // we get 0.
48*2abb3134SXin Li
49*2abb3134SXin Li // NOTE: cast is necessary to do unsigned arithmetic rather than signed.
50*2abb3134SXin Li // RAND_MAX is an int so adding 1 won't overflow a uint64_t.
51*2abb3134SXin Li uint64_t max = (uint64_t)RAND_MAX + 1u;
52*2abb3134SXin Li uint64_t threshold = p1 * max;
53*2abb3134SXin Li int i;
54*2abb3134SXin Li for (i = 0; i < num_bits; ++i) {
55*2abb3134SXin Li // NOTE: The comparison is <= so that p1 = 1.0 implies that the bit is
56*2abb3134SXin Li // ALWAYS set. RAND_MAX is the maximum value returned by rand().
57*2abb3134SXin Li uint64_t bit = (rand() < threshold);
58*2abb3134SXin Li result |= (bit << i);
59*2abb3134SXin Li }
60*2abb3134SXin Li return result;
61*2abb3134SXin Li }
62*2abb3134SXin Li
63*2abb3134SXin Li static PyObject *
func_randbits(PyObject * self,PyObject * args)64*2abb3134SXin Li func_randbits(PyObject *self, PyObject *args) {
65*2abb3134SXin Li float p1;
66*2abb3134SXin Li int num_bits;
67*2abb3134SXin Li
68*2abb3134SXin Li if (!PyArg_ParseTuple(args, "fi", &p1, &num_bits)) {
69*2abb3134SXin Li return NULL;
70*2abb3134SXin Li }
71*2abb3134SXin Li if (p1 < 0.0 || p1 > 1.0) {
72*2abb3134SXin Li printf("p1 must be between 0.0 and 1.0\n");
73*2abb3134SXin Li // return None for now; easier than raising ValueError
74*2abb3134SXin Li Py_INCREF(Py_None);
75*2abb3134SXin Li return Py_None;
76*2abb3134SXin Li }
77*2abb3134SXin Li if (num_bits < 0 || num_bits > 64) {
78*2abb3134SXin Li printf("num_bits must be 64 or less\n");
79*2abb3134SXin Li // return None for now; easier than raising ValueError
80*2abb3134SXin Li Py_INCREF(Py_None);
81*2abb3134SXin Li return Py_None;
82*2abb3134SXin Li }
83*2abb3134SXin Li
84*2abb3134SXin Li //printf("p: %f\n", p);
85*2abb3134SXin Li uint64_t r = randbits(p1, num_bits);
86*2abb3134SXin Li return PyLong_FromUnsignedLongLong(r);
87*2abb3134SXin Li }
88*2abb3134SXin Li
89*2abb3134SXin Li PyMethodDef methods[] = {
90*2abb3134SXin Li {"randbits", func_randbits, METH_VARARGS,
91*2abb3134SXin Li "Return a number with N bits, where each bit is 1 with probability p."},
92*2abb3134SXin Li {NULL, NULL},
93*2abb3134SXin Li };
94*2abb3134SXin Li
init_fastrand(void)95*2abb3134SXin Li void init_fastrand(void) {
96*2abb3134SXin Li Py_InitModule("_fastrand", methods);
97*2abb3134SXin Li
98*2abb3134SXin Li // Just seed it here; we don't give the application any control.
99*2abb3134SXin Li int seed = time(NULL);
100*2abb3134SXin Li srand(seed);
101*2abb3134SXin Li }
102