xref: /netbsd-src/external/lgpl3/mpfr/dist/src/grandom.c (revision ba125506a622fe649968631a56eba5d42ff57863)
1 /* mpfr_grandom (rop1, rop2, state, rnd_mode) -- Generate up to two
2    pseudorandom real numbers according to a standard normal Gaussian
3    distribution and round it to the precision of rop1, rop2 according
4    to the given rounding mode.
5 
6 Copyright 2011-2023 Free Software Foundation, Inc.
7 Contributed by the AriC and Caramba projects, INRIA.
8 
9 This file is part of the GNU MPFR Library.
10 
11 The GNU MPFR Library is free software; you can redistribute it and/or modify
12 it under the terms of the GNU Lesser General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or (at your
14 option) any later version.
15 
16 The GNU MPFR Library is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
19 License for more details.
20 
21 You should have received a copy of the GNU Lesser General Public License
22 along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
23 https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
24 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
25 
26 
27 /* #define MPFR_NEED_LONGLONG_H */
28 #include "mpfr-impl.h"
29 
30 
31 int
mpfr_grandom(mpfr_ptr rop1,mpfr_ptr rop2,gmp_randstate_t rstate,mpfr_rnd_t rnd)32 mpfr_grandom (mpfr_ptr rop1, mpfr_ptr rop2, gmp_randstate_t rstate,
33               mpfr_rnd_t rnd)
34 {
35   int inex1, inex2, s1, s2;
36   mpz_t x, y, xp, yp, t, a, b, s;
37   mpfr_t sfr, l, r1, r2;
38   mpfr_prec_t tprec, tprec0;
39 
40   inex2 = inex1 = 0;
41 
42   if (rop2 == NULL) /* only one output requested. */
43     tprec0 = MPFR_PREC (rop1);
44   else
45     tprec0 = MAX (MPFR_PREC (rop1), MPFR_PREC (rop2));
46 
47   tprec0 += 11;
48 
49   /* We use "Marsaglia polar method" here (cf.
50      George Marsaglia, Normal (Gaussian) random variables for supercomputers
51      The Journal of Supercomputing, Volume 5, Number 1, 49–55
52      DOI: 10.1007/BF00155857).
53 
54      First we draw uniform x and y in [0,1] using mpz_urandomb (in
55      fixed precision), and scale them to [-1, 1].
56   */
57 
58   mpz_init (xp);
59   mpz_init (yp);
60   mpz_init (x);
61   mpz_init (y);
62   mpz_init (t);
63   mpz_init (s);
64   mpz_init (a);
65   mpz_init (b);
66   mpfr_init2 (sfr, MPFR_PREC_MIN);
67   mpfr_init2 (l, MPFR_PREC_MIN);
68   mpfr_init2 (r1, MPFR_PREC_MIN);
69   if (rop2 != NULL)
70     mpfr_init2 (r2, MPFR_PREC_MIN);
71 
72   mpz_set_ui (xp, 0);
73   mpz_set_ui (yp, 0);
74 
75   for (;;)
76     {
77       tprec = tprec0;
78       do
79         {
80           mpz_urandomb (xp, rstate, tprec);
81           mpz_urandomb (yp, rstate, tprec);
82           mpz_mul (a, xp, xp);
83           mpz_mul (b, yp, yp);
84           mpz_add (s, a, b);
85         }
86       while (mpz_sizeinbase (s, 2) > tprec * 2);
87 
88       /* now s = x^2 + y^2 < 2^{2tprec} */
89 
90       for (;;)
91         {
92           /* we compute (xp+1)^2 + (yp+1)^2 as s + 2xp + 2yp + 2 */
93           mpz_addmul_ui (s, xp, 2);
94           mpz_addmul_ui (s, yp, 2);
95           mpz_add_ui (s, s, 2);
96           /* The case s = 2^(2*tprec) is not possible:
97            (a) if xp and yp have different parities, s is odd
98            (b) if xp and yp are even, (xp+1)^2 and (yp+1)^2 are 1 mod 4,
99                thus s = 2 mod 4 (and tprec >= 1);
100            (c) if xp and yp are odd, if we note x = xp+1, y = yp+1 and
101                p = tprec, we would have x^2 + y^2 = 2^(2p) with x and y even
102                0 < x, y <= 2^p, thus if x' = x/2, y' = y/2 and p'=p-1,
103                we would have x'^2 + y'^2 = 2^(2p') with
104                0 < x', y' <= 2^p', and we conclude by induction. */
105           if (mpz_sizeinbase (s, 2) <= 2 * tprec)
106             goto yeepee;
107           /* Extend by 32 bits: for tprec=12, the probability we get here
108              is 8191/13180825, i.e., about 0.000621 */
109           mpz_mul_2exp (xp, xp, 32);
110           mpz_mul_2exp (yp, yp, 32);
111           mpz_urandomb (x, rstate, 32);
112           mpz_urandomb (y, rstate, 32);
113           mpz_add (xp, xp, x);
114           mpz_add (yp, yp, y);
115           tprec += 32;
116 
117           mpz_mul (a, xp, xp);
118           mpz_mul (b, yp, yp);
119           mpz_add (s, a, b);
120           if (mpz_sizeinbase (s, 2) > tprec * 2)
121             break;
122         }
123     }
124  yeepee:
125 
126   /* FIXME: compute s with s -= 2x + 2y + 2 */
127   mpz_mul (a, xp, xp);
128   mpz_mul (b, yp, yp);
129   mpz_add (s, a, b);
130   /* Compute the signs of the output */
131   mpz_urandomb (x, rstate, 2);
132   s1 = mpz_tstbit (x, 0);
133   s2 = mpz_tstbit (x, 1);
134   for (;;)
135     {
136       /* s = xp^2 + yp^2 (loop invariant) */
137       mpfr_set_prec (sfr, 2 * tprec);
138       mpfr_set_prec (l, tprec);
139       mpfr_set_z (sfr, s, MPFR_RNDN); /* exact */
140       mpfr_mul_2si (sfr, sfr, -2 * tprec, MPFR_RNDN); /* exact */
141       mpfr_log (l, sfr, MPFR_RNDN);
142       mpfr_neg (l, l, MPFR_RNDN);
143       mpfr_mul_2si (l, l, 1, MPFR_RNDN);
144       mpfr_div (l, l, sfr, MPFR_RNDN);
145       mpfr_sqrt (l, l, MPFR_RNDN);
146 
147       mpfr_set_prec (r1, tprec);
148       mpfr_mul_z (r1, l, xp, MPFR_RNDN);
149       mpfr_div_2ui (r1, r1, tprec, MPFR_RNDN); /* exact */
150       if (s1)
151         mpfr_neg (r1, r1, MPFR_RNDN);
152       if (MPFR_CAN_ROUND (r1, tprec - 2, MPFR_PREC (rop1), rnd))
153         {
154           if (rop2 != NULL)
155             {
156               mpfr_set_prec (r2, tprec);
157               mpfr_mul_z (r2, l, yp, MPFR_RNDN);
158               mpfr_div_2ui (r2, r2, tprec, MPFR_RNDN); /* exact */
159               if (s2)
160                 mpfr_neg (r2, r2, MPFR_RNDN);
161               if (MPFR_CAN_ROUND (r2, tprec - 2, MPFR_PREC (rop2), rnd))
162                 break;
163             }
164           else
165             break;
166         }
167       /* Extend by 32 bits */
168       mpz_mul_2exp (xp, xp, 32);
169       mpz_mul_2exp (yp, yp, 32);
170       mpz_urandomb (x, rstate, 32);
171       mpz_urandomb (y, rstate, 32);
172       mpz_add (xp, xp, x);
173       mpz_add (yp, yp, y);
174       tprec += 32;
175       mpz_mul (a, xp, xp);
176       mpz_mul (b, yp, yp);
177       mpz_add (s, a, b);
178     }
179   inex1 = mpfr_set (rop1, r1, rnd);
180   if (rop2 != NULL)
181     {
182       inex2 = mpfr_set (rop2, r2, rnd);
183       inex2 = mpfr_check_range (rop2, inex2, rnd);
184     }
185   inex1 = mpfr_check_range (rop1, inex1, rnd);
186 
187   if (rop2 != NULL)
188     mpfr_clear (r2);
189   mpfr_clear (r1);
190   mpfr_clear (l);
191   mpfr_clear (sfr);
192   mpz_clear (b);
193   mpz_clear (a);
194   mpz_clear (s);
195   mpz_clear (t);
196   mpz_clear (y);
197   mpz_clear (x);
198   mpz_clear (yp);
199   mpz_clear (xp);
200 
201   return INEX (inex1, inex2);
202 }
203