From 4b2c822f0284118d09cfe7d8327a83b899d3361b Mon Sep 17 00:00:00 2001 From: TJ Date: Mon, 11 Jan 2016 22:00:18 +0000 Subject: [PATCH 1/1] Random Number List Generator Signed-off-by: TJ --- Makefile | 12 ++ randlist.c | 443 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 455 insertions(+) create mode 100644 Makefile create mode 100644 randlist.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..021e390 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +PROG = randlist +SRC = $(PROG).c +CC = gcc +CFLAGS = -O2 + +all: + $(CC) $(CFLAGS) -o $(PROG) $(SRC) + +clean: + rm -f $(PROG) *.o + +PHONY: all clean diff --git a/randlist.c b/randlist.c new file mode 100644 index 0000000..cbaa7b5 --- /dev/null +++ b/randlist.c @@ -0,0 +1,443 @@ +static const char const *copyright = +"Random Number List Generator\n" +"Copyright 2016 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 +#include +#include +#include +#include +#include +#include + +// 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; +} + -- 2.17.1