1 static const char const *copyright =
2 "Random Number List Generator\n"
3 "Copyright 2016 TJ <hacker@iam.tj>\n"
4 "Licensed on the terms of the GNU General Public License version 3\n";
6 static const char const *description =
7 "Seed the C standard library's random number generator from /dev/random and generate one of more Groups of "
8 "Count pseudo-random 'long int' numbers. If a Range is specified reduce each number modulo the range. "
9 "If an Offset is specified add it to the random number. If Unique is specified ensure the number has not "
10 "already been generated. If Split is specified change to the Split Range after Split numbers have been "
11 "generated. If Sort is specified each Group (or Split sub-Groups) is sorted low to high. Pre-defined settings "
12 "matching popular lottery games are included. Result is written as text to standard output; Optionally it is "
13 "presented as seven segment LED digits. Verbose (debug) messages are written to standard error.";
19 #include <sys/types.h>
23 // useful default values
30 // command line option
36 // order items by longest 'arg' strings first to avoid sub-string matches
37 static struct option app_options[] = {
38 { "-ukloto", "Generate values for UK Lotto (c=6,r=59,o=1,u,s)" },
39 { "-euromillions", "Generate values for EuroMillions (c=7,r=50,o=1,u,s,split=5,sr=10)" },
40 { "-split", "Change range after generating value [number]" },
41 { "-sr", "new Range [number] after split" },
42 { "-c", "generate [number] Count of values" },
43 { "-g", "generate [number] Groups of Count values" },
44 { "-r", "numbers in Range from 0 to [number]-1" },
45 { "-o", "Offset each value by +[number]" },
46 { "-u", "only Unique values in each group" },
47 { "-s", "Sort each Group (or Split group) low to high" },
48 { "-t", "Show Title [string] before numbers" },
49 { "-7", "7-segment LED display" },
50 { "-h", "display this help" },
51 { "-d", "debug messages (to stderr)" },
55 // declare in the same order as 'struct options'
56 enum OPTS { O_UKLOTO, O_EUROMILLIONS, O_SPLIT, O_SPLITRANGE, O_COUNT, O_GROUP, O_RANGE, O_OFFSET, O_UNIQUE, O_SORT, O_TITLE, O_7SEG, O_HELP, O_DEBUG };
58 // 7 segment LED number patterns
59 enum SEG_PATTERN {S_TOP = 0x01, S_MID = 0x02, S_BOT = 0x04, S_TL = 0x08, S_TR = 0x10, S_BL = 0x20, S_BR = 0x40, S_DOT = 0x80};
61 enum SEGS {S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_dot };
63 static unsigned char segments[] = {
64 /* 0 */ S_TOP | S_BOT | S_TL | S_TR | S_BL | S_BR,
66 /* 2 */ S_TOP | S_TR | S_MID | S_BL | S_BOT,
67 /* 3 */ S_TOP | S_TR | S_MID | S_BR | S_BOT,
68 /* 4 */ S_TL | S_TR | S_MID | S_BR,
69 /* 5 */ S_TOP | S_TL | S_MID | S_BR | S_BOT,
70 /* 6 */ S_TL | S_MID | S_BL | S_BR | S_BOT,
71 /* 7 */ S_TOP | S_TR | S_BR,
72 /* 8 */ S_TOP | S_TL | S_TR | S_MID | S_BL | S_BR | S_BOT,
73 /* 9 */ S_TOP | S_TL | S_TR | S_MID | S_BR,
74 /* A */ S_TOP | S_TL | S_TR | S_MID | S_BL | S_BR,
75 /* b */ S_TL | S_MID | S_TL | S_TR | S_BOT,
76 /* C */ S_TOP | S_TL | S_BL | S_BOT,
77 /* d */ S_TR | S_MID | S_BL | S_BR | S_BOT,
78 /* E */ S_TOP | S_TL | S_MID | S_BL | S_BOT,
79 /* F */ S_TOP | S_TL | S_MID | S_BL,
87 printf("%s\n", copyright);
88 for (int o = 0; app_options[o].arg; ++o)
89 printf(" %s\t\t%s\n", app_options[o].arg, app_options[o].usage);
90 printf("\n%s\n", description);
93 // integer powers of 10
95 powi(unsigned int base, unsigned int exponent)
97 for (int n = 0; n < exponent; ++n)
102 // parse base 10 string into a number
104 parse_long_int(const char *num)
107 unsigned bytes = sizeof(long int);
110 int negative = 0; // defaults to assuming a positive number
114 /* This initial parse extracts valid digits until the end of valid characters, or string, whichever happens first
115 Because at this point the number of valid digits is not known each digit's value is multiplied by 10 times its
116 position in the valid number. E.g. "12345" becomes 54,321:
117 (1*1) + (2*10) + (3*100) + (4*1000) + (5*10000)
118 Obviously this is inverted compared to the real value.
120 for (ch = num; ch && *ch; ++ch) {
123 else if (*ch == '-' && count == 0)
125 else if (*ch >= '0' && *ch <= '9') {
126 // add value of digit multiplied by 10 x 'column'
127 parsed += (*ch - '0') * powi(1, count);
134 /* If the parsed value is valid the value-position of each digit is inverted so e.g. 54,321 becomes 12,345:
135 (1*10000) + (2*1000) + (3*100) + (4*10) + (5*1).
139 for (count=count-1; count >= 0; --count) {
140 int div = powi(1, count);
141 int mul = powi(1, digit);
142 int val = parsed/div;
153 // count the base-10 digits in a number
155 digits_count(long int value)
165 // comparator for qsort()
167 compar(const void *left, const void *right)
169 const long int *l = (long int *)left;
170 const long int *r = (long int *)right;
171 return *l == *r ? 0 : (*l < *r ? -1 : 1);
176 main(int argc, char **argv, char **env)
185 int do_count = COUNT;
186 int do_range = RANGE;
187 int do_offset = OFFSET;
188 int do_unique = UNIQUE;
189 int do_groups = GROUPS;
193 int do_split = 0; // index of 'count' where a new range applies
194 int do_split_range = 0; // the new range (useful for e.g. EuroMillions 5+2 (1-50, 1-10))
195 const char *dev_random = "/dev/random";
196 const char *title = NULL;
203 printf("%s\n", copyright);
205 for (int a = 1; a < argc; ++a) { // parse each command-line argument
206 if (do_debug) fprintf(stderr, "Testing arg %d %s\n", a, argv[a]);
207 for (int option = 0; app_options[option].arg; ++option) { // for each option...
208 if (do_debug) fprintf(stderr, "Testing option %d %s\n", option, app_options[option].arg);
209 if (strncmp(argv[a], app_options[option].arg, strlen(app_options[option].arg)) == 0) {
218 do_count = parse_long_int(argv[++a]);
221 do_groups = parse_long_int(argv[++a]);
227 do_range = parse_long_int(argv[++a]);
229 do_split_range = do_range;
232 do_offset = parse_long_int(argv[++a]);
244 do_split = parse_long_int(argv[++a]);
247 do_split_range = parse_long_int(argv[++a]);
255 title = app_options[option].arg + 1;
265 title = app_options[option].arg + 1;
269 } // arg == option comparison
271 fprintf(stderr, "Error: unknown option %s\n", argv[a]);
278 do_split = do_count; // no Split sub-group
281 setvbuf(stderr, NULL, _IONBF, 0); // don't buffer debug messages
283 unique = calloc(do_count, sizeof(*unique)); // reserve storage for a Group's values
285 if ((fd = open(dev_random, O_RDONLY)) >= 0) {
286 bytes = read(fd, (void *)&seed, sizeof(seed)); // get a truly random value
287 if (do_debug) fprintf(stderr, "Seed %u of %ld bytes from %s\n", seed, bytes, dev_random);
289 if (bytes == sizeof(seed)) { // ensure bytes read from /dev/random fill the seed
290 srandom(seed); // seed te pseudo-random number generator
293 printf("%s\n", title);
295 for (int group = 0; group < do_groups; ++group) { // generate each Group
296 if (do_debug) fprintf(stderr, "Generating group %d\n", group);
297 int digit_count = 0; // keep track of the number of digits in the Group
299 for (int n = 0; n < do_count; ++n) { // generate each value
300 // determine if this is now a Split sub-group
301 unsigned int in_split = (do_split < do_count && n >= do_split) ? 1 : 0;
303 if (do_debug) fprintf(stderr, "Generating value %d in range %d,", n, in_split ? do_split_range : do_range);
305 r = random(); // the (long int) random number
306 // adjust to fit Range
307 r = do_offset + (r % (in_split ? do_split_range : do_range));
309 if (do_debug) fprintf(stderr, " value: %ld\n", unique[n]);
312 if (do_debug) fprintf(stderr, "%s\n", "Unique check");
315 for (int m = (in_split ? do_split : 0); m < n && !not_unique; ++m) { // detect repeated values in sub-group
316 if (do_debug) fprintf(stderr, " comparing value %d (%ld) with %d (%ld)\n", n, unique[n], m, unique[m]);
317 if (unique[m] == unique[n])
319 if (do_debug) fprintf(stderr, "%sUnique\n", not_unique ? "NOT " : "");
322 --n; // do this Count value again
325 digit_count += digits_count(unique[n]);
329 if (do_debug) fprintf(stderr, "%s\n", "Sorting");
330 // if there is no split sub-group this will sort all values (since do_split == do_count)
331 // if there is a split sub-group this will only sort the values before the split
332 qsort(unique, do_split, sizeof(*unique), compar);
333 if (do_split < do_count) // sort the values after the split
334 qsort(unique + do_split, do_count - do_split, sizeof(*unique), compar);
337 if (do_7seg) { // generate and draw the pseudo 7-segment 'LED' display
338 // size and spacing of digits
339 unsigned int d_rows = 9;
340 unsigned int d_cols = 5;
341 unsigned int d_space = 3;
342 // (x,y) coordinate maximums
343 int grid_y_max = d_rows;
344 int grid_x_max = (digit_count * d_cols) + (do_count * d_space);
345 if (do_split < do_count)
346 grid_x_max += d_space * 2; // space the Split sub-group clearly
348 // track current (x,y) coordinate
352 if (do_debug) fprintf(stderr, "Grid: digits: %d rows: %d cols: %d\n", digit_count, grid_y_max, grid_x_max);
354 unsigned char *grid = malloc(grid_x_max * grid_y_max);
356 memset(grid, ' ', grid_y_max * grid_x_max); // default all 'LEDs' to unlit
357 int digit_i = 0; // keep track of which digit of the entire current group is being rendered
359 for (int y = 1; y <= grid_y_max; ++y) { // insert string terminators at the end of each row
360 char *p = grid + y * grid_x_max - 1;
363 for (int c = 0; c < do_count; ++c) { // process each value in the group
364 long int val = unique[c];
366 for (int d = digits_count(unique[c]) - 1; d >= 0; --d) { // split value into individual digits
367 long int v = val / powi(1, d); // isolate a base-10 digit
368 val -= v * powi(1, d); // update the remaining value
369 unsigned char s = segments[v]; // get the 'LED' On flags for this digit
370 if (do_debug) fprintf(stderr, " digit: %d %d: %ld = %02x",digit_i, d, v, s);
372 for (int x = grid_x; x < grid_x + d_cols; ++x) { // scan the columns...
373 for (int y = grid_y; y < grid_y_max; ++y) { // ... by row
374 int show = 0; // set to 1 if this 'LED' position should be 'lit'
376 // draw the horizontal bars
377 if (x >= grid_x && x < grid_x + d_cols - 1) {
378 if ((s & S_TOP && y == 0) ||
379 (s & S_MID && y == (grid_y_max-1)/2) ||
380 (s & S_BOT && y == grid_y_max-1))
383 // draw the vertical bars
384 if (s & S_TL && (x == grid_x && (y >= 0 && y <= (grid_y_max-1)/2)))
386 if (s & S_TR && (x == (grid_x + d_cols - 2) && (y >= 0 && y <= (grid_y_max-1)/2)))
388 if (s & S_BL && (x == grid_x && (y >= (grid_y_max-1)/2)))
390 if (s & S_BR && (x == (grid_x + d_cols - 2) && (y >= (grid_y_max-1)/2)))
394 *(grid + (y * grid_x_max) + x ) = '0' + v; // use the current digit as the 'light'
397 grid_x += d_cols; // advance to next digit position
398 ++digit_i; // advance to next digit in group
400 if (do_debug) fprintf(stderr, "%s", "\n");
401 // insert comma after digits of each value
402 *(grid + ((grid_y_max-1) * grid_x_max) + grid_x + 1) = ',';
403 grid_x += d_space; // advance with additional spacing between digits
405 if (do_split < do_count && c == do_split - 1)
406 grid_x += d_space * 2; // advance with addtional spacing between Split sub-groups
410 // the completed 7-segment 'LED' display
412 for (int y = 0; y < grid_y_max; ++y) {
413 char *line = grid + y * grid_x_max;
414 printf("%s\n", line);
422 else { // normal text output of values
423 for (int c = 0; c < do_count; ++c) {
424 printf(" %ld", unique[c]);
432 fprintf(stderr, "Error: insuffient random bytes returned from %s\n", dev_random);
436 } // memory allocated for values
438 fprintf(stderr, "Error: failed to allocate %ld bytes of memory for values\n", do_count * sizeof(*unique));