>From 12ca3d6d5b8591e7bd424ff264b9f26cc2f31b90 Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Thu, 4 Jul 2013 14:40:15 -0600 Subject: [PATCH 0/4] *** SUBJECT HERE *** *** BLURB HERE *** Assaf Gordon (4): shuf: add --repetition to generate random numbers shuf: add tests for --repetition option shuf: mention new --repetition option in NEWS shuf: document new --repetition option NEWS | 3 +++ doc/coreutils.texi | 23 +++++++++++++++++++++++ src/shuf.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- tests/misc/shuf.sh | 29 +++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 4 deletions(-) -- 1.8.3.2 >From 2c09d46ebeee61e2e46633dc8b9158edba1eaa8b Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Thu, 4 Jul 2013 13:26:45 -0600 Subject: [PATCH 1/4] shuf: add --repetition to generate random numbers * src/shuf.c: new option (-r,--repetition), generate random numbers. main(): process new option. usage(): mention new option. write_random_numbers(): generate random numbers. --- src/shuf.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/shuf.c b/src/shuf.c index 0fabb0b..cdc3151 100644 --- a/src/shuf.c +++ b/src/shuf.c @@ -76,6 +76,9 @@ Write a random permutation of the input lines to standard output.\n\ -n, --head-count=COUNT output at most COUNT lines\n\ -o, --output=FILE write result to FILE instead of standard output\n\ --random-source=FILE get random bytes from FILE\n\ + -r, --repetition used with -iLO-HI, output COUNT random numbers\n\ + between LO and HI, with repetitions.\n\ + count defaults to 1 if -n COUNT is not used.\n\ -z, --zero-terminated end lines with 0 byte, not newline\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); @@ -104,6 +107,7 @@ static struct option const long_opts[] = {"head-count", required_argument, NULL, 'n'}, {"output", required_argument, NULL, 'o'}, {"random-source", required_argument, NULL, RANDOM_SOURCE_OPTION}, + {"repetition", no_argument, NULL, 'r'}, {"zero-terminated", no_argument, NULL, 'z'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, @@ -328,6 +332,23 @@ write_permuted_output (size_t n_lines, char *const *line, size_t lo_input, return 0; } +static int +write_random_numbers (struct randint_source *s, size_t count, + size_t lo_input, size_t hi_input, char eolbyte) +{ + size_t i; + const randint range = hi_input - lo_input + 1; + + for (i = 0; i < count; i++) + { + randint j = lo_input + randint_choose (s, range); + if (printf ("%lu%c", j, eolbyte) < 0) + return -1; + } + + return 0; +} + int main (int argc, char **argv) { @@ -340,6 +361,7 @@ main (int argc, char **argv) char eolbyte = '\n'; char **input_lines = NULL; bool use_reservoir_sampling = false; + bool repetition = false; int optc; int n_operands; @@ -348,7 +370,7 @@ main (int argc, char **argv) char **line = NULL; struct linebuffer *reservoir = NULL; struct randint_source *randint_source; - size_t *permutation; + size_t *permutation = NULL; int i; initialize_main (&argc, &argv); @@ -424,6 +446,10 @@ main (int argc, char **argv) random_source = optarg; break; + case 'r': + repetition = true; + break; + case 'z': eolbyte = '\0'; break; @@ -454,9 +480,19 @@ main (int argc, char **argv) } n_lines = hi_input - lo_input + 1; line = NULL; + + /* When generating random numbers with repetitions, + the default count is one, unless specified by the user */ + if (repetition && head_lines == SIZE_MAX) + head_lines = 1 ; } else { + if (repetition) + { + error (0, 0, _("--repetition requires --input-range")); + usage (EXIT_FAILURE); + } switch (n_operands) { case 0: @@ -488,10 +524,12 @@ main (int argc, char **argv) } } - head_lines = MIN (head_lines, n_lines); + if (!repetition) + head_lines = MIN (head_lines, n_lines); randint_source = randint_all_new (random_source, - use_reservoir_sampling ? SIZE_MAX : + (use_reservoir_sampling||repetition)? + SIZE_MAX: randperm_bound (head_lines, n_lines)); if (! randint_source) error (EXIT_FAILURE, errno, "%s", quotearg_colon (random_source)); @@ -512,13 +550,17 @@ main (int argc, char **argv) && (fclose (stdin) != 0)) error (EXIT_FAILURE, errno, _("read error")); - permutation = randperm_new (randint_source, head_lines, n_lines); + if (!repetition) + permutation = randperm_new (randint_source, head_lines, n_lines); if (outfile && ! freopen (outfile, "w", stdout)) error (EXIT_FAILURE, errno, "%s", quotearg_colon (outfile)); if (use_reservoir_sampling) i = write_permuted_output_reservoir (n_lines, reservoir, permutation); + else if (repetition) + i = write_random_numbers (randint_source, head_lines, + lo_input, hi_input, eolbyte); else i = write_permuted_output (head_lines, line, lo_input, permutation, eolbyte); -- 1.8.3.2 >From eb9d50321782cef06d99e03bc07c0658e99091f3 Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Thu, 4 Jul 2013 13:54:04 -0600 Subject: [PATCH 2/4] shuf: add tests for --repetition option * tests/misc/shuf.sh: add tests for --repetition option. --- tests/misc/shuf.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/misc/shuf.sh b/tests/misc/shuf.sh index 171b25a..86bff19 100755 --- a/tests/misc/shuf.sh +++ b/tests/misc/shuf.sh @@ -63,4 +63,33 @@ chmod 0 unreadable || framework_failure_ shuf -n0 unreadable || fail=1 shuf -n1 unreadable && fail=1 +# Test --repetition option + +# --repetition should fail without --input-range +shuf --repetition && + { fail=1; echo "--repetition should fail without range">&2 ; } + +# --repetition without count should return one line +shuf --rep -i0-10 > exp || framework_failure_ +c=$(cat exp | wc -l) || framework_failure_ +test "$c" -eq 1 || { fail=1; echo "--repetition default count is not 1">&2 ; } + +# --repetition can output more values than the input range +shuf --rep -i0-9 -n1000 > exp || framework_failure_ +c=$(cat exp | wc -l) || framework_failure_ +test "$c" -eq 1000 || { fail=1; echo "--repetition with --count failed">&2 ; } + +# Check output values (this is not bullet-proof, but drawing 1000 values +# between 0 and 9 should produce all values, unless there's a bug in shuf +# or a very poor random source +c=$(cat exp | sort -nu | fmt ) || framework_failure_ +test "$c" = "0 1 2 3 4 5 6 7 8 9" || + { fail=1; echo "--repetition produced bad output">&2 ; } + +# check --repetition with non-zero low value +shuf --rep -i222-233 -n2000 > exp || framework_failure_ +c=$(cat exp | sort -nu | fmt ) || framework_failure_ +test "$c" = "222 223 224 225 226 227 228 229 230 231 232 233" || + { fail=1; echo "--repetition produced bad output with non-zero low">&2 ; } + Exit $fail -- 1.8.3.2 >From 5d0c472e55f2ca36f3c3c9a8e8c6117c7a81e35a Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Thu, 4 Jul 2013 13:55:59 -0600 Subject: [PATCH 3/4] shuf: mention new --repetition option in NEWS * NEWS: mention new shuf option. --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 75ec253..9736690 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,9 @@ GNU coreutils NEWS -*- outline -*- csplit accepts a new option: --suppressed-matched, to elide the lines used to identify the split points. + shuf accepts a new option: --repetition (-r). Output random numbers in + a given range, with repetitions. + ** Changes in behavior stdbuf now requires at least one buffering mode option to be specified, -- 1.8.3.2 >From 12ca3d6d5b8591e7bd424ff264b9f26cc2f31b90 Mon Sep 17 00:00:00 2001 From: Assaf Gordon Date: Thu, 4 Jul 2013 14:31:06 -0600 Subject: [PATCH 4/4] shuf: document new --repetition option * doc/coreutils.texi: mention --repetition, add examples. --- doc/coreutils.texi | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/doc/coreutils.texi b/doc/coreutils.texi index b3233f6..817cd7e 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -4945,6 +4945,15 @@ commands like @code{shuf -o F