1 /*
2 *========================================================================
3 * $Id: libdieharder.h 176 2006-07-11 21:18:27Z rgb $
4 *
5 * file_input (GSL compatible).
6 *
7 * By Daniel Summerhays
8 * Mar. 10, 2005
9 *
10 * Heavily modifed by rgb, June-July 2006 (and beyond)
11 *
12 * See copyright in copyright.h and the accompanying file COPYING
13 *========================================================================
14 */
15
16 #include <dieharder/libdieharder.h>
17
18 /*
19 * This is a wrapper for getting random numbers from a file. Note
20 * CAREFULLY how we must proceed to access the state variables inside of a
21 * given rng.
22 */
23
24 static unsigned long int file_input_get (void *vstate);
25 static double file_input_get_double (void *vstate);
26 static void file_input_set (void *vstate, unsigned long int s);
27
28 /*
29 * This typedef struct file_input_state_t struct contains the data
30 * maintained on the operation of the file_input rng, and can be accessed
31 * via rng->state->whatever
32 *
33 * fp is the file pointer
34 * flen is the number of rands in the file (state->flen)
35 * rptr is a count of rands returned since last rewind
36 * rtot is a count of rands returned since the file was opened or it
37 * was deliberately reset.
38 * rewind_cnt is a count of how many times the file was rewound since
39 * its last open.
40 *
41 * file_input_state_t is defined in libdieharder.h currently and shared with
42 * file_input_raw.c
43 *
44 * In this way the routines below should work for BOTH file_input AND
45 * file_input_raw (as would rng->state->rtot, e.g., from the calling
46 * routine :-).
47 */
48
file_input_get_rewind_cnt(gsl_rng * rng)49 uint file_input_get_rewind_cnt(gsl_rng *rng)
50 {
51 file_input_state_t *state = (file_input_state_t *) rng->state;
52 return state->rewind_cnt;
53 }
54
55 off_t
file_input_get_rtot(gsl_rng * rng)56 file_input_get_rtot(gsl_rng *rng)
57 {
58 file_input_state_t *state = (file_input_state_t *) rng->state;
59 return state->rtot;
60 }
61
62 void
file_input_set_rtot(gsl_rng * rng,uint value)63 file_input_set_rtot(gsl_rng *rng,uint value)
64 {
65 file_input_state_t *state = (file_input_state_t *) rng;
66 state->rtot = 0;
67 }
68
file_input_get(void * vstate)69 static unsigned long int file_input_get (void *vstate)
70 {
71
72 file_input_state_t *state = (file_input_state_t *) vstate;
73 unsigned int iret;
74 double f;
75 char inbuf[K]; /* input buffer */
76
77 /*
78 * Check that the file is open (via file_input_set()).
79 */
80 if(state->fp != NULL) {
81
82 /*
83 * Read in the next random number from the file
84 */
85 if(fgets(inbuf,K,state->fp) == 0){
86 fprintf(stderr,"# file_input(): Error: EOF on %s\n",filename);
87 exit(0);
88 }
89 /*
90 * Got one (as we pretty much have to unless the file is badly
91 * broken). Convert the STRING input above into a uint according to
92 * the "type" (basically matching scanf type).
93 */
94 switch(filetype){
95 /*
96 * 32 bit unsigned int by assumption
97 */
98 case 'd':
99 case 'i':
100 case 'u':
101 if(0 == sscanf(inbuf,"%u",&iret)){
102 fprintf(stderr,"Error: converting %s failed. Exiting.\n", inbuf);
103 exit(0);
104 }
105 break;
106 /*
107 * double precision floats get converted to 32 bit uint
108 */
109 case 'e':
110 case 'E':
111 case 'f':
112 case 'F':
113 case 'g':
114 if(0 == sscanf(inbuf,"%lg",&f)){
115 fprintf(stderr,"Error: converting %s failed. Exiting.\n", inbuf);
116 exit(0);
117 }
118 iret = (uint) f*UINT_MAX;
119 break;
120 /*
121 * OK, so octal is really pretty silly, but we got it. Still uint.
122 */
123 case 'o':
124 if(0 == sscanf(inbuf,"%o",&iret)){
125 fprintf(stderr,"Error: converting %s failed. Exiting.\n", inbuf);
126 exit(0);
127 }
128 break;
129 /*
130 * hexadecimal is silly too, but we got it. uint, of course.
131 */
132 case 'x':
133 if(0 == sscanf(inbuf,"%x",&iret)){
134 fprintf(stderr,"Error: converting %s failed. Exiting.\n", inbuf);
135 exit(0);
136 }
137 break;
138 case 'X':
139 if(0 == sscanf(inbuf,"%X",&iret)){
140 fprintf(stderr,"Error: converting %s failed. Exiting.\n", inbuf);
141 exit(0);
142 }
143 break;
144 /*
145 * binary is NOT so silly. Let's do it. The hard way. A typical
146 * entry should look like:
147 * 01110101001010100100111101101110
148 */
149 case 'b':
150 iret = bit2uint(inbuf,filenumbits);
151 break;
152 default:
153 fprintf(stderr,"# file_input(): Error. File type %c is not recognized.\n",filetype);
154 exit(0);
155 break;
156 }
157
158 /*
159 * Success. iret is presumably valid and ready to return. Increment the
160 * counter of rands read so far.
161 */
162 state->rptr++;
163 state->rtot++;
164 if(verbose){
165 fprintf(stdout,"# file_input() %lu: %lu/%lu -> %u\n", state->rtot, state->rptr,state->flen,(uint)iret);
166 }
167
168 /*
169 * This (with seed s == 0) basically rewinds the file and resets
170 * state->rptr to 0, but rtot keeps running,
171 */
172 if(state->rptr == state->flen) {
173 /*
174 * Reset/rewind the file
175 */
176 file_input_set(vstate, 0);
177 }
178 return iret;
179 } else {
180 fprintf(stderr,"Error: %s not open. Exiting.\n", filename);
181 exit(0);
182 }
183
184 }
185
file_input_get_double(void * vstate)186 static double file_input_get_double (void *vstate)
187 {
188 return file_input_get (vstate) / (double) UINT_MAX;
189 }
190
191
192 /*
193 * file_input_set() is not yet terriby robust. For example, it
194 * cannot cope with missing header info, duplicate header info,
195 * impossible header info. It should work, though, for a well-formed
196 * header
197 */
198
file_input_set(void * vstate,unsigned long int s)199 static void file_input_set (void *vstate, unsigned long int s)
200 {
201
202 int cnt,numfields;
203 char inbuf[K]; /* input buffer */
204
205 file_input_state_t *state = (file_input_state_t *) vstate;
206
207 if(verbose == D_FILE_INPUT || verbose == D_ALL){
208 fprintf(stdout,"# file_input(): entering file_input_set\n");
209 fprintf(stdout,"# file_input(): state->fp = %p, seed = %lu\n",(void*) state->fp,s);
210 }
211
212 /*
213 * We use the "seed" to determine whether or not to reopen or
214 * rewind. A seed s == 0 for an open file means rewind; a seed
215 * of anything else forces a close (resetting rewind_cnt) followed
216 * by a reopen.
217 */
218 if(state->fp && s ) {
219 if(verbose == D_FILE_INPUT || verbose == D_ALL){
220 fprintf(stdout,"# file_input(): Closing/reopening/resetting %s\n",filename);
221 }
222 /* fclose(state->fp); */
223 state->fp = NULL;
224 }
225
226 if (state->fp == NULL){
227 if(verbose == D_FILE_INPUT || verbose == D_ALL){
228 fprintf(stdout,"# file_input(): Opening %s\n", filename);
229 }
230
231 /*
232 * If we get here, the file exists, is a regular file, and we know its
233 * length. We can now open it. The test catches all other conditions
234 * that might keep the file from reading, e.g. permissions.
235 */
236 if ((state->fp = fopen(filename,"r")) == NULL) {
237 fprintf(stderr,"# file_input(): Error: Cannot open %s, exiting.\n", filename);
238 exit(0);
239 }
240
241
242 /*
243 * OK, so if we get here, the file is open.
244 */
245 if(verbose == D_FILE_INPUT || verbose == D_ALL){
246 fprintf(stdout,"# file_input(): Opened %s for the first time at %p\n", filename,(void*) state->fp);
247 fprintf(stdout,"# file_input(): state->fp is %8p\n",(void*) state->fp);
248 fprintf(stdout,"# file_input(): Parsing header:\n");
249 }
250 state->rptr = 0; /* No rands read yet */
251 /*
252 * We only reset the entire file if there is a nonzero seed passed in.
253 * This clears both rtot and rewind_cnt in addition to rptr.
254 */
255 if(s) {
256 state->rtot = 0;
257 state->rewind_cnt = 0;
258 }
259
260 } else {
261 /*
262 * Rewinding seriously reduces the size of the space being explored.
263 * On the other hand, bombing a test also sucks, especially in a long
264 * -a(ll) run. Therefore we rewind every time our file pointer reaches
265 * the end of the file or call gsl_rng_set(rng,0).
266 */
267 if(state->rptr >= state->flen){
268 rewind(state->fp);
269 state->rptr = 0;
270 state->rewind_cnt++;
271 if(verbose == D_FILE_INPUT || verbose == D_ALL){
272 fprintf(stderr,"# file_input(): Rewinding %s at rtot = %u\n", filename,(uint) state->rtot);
273 fprintf(stderr,"# file_input(): Rewind count = %u, resetting rptr = %lu\n",state->rewind_cnt,state->rptr);
274 }
275 } else {
276 return;
277 }
278 }
279
280 /*
281 * We MUST have precisely three data lines at the beginning after
282 * any comments.
283 */
284 cnt = 0;
285 while(cnt < 3){
286 if(state->fp != NULL) {
287 if(fgets(inbuf,K,state->fp) == 0){
288 fprintf(stderr,"# file_input(): Error: EOF on %s\n",filename);
289 exit(0);
290 }
291 }
292 if(verbose){
293 fprintf(stdout,"%d: %s",cnt,inbuf);
294 }
295
296 /*
297 * Skip comments altogether, whereever they might be. Also adopt code
298 * to use new, improved, more portable "split()" command.
299 */
300 if(inbuf[0] != '#'){
301 /*
302 * Just like perl, sorta. In fact, I'm really liking using
303 * perl-derived utility functions for parsing where I can.
304 */
305 chop(inbuf);
306 numfields = split(inbuf);
307 if(numfields != 2){
308 fprintf(stderr,"# file_input(): Error: Wrong number of fields: format is 'fieldname: value'\n");
309 exit(0);
310 }
311 if(strncmp(splitbuf[0],"type",4) == 0){
312 filetype = splitbuf[1][0];
313 cnt++;
314 if(verbose){
315 fprintf(stdout,"# file_input(): cnt = %d\n",cnt);
316 fprintf(stdout,"# file_input(): filenumtype set to %c\n",filetype);
317 }
318 }
319 if(strncmp(splitbuf[0],"count",5) == 0){
320 state->flen = atoi(splitbuf[1]);
321 filecount = state->flen;
322 cnt++;
323 if(verbose){
324 fprintf(stdout,"# file_input(): cnt = %d\n",cnt);
325 fprintf(stdout,"# file_input(): state->flen set to %lu\n",state->flen);
326 }
327 }
328 if(strncmp(splitbuf[0],"numbit",6) == 0){
329 filenumbits = atoi(splitbuf[1]);
330 cnt++;
331 if(verbose){
332 fprintf(stdout,"# file_input(): cnt = %d\n",cnt);
333 fprintf(stdout,"# file_input(): filenumbits set to %i\n",filenumbits);
334 }
335 }
336 }
337 }
338
339 return;
340
341 }
342
343 static const gsl_rng_type file_input_type =
344 {"file_input", /* name */
345 UINT_MAX, /* RAND_MAX */
346 0, /* RAND_MIN */
347 sizeof (file_input_state_t),
348 &file_input_set,
349 &file_input_get,
350 &file_input_get_double };
351
352 const gsl_rng_type *gsl_rng_file_input = &file_input_type;
353