1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 The PHP Group | |
6 | +----------------------------------------------------------------------+ |
7 | | This source file is subject to version 3.01 of the PHP license, | |
8 | | that is bundled with this package in the file LICENSE, and is | |
9 | | available through the world-wide-web at the following url: | |
10 | | http://www.php.net/license/3_01.txt | |
11 | | If you did not receive a copy of the PHP license and are unable to | |
12 | | obtain it through the world-wide-web, please send a note to | |
13 | | license@php.net so we can mail you a copy immediately. | |
14 | +----------------------------------------------------------------------+ |
15 | | Author: Sascha Schumann <sascha@schumann.cx> | |
16 | +----------------------------------------------------------------------+ |
17 | */ |
18 | |
19 | /* $Id$ */ |
20 | |
21 | #include "php.h" |
22 | #include "php_lcg.h" |
23 | |
24 | #if HAVE_UNISTD_H |
25 | #include <unistd.h> |
26 | #endif |
27 | |
28 | #ifdef PHP_WIN32 |
29 | #include "win32/time.h" |
30 | #else |
31 | #include <sys/time.h> |
32 | #endif |
33 | |
34 | #ifdef ZTS |
35 | int lcg_globals_id; |
36 | #else |
37 | static php_lcg_globals lcg_globals; |
38 | #endif |
39 | |
40 | #ifdef PHP_WIN32 |
41 | #include <process.h> |
42 | #endif |
43 | |
44 | /* |
45 | * combinedLCG() returns a pseudo random number in the range of (0, 1). |
46 | * The function combines two CGs with periods of |
47 | * 2^31 - 85 and 2^31 - 249. The period of this function |
48 | * is equal to the product of both primes. |
49 | */ |
50 | |
51 | #define MODMULT(a, b, c, m, s) q = s/a;s=b*(s-a*q)-c*q;if(s<0)s+=m |
52 | |
53 | static void lcg_seed(TSRMLS_D); |
54 | |
55 | PHPAPI double php_combined_lcg(TSRMLS_D) /* {{{ */ |
56 | { |
57 | php_int32 q; |
58 | php_int32 z; |
59 | |
60 | if (!LCG(seeded)) { |
61 | lcg_seed(TSRMLS_C); |
62 | } |
63 | |
64 | MODMULT(53668, 40014, 12211, 2147483563L, LCG(s1)); |
65 | MODMULT(52774, 40692, 3791, 2147483399L, LCG(s2)); |
66 | |
67 | z = LCG(s1) - LCG(s2); |
68 | if (z < 1) { |
69 | z += 2147483562; |
70 | } |
71 | |
72 | return z * 4.656613e-10; |
73 | } |
74 | /* }}} */ |
75 | |
76 | static void lcg_seed(TSRMLS_D) /* {{{ */ |
77 | { |
78 | struct timeval tv; |
79 | |
80 | if (gettimeofday(&tv, NULL) == 0) { |
81 | LCG(s1) = tv.tv_sec ^ (tv.tv_usec<<11); |
82 | } else { |
83 | LCG(s1) = 1; |
84 | } |
85 | #ifdef ZTS |
86 | LCG(s2) = (long) tsrm_thread_id(); |
87 | #else |
88 | LCG(s2) = (long) getpid(); |
89 | #endif |
90 | |
91 | /* Add entropy to s2 by calling gettimeofday() again */ |
92 | if (gettimeofday(&tv, NULL) == 0) { |
93 | LCG(s2) ^= (tv.tv_usec<<11); |
94 | } |
95 | |
96 | LCG(seeded) = 1; |
97 | } |
98 | /* }}} */ |
99 | |
100 | static void lcg_init_globals(php_lcg_globals *lcg_globals_p TSRMLS_DC) /* {{{ */ |
101 | { |
102 | LCG(seeded) = 0; |
103 | } |
104 | /* }}} */ |
105 | |
106 | PHP_MINIT_FUNCTION(lcg) /* {{{ */ |
107 | { |
108 | #ifdef ZTS |
109 | ts_allocate_id(&lcg_globals_id, sizeof(php_lcg_globals), (ts_allocate_ctor) lcg_init_globals, NULL); |
110 | #else |
111 | lcg_init_globals(&lcg_globals); |
112 | #endif |
113 | return SUCCESS; |
114 | } |
115 | /* }}} */ |
116 | |
117 | /* {{{ proto float lcg_value() |
118 | Returns a value from the combined linear congruential generator */ |
119 | PHP_FUNCTION(lcg_value) |
120 | { |
121 | RETURN_DOUBLE(php_combined_lcg(TSRMLS_C)); |
122 | } |
123 | /* }}} */ |
124 | |
125 | /* |
126 | * Local variables: |
127 | * tab-width: 4 |
128 | * c-basic-offset: 4 |
129 | * End: |
130 | * vim600: sw=4 ts=4 fdm=marker |
131 | * vim<600: sw=4 ts=4 |
132 | */ |
133 | |