--- /dev/null
+static const char const *copyright =
+"Random Number List Generator\n"
+"Copyright 2016 TJ <hacker@iam.tj>\n"
+"Licensed on the terms of the GNU General Public License version 3\n";
+
+static const char const *description =
+"Seed the C standard library's random number generator from /dev/random and generate one of more Groups of "
+"Count pseudo-random 'long int' numbers. If a Range is specified reduce each number modulo the range. "
+"If an Offset is specified add it to the random number. If Unique is specified ensure the number has not "
+"already been generated. If Split is specified change to the Split Range after Split numbers have been "
+"generated. If Sort is specified each Group (or Split sub-Groups) is sorted low to high. Pre-defined settings "
+"matching popular lottery games are included. Result is written as text to standard output; Optionally it is "
+"presented as seven segment LED digits. Verbose (debug) messages are written to standard error.";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+// useful default values
+#define COUNT 1
+#define RANGE 100
+#define OFFSET 0
+#define UNIQUE 1
+#define GROUPS 1
+
+// command line option
+struct option {
+ const char *arg;
+ const char *usage;
+};
+
+// order items by longest 'arg' strings first to avoid sub-string matches
+static struct option app_options[] = {
+ { "-ukloto", "Generate values for UK Lotto (c=6,r=59,o=1,u,s)" },
+ { "-euromillions", "Generate values for EuroMillions (c=7,r=50,o=1,u,s,split=5,sr=10)" },
+ { "-split", "Change range after generating value [number]" },
+ { "-sr", "new Range [number] after split" },
+ { "-c", "generate [number] Count of values" },
+ { "-g", "generate [number] Groups of Count values" },
+ { "-r", "numbers in Range from 0 to [number]-1" },
+ { "-o", "Offset each value by +[number]" },
+ { "-u", "only Unique values in each group" },
+ { "-s", "Sort each Group (or Split group) low to high" },
+ { "-t", "Show Title [string] before numbers" },
+ { "-7", "7-segment LED display" },
+ { "-h", "display this help" },
+ { "-d", "debug messages (to stderr)" },
+ { NULL, NULL }
+};
+
+// declare in the same order as 'struct options'
+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 };
+
+// 7 segment LED number patterns
+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};
+
+enum SEGS {S_0, S_1, S_2, S_3, S_4, S_5, S_6, S_7, S_8, S_9, S_dot };
+
+static unsigned char segments[] = {
+ /* 0 */ S_TOP | S_BOT | S_TL | S_TR | S_BL | S_BR,
+ /* 1 */ S_TR | S_BR,
+ /* 2 */ S_TOP | S_TR | S_MID | S_BL | S_BOT,
+ /* 3 */ S_TOP | S_TR | S_MID | S_BR | S_BOT,
+ /* 4 */ S_TL | S_TR | S_MID | S_BR,
+ /* 5 */ S_TOP | S_TL | S_MID | S_BR | S_BOT,
+ /* 6 */ S_TL | S_MID | S_BL | S_BR | S_BOT,
+ /* 7 */ S_TOP | S_TR | S_BR,
+ /* 8 */ S_TOP | S_TL | S_TR | S_MID | S_BL | S_BR | S_BOT,
+ /* 9 */ S_TOP | S_TL | S_TR | S_MID | S_BR,
+ /* A */ S_TOP | S_TL | S_TR | S_MID | S_BL | S_BR,
+ /* b */ S_TL | S_MID | S_TL | S_TR | S_BOT,
+ /* C */ S_TOP | S_TL | S_BL | S_BOT,
+ /* d */ S_TR | S_MID | S_BL | S_BR | S_BOT,
+ /* E */ S_TOP | S_TL | S_MID | S_BL | S_BOT,
+ /* F */ S_TOP | S_TL | S_MID | S_BL,
+ /* . */ S_DOT
+};
+
+// display help
+void
+usage(void)
+{
+ printf("%s\n", copyright);
+ for (int o = 0; app_options[o].arg; ++o)
+ printf(" %s\t\t%s\n", app_options[o].arg, app_options[o].usage);
+ printf("\n%s\n", description);
+}
+
+// integer powers of 10
+long int
+powi(unsigned int base, unsigned int exponent)
+{
+ for (int n = 0; n < exponent; ++n)
+ base *= 10;
+ return base;
+}
+
+// parse base 10 string into a number
+long int
+parse_long_int(const char *num)
+{
+ const char *ch;
+ unsigned bytes = sizeof(long int);
+ int count = 0;
+ int valid = 0;
+ int negative = 0; // defaults to assuming a positive number
+ long int result = 0;
+ long int parsed = 0;
+
+ /* This initial parse extracts valid digits until the end of valid characters, or string, whichever happens first
+ Because at this point the number of valid digits is not known each digit's value is multiplied by 10 times its
+ position in the valid number. E.g. "12345" becomes 54,321:
+ (1*1) + (2*10) + (3*100) + (4*1000) + (5*10000)
+ Obviously this is inverted compared to the real value.
+ */
+ for (ch = num; ch && *ch; ++ch) {
+ if (*ch == ' ')
+ continue;
+ else if (*ch == '-' && count == 0)
+ negative = 1;
+ else if (*ch >= '0' && *ch <= '9') {
+ // add value of digit multiplied by 10 x 'column'
+ parsed += (*ch - '0') * powi(1, count);
+ ++count;
+ valid = 1;
+ }
+ else
+ break;
+ }
+ /* If the parsed value is valid the value-position of each digit is inverted so e.g. 54,321 becomes 12,345:
+ (1*10000) + (2*1000) + (3*100) + (4*10) + (5*1).
+ */
+ if (valid) {
+ int digit = 0;
+ for (count=count-1; count >= 0; --count) {
+ int div = powi(1, count);
+ int mul = powi(1, digit);
+ int val = parsed/div;
+ parsed -= val * div;
+ result += val * mul;
+ ++digit;
+ }
+ if (negative)
+ result = -result;
+ }
+ return result;
+}
+
+// count the base-10 digits in a number
+int
+digits_count(long int value)
+{
+ int count = 1;
+ while (value > 9) {
+ value /= 10;
+ ++count;
+ }
+ return count;
+}
+
+// comparator for qsort()
+static int
+compar(const void *left, const void *right)
+{
+ const long int *l = (long int *)left;
+ const long int *r = (long int *)right;
+ return *l == *r ? 0 : (*l < *r ? -1 : 1);
+}
+
+// entry point
+int
+main(int argc, char **argv, char **env)
+{
+ long int r;
+ long int *unique;
+ ssize_t bytes;
+ unsigned int seed;
+ int not_unique;
+ int fd;
+ int result = 1;
+ int do_count = COUNT;
+ int do_range = RANGE;
+ int do_offset = OFFSET;
+ int do_unique = UNIQUE;
+ int do_groups = GROUPS;
+ int do_sort = 0;
+ int do_debug = 0;
+ int do_7seg = 0;
+ int do_split = 0; // index of 'count' where a new range applies
+ int do_split_range = 0; // the new range (useful for e.g. EuroMillions 5+2 (1-50, 1-10))
+ const char *dev_random = "/dev/random";
+ const char *title = NULL;
+
+ if (argc == 1) {
+ usage();
+ return 0;
+ }
+
+ printf("%s\n", copyright);
+
+ for (int a = 1; a < argc; ++a) { // parse each command-line argument
+ if (do_debug) fprintf(stderr, "Testing arg %d %s\n", a, argv[a]);
+ for (int option = 0; app_options[option].arg; ++option) { // for each option...
+ if (do_debug) fprintf(stderr, "Testing option %d %s\n", option, app_options[option].arg);
+ if (strncmp(argv[a], app_options[option].arg, strlen(app_options[option].arg)) == 0) {
+ switch (option) {
+ case O_HELP:
+ usage();
+ return 0;
+ case O_DEBUG:
+ do_debug = 1;
+ break;
+ case O_COUNT:
+ do_count = parse_long_int(argv[++a]);
+ break;
+ case O_GROUP:
+ do_groups = parse_long_int(argv[++a]);
+ break;
+ case O_UNIQUE:
+ do_unique = 1;
+ break;
+ case O_RANGE:
+ do_range = parse_long_int(argv[++a]);
+ if (!do_split_range)
+ do_split_range = do_range;
+ break;
+ case O_OFFSET:
+ do_offset = parse_long_int(argv[++a]);
+ break;
+ case O_SORT:
+ do_sort = 1;
+ break;
+ case O_TITLE:
+ title = argv[++a];
+ break;
+ case O_7SEG:
+ do_7seg = 1;
+ break;
+ case O_SPLIT:
+ do_split = parse_long_int(argv[++a]);
+ break;
+ case O_SPLITRANGE:
+ do_split_range = parse_long_int(argv[++a]);
+ break;
+ case O_UKLOTO:
+ do_count = 6;
+ do_range = 59;
+ do_offset = 1;
+ do_unique = 1;
+ do_sort = 1;
+ title = app_options[option].arg + 1;
+ break;
+ case O_EUROMILLIONS:
+ do_count = 7;
+ do_range = 50;
+ do_offset = 1;
+ do_unique = 1;
+ do_sort = 1;
+ do_split = 5;
+ do_split_range = 10;
+ title = app_options[option].arg + 1;
+ break;
+ } // switch
+ break;
+ } // arg == option comparison
+ else {
+ fprintf(stderr, "Error: unknown option %s\n", argv[a]);
+ return 1;
+ }
+ } // options
+ } // args
+
+ if (!do_split)
+ do_split = do_count; // no Split sub-group
+
+ if (do_debug)
+ setvbuf(stderr, NULL, _IONBF, 0); // don't buffer debug messages
+
+ unique = calloc(do_count, sizeof(*unique)); // reserve storage for a Group's values
+ if (unique) {
+ if ((fd = open(dev_random, O_RDONLY)) >= 0) {
+ bytes = read(fd, (void *)&seed, sizeof(seed)); // get a truly random value
+ if (do_debug) fprintf(stderr, "Seed %u of %ld bytes from %s\n", seed, bytes, dev_random);
+ close(fd);
+ if (bytes == sizeof(seed)) { // ensure bytes read from /dev/random fill the seed
+ srandom(seed); // seed te pseudo-random number generator
+
+ if (title)
+ printf("%s\n", title);
+
+ for (int group = 0; group < do_groups; ++group) { // generate each Group
+ if (do_debug) fprintf(stderr, "Generating group %d\n", group);
+ int digit_count = 0; // keep track of the number of digits in the Group
+
+ for (int n = 0; n < do_count; ++n) { // generate each value
+ // determine if this is now a Split sub-group
+ unsigned int in_split = (do_split < do_count && n >= do_split) ? 1 : 0;
+
+ if (do_debug) fprintf(stderr, "Generating value %d in range %d,", n, in_split ? do_split_range : do_range);
+
+ r = random(); // the (long int) random number
+ // adjust to fit Range
+ r = do_offset + (r % (in_split ? do_split_range : do_range));
+ unique[n] = r;
+ if (do_debug) fprintf(stderr, " value: %ld\n", unique[n]);
+
+ if (do_unique) {
+ if (do_debug) fprintf(stderr, "%s\n", "Unique check");
+ not_unique = 0;
+
+ for (int m = (in_split ? do_split : 0); m < n && !not_unique; ++m) { // detect repeated values in sub-group
+ if (do_debug) fprintf(stderr, " comparing value %d (%ld) with %d (%ld)\n", n, unique[n], m, unique[m]);
+ if (unique[m] == unique[n])
+ not_unique = 1;
+ if (do_debug) fprintf(stderr, "%sUnique\n", not_unique ? "NOT " : "");
+ }
+ if (not_unique) {
+ --n; // do this Count value again
+ continue;
+ }
+ digit_count += digits_count(unique[n]);
+ }
+ } // count
+ if (do_sort) {
+ if (do_debug) fprintf(stderr, "%s\n", "Sorting");
+ // if there is no split sub-group this will sort all values (since do_split == do_count)
+ // if there is a split sub-group this will only sort the values before the split
+ qsort(unique, do_split, sizeof(*unique), compar);
+ if (do_split < do_count) // sort the values after the split
+ qsort(unique + do_split, do_count - do_split, sizeof(*unique), compar);
+ }
+
+ if (do_7seg) { // generate and draw the pseudo 7-segment 'LED' display
+ // size and spacing of digits
+ unsigned int d_rows = 9;
+ unsigned int d_cols = 5;
+ unsigned int d_space = 3;
+ // (x,y) coordinate maximums
+ int grid_y_max = d_rows;
+ int grid_x_max = (digit_count * d_cols) + (do_count * d_space);
+ if (do_split < do_count)
+ grid_x_max += d_space * 2; // space the Split sub-group clearly
+
+ // track current (x,y) coordinate
+ int grid_y = 0;
+ int grid_x = 0;
+
+ if (do_debug) fprintf(stderr, "Grid: digits: %d rows: %d cols: %d\n", digit_count, grid_y_max, grid_x_max);
+
+ unsigned char *grid = malloc(grid_x_max * grid_y_max);
+ if (grid) {
+ memset(grid, ' ', grid_y_max * grid_x_max); // default all 'LEDs' to unlit
+ int digit_i = 0; // keep track of which digit of the entire current group is being rendered
+
+ for (int y = 1; y <= grid_y_max; ++y) { // insert string terminators at the end of each row
+ char *p = grid + y * grid_x_max - 1;
+ *p = 0;
+ }
+ for (int c = 0; c < do_count; ++c) { // process each value in the group
+ long int val = unique[c];
+
+ for (int d = digits_count(unique[c]) - 1; d >= 0; --d) { // split value into individual digits
+ long int v = val / powi(1, d); // isolate a base-10 digit
+ val -= v * powi(1, d); // update the remaining value
+ unsigned char s = segments[v]; // get the 'LED' On flags for this digit
+ if (do_debug) fprintf(stderr, " digit: %d %d: %ld = %02x",digit_i, d, v, s);
+
+ for (int x = grid_x; x < grid_x + d_cols; ++x) { // scan the columns...
+ for (int y = grid_y; y < grid_y_max; ++y) { // ... by row
+ int show = 0; // set to 1 if this 'LED' position should be 'lit'
+
+ // draw the horizontal bars
+ if (x >= grid_x && x < grid_x + d_cols - 1) {
+ if ((s & S_TOP && y == 0) ||
+ (s & S_MID && y == (grid_y_max-1)/2) ||
+ (s & S_BOT && y == grid_y_max-1))
+ show = 1;
+ }
+ // draw the vertical bars
+ if (s & S_TL && (x == grid_x && (y >= 0 && y <= (grid_y_max-1)/2)))
+ show = 1;
+ if (s & S_TR && (x == (grid_x + d_cols - 2) && (y >= 0 && y <= (grid_y_max-1)/2)))
+ show = 1;
+ if (s & S_BL && (x == grid_x && (y >= (grid_y_max-1)/2)))
+ show = 1;
+ if (s & S_BR && (x == (grid_x + d_cols - 2) && (y >= (grid_y_max-1)/2)))
+ show = 1;
+
+ if (show)
+ *(grid + (y * grid_x_max) + x ) = '0' + v; // use the current digit as the 'light'
+ }
+ }
+ grid_x += d_cols; // advance to next digit position
+ ++digit_i; // advance to next digit in group
+ } // digits
+ if (do_debug) fprintf(stderr, "%s", "\n");
+ // insert comma after digits of each value
+ *(grid + ((grid_y_max-1) * grid_x_max) + grid_x + 1) = ',';
+ grid_x += d_space; // advance with additional spacing between digits
+
+ if (do_split < do_count && c == do_split - 1)
+ grid_x += d_space * 2; // advance with addtional spacing between Split sub-groups
+
+ } // count
+
+ // the completed 7-segment 'LED' display
+ printf("%s", "\n");
+ for (int y = 0; y < grid_y_max; ++y) {
+ char *line = grid + y * grid_x_max;
+ printf("%s\n", line);
+ }
+ printf("%s", "\n");
+
+ free(grid);
+ grid = NULL;
+ } // grid
+ } // 7-segment
+ else { // normal text output of values
+ for (int c = 0; c < do_count; ++c) {
+ printf(" %ld", unique[c]);
+ }
+ }
+ printf("%s", "\n");
+ } // group
+ result = 0;
+ } // seed is valid
+ else {
+ fprintf(stderr, "Error: insuffient random bytes returned from %s\n", dev_random);
+ }
+ free(unique);
+ } // /dev/random
+ } // memory allocated for values
+ else {
+ fprintf(stderr, "Error: failed to allocate %ld bytes of memory for values\n", do_count * sizeof(*unique));
+ result = 3;
+ }
+ return result;
+}
+