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