numtools

perform numerical operations on vectors and matrices in unix pipes
git clone git://src.adamsgaard.dk/numtools # fast
git clone https://src.adamsgaard.dk/numtools.git # slow
Log | Files | Refs | README | LICENSE Back to index

commit 8787e852ca96ee9c18d4ed1a45016996e464940a
parent d206d176dbb5704fda2d5cc33010f54b8f36ff38
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Wed, 11 May 2022 14:57:18 +0200

use safe formatting parameters and option parsing

Diffstat:
Mmax.1 | 29++++++++++++++++-------------
Mmax.c | 45++++++++++++++++++++++++++-------------------
Mmean.1 | 29++++++++++++++++-------------
Mmean.c | 45++++++++++++++++++++++++++-------------------
Mmin.1 | 29++++++++++++++++-------------
Mmin.c | 45++++++++++++++++++++++++++-------------------
Mrandcounts.1 | 38++++++++++++++++++++++++++++----------
Mrandcounts.c | 98+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Mrandnum.c | 8++++----
Mrange.1 | 61+++++++++++++++++++++++++++++++------------------------------
Mrange.c | 107++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mrangetest.c | 40+++++++++++++++++++---------------------
Mstddev.1 | 27+++++++++++++++------------
Mstddev.c | 52++++++++++++++++++++++++++++++----------------------
Mstdvar.1 | 29++++++++++++++++-------------
Mstdvar.c | 52++++++++++++++++++++++++++++++----------------------
Msum.1 | 29++++++++++++++++-------------
Msum.c | 45++++++++++++++++++++++++++-------------------
Mtranspose.1 | 6+++---
Mutil.c | 9++++-----
Mutil.h | 2+-
21 files changed, 469 insertions(+), 356 deletions(-)

diff --git a/max.1 b/max.1 @@ -6,8 +6,9 @@ .Nd returns the maximum value for each column .Sh SYNOPSIS .Nm -.Op Fl f Ar fmtstr -.Op Fl h +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl p Ar prec .Sh DESCRIPTION .Nm returns the maximum numerical value for each column in standard @@ -17,20 +18,22 @@ the same number of fields. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information and exit. +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is tab characters. +.It Fl n +Do not print a newline after the final value. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .El .Sh EXAMPLES .Dl $ printf '1\et2\et3\en4\et5\et6\en' | max -.Dl 4 5 6 +.Dl 4 5 6 .Sh SEE ALSO .Xr mean 1 , .Xr min 1 , diff --git a/max.c b/max.c @@ -4,40 +4,45 @@ #include <unistd.h> #include <limits.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-f fmtstr] [-h] ", argv0); + errx(1, "usage: max [-d delimstr] [-n] [-p prec]"); } int main(int argc, char *argv[]) { - int ret; + int ch, prec = 17, finalnl = 1; size_t i = 0, nf = 0, nr = 0, linesize = 0; - char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g"; + char *line = NULL, *data = NULL, *delimstr = "\t"; + const char *errstr; double val, *vals = NULL; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - default: - usage(); - } ARGEND; + while ((ch = getopt(argc, argv, "d:np:")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'p': + prec = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + /*argv += optind;*/ if (argc > 0) usage(); @@ -54,7 +59,9 @@ main(int argc, char *argv[]) } nr++; } - printfarr(fmtstr, vals, nf); + printfarr(delimstr, prec, vals, nf); + if (finalnl) + putchar('\n'); free(line); free(vals); diff --git a/mean.1 b/mean.1 @@ -6,8 +6,9 @@ .Nd returns the average value for each column .Sh SYNOPSIS .Nm -.Op Fl f Ar fmtstr -.Op Fl h +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl p Ar prec .Sh DESCRIPTION .Nm returns the mean numerical value for each column in standard input. @@ -16,20 +17,22 @@ number of fields. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information and exit. +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is tab characters. +.It Fl n +Do not print a newline after the final value. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .El .Sh EXAMPLES .Dl $ printf '1\et2\et3\en4\et5\et6\en' | mean -.Dl 2.5 3.5 4.5 +.Dl 2.5 3.5 4.5 .Sh SEE ALSO .Xr max 1 , .Xr min 1 , diff --git a/mean.c b/mean.c @@ -4,40 +4,45 @@ #include <unistd.h> #include <limits.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-f fmtstr] [-h] ", argv0); + errx(1, "usage: mean [-d delimstr] [-n] [-p prec]"); } int main(int argc, char *argv[]) { - int ret; + int ch, prec = 17, finalnl = 1; size_t i = 0, nf = 0, nr = 0, linesize = 0; - char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g"; + char *line = NULL, *data = NULL, *delimstr = "\t"; + const char *errstr; double val, *vals = NULL; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - default: - usage(); - } ARGEND; + while ((ch = getopt(argc, argv, "d:np:")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'p': + prec = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + /*argv += optind;*/ if (argc > 0) usage(); @@ -57,7 +62,9 @@ main(int argc, char *argv[]) } for (i = 0; i < nf; i++) vals[i] /= (double)nr; - printfarr(fmtstr, vals, nf); + printfarr(delimstr, prec, vals, nf); + if (finalnl) + putchar('\n'); free(line); free(vals); diff --git a/min.1 b/min.1 @@ -6,8 +6,9 @@ .Nd returns the minimum value for each column .Sh SYNOPSIS .Nm -.Op Fl f Ar fmtstr -.Op Fl h +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl p Ar prec .Sh DESCRIPTION .Nm returns the minimum numerical value for each column in standard @@ -17,20 +18,22 @@ number of fields. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information and exit. +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is tab characters. +.It Fl n +Do not print a newline after the final value. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .El .Sh EXAMPLES .Dl $ printf '1\et2\et3\en4\et5\et6\en' | min -.Dl 1 2 3 +.Dl 1 2 3 .Sh SEE ALSO .Xr max 1 , .Xr mean 1 , diff --git a/min.c b/min.c @@ -4,40 +4,45 @@ #include <unistd.h> #include <limits.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-f fmtstr] [-h] ", argv0); + errx(1, "usage: max [-d delimstr] [-n] [-p prec]"); } int main(int argc, char *argv[]) { - int ret; + int ch, prec = 17, finalnl = 1; size_t i = 0, nf = 0, nr = 0, linesize = 0; - char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g"; + char *line = NULL, *data = NULL, *delimstr = "\t"; + const char *errstr; double val, *vals = NULL; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - default: - usage(); - } ARGEND; + while ((ch = getopt(argc, argv, "d:np:")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'p': + prec = strtonum(optarg, -10, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + /*argv += optind;*/ if (argc > 0) usage(); @@ -54,7 +59,9 @@ main(int argc, char *argv[]) } nr++; } - printfarr(fmtstr, vals, nf); + printfarr(delimstr, prec, vals, nf); + if (finalnl) + putchar('\n'); free(line); free(vals); diff --git a/randcounts.1 b/randcounts.1 @@ -6,8 +6,10 @@ .Nd produces random counts in weighted bins .Sh SYNOPSIS .Nm -.Op Fl h -.Op Fl n Ar num +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl N Ar num +.Op Fl p Ar prec .Op Fl r Ar repeats .Op Fl R .Op Fl s Ar seed @@ -31,15 +33,28 @@ Output consists of the number of points per bin, in tab-separated format. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl h -Show usage information. -.It Fl n Ar num +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is tab characters. +.It Fl n +Do not print a newline after the final value. +.It Fl N Ar num Number of points to place in the bins. The default is 1. .It Fl r Ar repeats Repeat the binning several times, with one realization per line of output. .It Fl R Show the output as ratios (in the range [0;1]) instead of counts. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +This option only applies if called with the +.Fl R +option. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .It Fl s Ar seed Seed the pseudo-random number generator with this value, which is used to generate reproducable binning. @@ -48,15 +63,18 @@ to generate reproducable binning. Put one point in four bins with equal probability (25%). Due to the randomness, your output may differ: .Dl $ randcounts 0.25 0.25 0.25 0.25 -.Dl 0 1 0 0 +.Dl 0 1 0 0 .Pp -Put 100 points in two bins with 75% and 25% probability, respectively: -.Dl $ randcounts -n 100 0.75 0.25 +Put 100 points in two bins with 75% and 25% probability, respectively, +and print the count ratios for each bin: +.Dl $ randcounts -N 100 -R 0.75 0.25 +.Dl 0.72999999999999998 0.27000000000000002 .Pp -Put 100 points in three equal bins 1000 times, and calculate the average bin sizes with +Put 100 points in three equal bins 1000 times, and calculate the average +bin sizes with .Xr mean 1 : .Dl $ randcounts -n 100 -r 1000 0.333 0.333 0.334 | mean -.Dl 33.067 32.82 34.113 +.Dl 33.067 32.82 34.113 .Sh SEE ALSO .Xr mean 1 , .Xr range 1 , diff --git a/randcounts.c b/randcounts.c @@ -5,68 +5,85 @@ #include <string.h> #include <math.h> #include <sys/time.h> +#include <unistd.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-h] [-n num] [-r repeats] [-R] [-s seed] " - "weight1 [weight2 ...]\n", argv0); + errx(1, "usage: randcounts [-d delimstr] [-n] [-N num] [-p prec]" + "[-r repeats] [-R] [-s seed] weight1 [weight2 ...]"); } int main(int argc, char *argv[]) { - int i, s = 0, N; + int i, ch, nbins, prec = 17, finalnl = 1, s = 0; long j, seed, *counts = NULL, n = 1, r, repeats = 1, ratio = 0; double val = 0.0, weightsum = 0.0, *weights = NULL; + char *delimstr = "\t"; + const char *errstr; struct timeval t1; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'h': - usage(); - break; - case 'n': - n = atol(EARGF(usage())); - break; - case 'r': - repeats = atol(EARGF(usage())); - break; - case 'R': - ratio = 1; - break; - case 's': - s = 1; - seed = atol(EARGF(usage())); - break; - default: - usage(); - } ARGEND; - + while ((ch = getopt(argc, argv, "d:nN:r:Rp:s:")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'N': + n = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad num value, %s: %s", errstr, optarg); + break; + case 'r': + repeats = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad repeats value, %s: %s", errstr, optarg); + break; + case 'R': + ratio = 1; + break; + case 'p': + prec = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + case 's': + seed = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad seed value, %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; if (argc < 1) usage(); - N = argc; - if (!(weights = calloc(N, sizeof(double))) || - !(counts = calloc(N, sizeof(long)))) + nbins = argc; + if (!(weights = calloc(nbins, sizeof(double))) || + !(counts = calloc(nbins, sizeof(long)))) err(1, "calloc"); - for (i = 0; i < N; i++) { - weights[i] = atof(argv[i]); + for (i = 0; i < nbins; i++) { + if (!sscanf(argv[i], "%lf", &weights[i])) + errx(1, "bad weight value: %s", argv[i]); if (weights[i] <= 0.0) errx(1, "weight %d is not positive (%g)", i, weights[i]); if (weights[i] > 1.0) errx(1, "weight %d is greater than 1 (%g)", i, weights[i]); } - for (i = 0; i < N; i++) + for (i = 0; i < nbins; i++) weightsum += weights[i]; if (fabs(weightsum - 1.0) > 1e-3) errx(1, "weights do not sum to 1 (%g)", weightsum); @@ -83,12 +100,12 @@ main(int argc, char *argv[]) #endif for (r = 0; r < repeats; r++) { - for (i = 0; i < N; i++) + for (i = 0; i < nbins; i++) counts[i] = 0; for (j = 0; j < n; j++) { val = drand48(); weightsum = 0.0; - for (i = 0; i < N; i++) { + for (i = 0; i < nbins; i++) { weightsum += weights[i]; if (val <= weightsum) { counts[i]++; @@ -96,15 +113,16 @@ main(int argc, char *argv[]) } } } - for (i = 0; i < N; i++) { + for (i = 0; i < nbins; i++) { if (ratio) - printf("%.17g", (double)counts[i] / n); + printf("%.*g", prec, (double)counts[i] / n); else printf("%ld", counts[i]); - if (i < N - 1) - putchar('\t'); + if (i < nbins - 1) + fputs(delimstr, stdout); else - putchar('\n'); + if (finalnl) + putchar('\n'); } } diff --git a/randnum.c b/randnum.c @@ -20,9 +20,9 @@ usage(void) int main(int argc, char *argv[]) { - int i, ret, ch, s = 0, prec = 17, finalnl = 1; - long j, seed, n = 1; - double val, minv = 0.0, maxv = 1.0; + int i, ch, s = 0, prec = 17, finalnl = 1; + long seed, n = 1; + double minv = 0.0, maxv = 1.0; char *delimstr = "\n"; const char *errstr; struct timeval t1; @@ -44,7 +44,7 @@ main(int argc, char *argv[]) errx(1, "bad num value, %s: %s", errstr, optarg); break; case 'p': - prec = strtonum(optarg, -10, INT_MAX, &errstr); + prec = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr != NULL) errx(1, "bad precision value, %s: %s", errstr, optarg); break; diff --git a/range.1 b/range.1 @@ -7,11 +7,12 @@ .Sh SYNOPSIS .Nm .Op Fl b +.Op Fl d Ar delimstr .Op Fl e -.Op Fl f Ar fmtstr -.Op Fl h .Op Fl l -.Op Fl n Ar num +.Op Fl n +.Op Fl N Ar num +.Op Fl p Ar prec .Op Fl s .Oo .Op Ar min_val @@ -45,6 +46,10 @@ as the first value, making it a half-open interval .Ar max_val ], or an entirely open interval when combined with .Op Ar e . +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is newlines. .It Fl e Do not include .Ar max_val @@ -53,54 +58,50 @@ as the last value, making it a half-open interval .Ar max_val [, or an entirely open interval when combined with .Op Ar b . -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information. .It Fl l Produce output with even intervals in logarithmic space between 10^min_val and 10^max_val. -.It Fl n Ar num +.It Fl n +Do not print a newline after the final value. +.It Fl N Ar num Number of values to produce within the specified range. The default is 10. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .It Fl s -Print the spacing between numbers and exit. +Print the numerical spacing between numbers and exit. .El .Sh EXAMPLES Generate four equally-spaced numbers in the closed default range [0;1]: -.Dl $ range -n 4 -f '%.17g\en' 0 1 +.Dl $ range -N 4 .Dl 0 .Dl 0.33333333333333331 .Dl 0.66666666666666663 .Dl 1 .Pp Generate four numbers in the range ]0;1[: -.Dl $ range -b -e -n 4 0 1 -.Dl 0.2 -.Dl 0.4 -.Dl 0.6 -.Dl 0.8 +.Dl $ range -be -N 4 +.Dl 0.20000000000000001 +.Dl 0.40000000000000002 +.Dl 0.60000000000000009 +.Dl 0.80000000000000004 .Pp -Repeat and modify a string three times: -.Dl $ range -n 3 -f 'The best number is %.0g' 1 3 -.Dl The best number is 1 -.Dl The best number is 2 -.Dl The best number is 3 +Generate three space-separated numbers: +.Dl $ range -d' ' -N 3 1 3 +.Dl 1 2 3 .Pp Generate three numbers evenly distributed in logspace from 10^0 to 10^2: -.Dl $ range -l -n 3 0 2 +.Dl $ range -l -N 3 0 2 .Dl 1 .Dl 10 .Dl 100 .Pp Generate three numbers in the range [-2;-1]: -.Dl $ range -n 3 -- -2 -1 +.Dl $ range -N 3 -- -2 -1 .Dl -2 .Dl -1.5 .Dl -1 @@ -108,7 +109,7 @@ Generate three numbers in the range [-2;-1]: .Xr max 1 , .Xr mean 1 , .Xr min 1 , -.Xr rangetest 1 , -.Xr sum 1 +.Xr randnum 1 , +.Xr rangetest 1 .Sh AUTHORS .An Anders Damsgaard Aq Mt anders@adamsgaard.dk diff --git a/range.c b/range.c @@ -4,78 +4,95 @@ #include <limits.h> #include <string.h> #include <math.h> +#include <unistd.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-b] [-e] [-f fmtstr] [-h] [-l] [-n num] [-s] " - "[[min_val] max_val]\n", argv0); + errx(1, "usage: range [-b] [-d delimstr] [-e] [-l] [-n] [-N num] " + "[-p prec] [-s] [[min_val] max_val]"); } int main(int argc, char *argv[]) { - int i, ret, n = 10, logrange = 0, openstart = 0, openend = 0, - reportdx = 0; - double minv = 0.0, maxv = 1.0, dx; - char fmtstr[PATH_MAX] = "%.17g"; + int i, j, ch, n = 10, logrange = 0, openstart = 0, openend = 0, + prec = 17, finalnl = 1, reportdx = 0; + double minv = 0.0, maxv = 1.0, dx, val; + const char *errstr; + char *delimstr = "\n"; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'b': - openstart = 1; - break; - case 'e': - openend = 1; - break; - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - case 'l': - logrange = 1; - break; - case 'n': - n = atoi(EARGF(usage())); - break; - case 's': - reportdx = 1; - break; - default: - usage(); - } ARGEND; - + while ((ch = getopt(argc, argv, "bd:elnN:p:s")) != -1) { + switch (ch) { + case 'b': + openstart = 1; + break; + case 'd': + delimstr = optarg; + break; + case 'e': + openend = 1; + break; + case 'l': + logrange = 1; + break; + case 'n': + finalnl = 0; + break; + case 'N': + n = strtonum(optarg, 0, LONG_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad num value, %s: %s", errstr, optarg); + break; + case 'p': + prec = strtonum(optarg, -10, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + case 's': + reportdx = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; if (argc > 2) usage(); else if (argc == 2) { - minv = atof(argv[0]); - maxv = atof(argv[1]); + if (!sscanf(argv[0], "%lf", &minv)) + errx(1, "bad minv value: %s", argv[0]); + if (!sscanf(argv[1], "%lf", &maxv)) + errx(1, "bad maxv value: %s", argv[1]); } else if (argc == 1) - maxv = atof(argv[0]); + if (!sscanf(argv[0], "%lf", &maxv)) + errx(1, "bad maxv value: %s", argv[0]); dx = (maxv - minv) / (n - 1 + openend + openstart); if (reportdx) { - printf(fmtstr, dx); + printf("%.*g", prec, dx); + if (finalnl) + putchar('\n'); return 0; } - for (i = 0 + openstart; i < n + openstart; i++) { + for (i = 0; i < n; i++) { + j = i + openstart; if (logrange) - printf(fmtstr, pow(10, minv + i * dx)); + val = pow(10, minv + j * dx); else - printf(fmtstr, minv + i * dx); - putchar('\n'); + val = minv + j * dx; + printf("%.*g", prec, val); + if (i < n - 1) + fputs(delimstr, stdout); } + if (finalnl) + putchar('\n'); return 0; } diff --git a/rangetest.c b/rangetest.c @@ -3,19 +3,17 @@ #include <err.h> #include <limits.h> #include <string.h> +#include <unistd.h> -#include "arg.h" #include "util.h" #define VALUESTR "@VAL@" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-h] [-n maxiter] cmd min_val max_val\n" - "where cmd must contain the string '" VALUESTR "'", argv0); + errx(1, "usage: rangetest [-n maxiter] cmd min_val max_val\n" + "where cmd must contain the string '" VALUESTR "'"); } static int @@ -41,8 +39,7 @@ launch(char *cmd, char *cmd0, double val) static void binary_search(char *cmd, char *cmd0, double minv, double maxv, int maxiter) { - int minfail, maxfail; - int iter = 0; + int minfail, maxfail, iter = 0; double val; minfail = launch(cmd, cmd0, minv); @@ -76,23 +73,24 @@ binary_search(char *cmd, char *cmd0, double minv, double maxv, int maxiter) int main(int argc, char *argv[]) { - int maxiter = 10; + int ch, maxiter = 10; double minv, maxv; char cmd0[PATH_MAX] = "", cmd[PATH_MAX] = ""; + const char *errstr; - ARGBEGIN { - case 'h': - usage(); - break; - case 'n': - maxiter = atoi(EARGF(usage())); - if (maxiter < 1) - errx(1, "maxiter (-n) must be positive"); - break; - default: - usage(); - } ARGEND; - + while ((ch = getopt(argc, argv, "N:")) != -1) { + switch (ch) { + case 'N': + maxiter = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad maxiter value, %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; if (argc == 3) { if (strlcpy(cmd0, argv[0], sizeof(cmd0)) >= sizeof(cmd0)) err(1, "cmd too long"); diff --git a/stddev.1 b/stddev.1 @@ -6,8 +6,9 @@ .Nd returns the standard deviation for each input column .Sh SYNOPSIS .Nm -.Op Fl f Ar fmtstr -.Op Fl h +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl p Ar prec .Op Fl u .Sh DESCRIPTION .Nm @@ -19,16 +20,18 @@ The output is always in full double precision. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information and exit. +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is tab characters. +.It Fl n +Do not print a newline after the final value. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .It Fl u Return the uncorrected sample standard deviation instead. .El diff --git a/stddev.c b/stddev.c @@ -4,44 +4,50 @@ #include <unistd.h> #include <math.h> #include <limits.h> +#include <unistd.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-f fmtstr] [-h] [-u]\n", argv0); + errx(1, "usage: stddev [-d delimstr] [-n] [-p prec] [-u]"); } int main(int argc, char *argv[]) { - int ret; + int ch, prec = 17, finalnl = 1; size_t i, j, nf = 0, nr = 0, correction = 1; double *means = NULL, *stdvals = NULL, **vals = NULL; - char fmtstr[PATH_MAX] = "%.17g"; + const char *errstr; + char *delimstr = "\t"; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - case 'u': - correction = 0; - break; - default: - usage(); - } ARGEND; + while ((ch = getopt(argc, argv, "d:np:u")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'p': + prec = strtonum(optarg, -10, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + case 'u': + correction = 0; + break; + default: + usage(); + } + } + /*argc -= optind;*/ + /*argv += optind;*/ nr = fscanmatrix(stdin, &vals, &nf); @@ -63,7 +69,9 @@ main(int argc, char *argv[]) stdvals[i] = sqrt(stdvals[i] / ((double)(nr - correction))); } - printfarr(fmtstr, stdvals, nf); + printfarr(delimstr, prec, stdvals, nf); + if (finalnl) + putchar('\n'); free(means); free(stdvals); diff --git a/stdvar.1 b/stdvar.1 @@ -6,8 +6,9 @@ .Nd returns the standard variance for each input column .Sh SYNOPSIS .Nm -.Op Fl f Ar fmtstr -.Op Fl h +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl p Ar prec .Op Fl u .Sh DESCRIPTION .Nm @@ -19,18 +20,20 @@ The output is always in full double precision. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information and exit. +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is tab characters. +.It Fl n +Do not print a newline after the final value. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .It Fl u -Return the uncorrected sample standard variance instead. +Return the uncorrected sample standard deviation instead. .El .Sh EXAMPLES Compute the corrected standard variance for some input numbers: diff --git a/stdvar.c b/stdvar.c @@ -4,44 +4,50 @@ #include <unistd.h> #include <math.h> #include <limits.h> +#include <unistd.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-f fmtstr] [-h] [-u]\n", argv0); + errx(1, "usage: stdvar [-d delimstr] [-n] [-p prec] [-u]"); } int main(int argc, char *argv[]) { - int ret; + int ch, prec = 17, finalnl = 1; size_t i, j, nf = 0, nr = 0, correction = 1; double *means = NULL, *stdvars = NULL, **vals = NULL; - char fmtstr[PATH_MAX] = "%.17g"; + const char *errstr; + char *delimstr = "\t"; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - case 'u': - correction = 0; - break; - default: - usage(); - } ARGEND; + while ((ch = getopt(argc, argv, "d:np:u")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'p': + prec = strtonum(optarg, -10, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + case 'u': + correction = 0; + break; + default: + usage(); + } + } + /*argc -= optind;*/ + /*argv += optind;*/ nr = fscanmatrix(stdin, &vals, &nf); @@ -63,7 +69,9 @@ main(int argc, char *argv[]) stdvars[i] /= (double)(nr - correction); } - printfarr(fmtstr, stdvars, nf); + printfarr(delimstr, prec, stdvars, nf); + if (finalnl) + putchar('\n'); free(means); free(stdvars); diff --git a/sum.1 b/sum.1 @@ -6,8 +6,9 @@ .Nd returns the sum for each column .Sh SYNOPSIS .Nm -.Op Fl f Ar fmtstr -.Op Fl h +.Op Fl d Ar delimstr +.Op Fl n +.Op Fl p Ar prec .Sh DESCRIPTION .Nm returns the numerical sum for each column in standard input. @@ -16,20 +17,22 @@ same number of fields. .Pp The options are as follows: .Bl -tag -width Ds -.It Fl f Ar fmtstr -Formatting string to use as documented in -.Xr printf 3 . -When including a format specifier (%..), only use forms that are -compatible with -.Vt double -types. -The default format string is '%.17g'. -.It Fl h -Show usage information and exit. +.It Fl d Ar delimstr +Separate output values by +.Ar delimstr . +The default delimiter is tab characters. +.It Fl n +Do not print a newline after the final value. +.It Fl p prec +Print the output values with +.Ar prec +digits of precision. +By default, the output is printed with 17 digits of precision, which is +full double precision on 64-bit systems. .El .Sh EXAMPLES .Dl $ printf '1\et2\et3\en4\et5\et6\en' | sum -.Dl 5 7 9 +.Dl 5 7 9 .Sh SEE ALSO .Xr max 1 , .Xr mean 1 , diff --git a/sum.c b/sum.c @@ -4,40 +4,45 @@ #include <unistd.h> #include <limits.h> -#include "arg.h" #include "util.h" -char *argv0; - static void usage(void) { - errx(1, "usage: %s [-f fmtstr] [-h] ", argv0); + errx(1, "usage: sum [-d delimstr] [-n] [-p prec]"); } int main(int argc, char *argv[]) { - int ret; + int ch, prec = 17, finalnl = 1; size_t i = 0, nf = 0, nr = 0, linesize = 0; - char *line = NULL, *data = NULL, fmtstr[PATH_MAX] = "%.17g"; + char *line = NULL, *data = NULL, *delimstr = "\t"; + const char *errstr; double val, *vals = NULL; if (pledge("stdio", NULL) == -1) err(2, "pledge"); - ARGBEGIN { - case 'f': - ret = snprintf(fmtstr, sizeof(fmtstr), "%s", EARGF(usage())); - if (ret < 0 || (size_t)ret >= sizeof(fmtstr)) - errx(1, "%s: could not write fmtstr", __func__); - break; - case 'h': - usage(); - break; - default: - usage(); - } ARGEND; + while ((ch = getopt(argc, argv, "d:np:")) != -1) { + switch (ch) { + case 'd': + delimstr = optarg; + break; + case 'n': + finalnl = 0; + break; + case 'p': + prec = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr != NULL) + errx(1, "bad precision value, %s: %s", errstr, optarg); + break; + default: + usage(); + } + } + argc -= optind; + /*argv += optind;*/ if (argc > 0) usage(); @@ -55,7 +60,9 @@ main(int argc, char *argv[]) } nr++; } - printfarr(fmtstr, vals, nf); + printfarr(delimstr, prec, vals, nf); + if (finalnl) + putchar('\n'); free(line); free(vals); diff --git a/transpose.1 b/transpose.1 @@ -16,9 +16,9 @@ Input fields must be tab-separated and each line must contain the same number of fields. .Sh EXAMPLES .Dl $ printf '1\et2\et3\en4\et5\et6\en' | transpose -.Dl 1 4 -.Dl 2 5 -.Dl 3 6 +.Dl 1 4 +.Dl 2 5 +.Dl 3 6 .Sh SEE ALSO .Xr max 1 , .Xr mean 1 , diff --git a/util.c b/util.c @@ -45,16 +45,15 @@ printarr(double *arr, size_t len) } void -printfarr(char *fmtstr, double *arr, size_t len) +printfarr(char *delimstr, int prec, double *arr, size_t len) { size_t i; for (i = 0; i < len; i++) { - printf(fmtstr, arr[i]); - if (i < len) - printf(DELIMSTR); + printf("%.*g", prec, arr[i]); + if (i < len - 1) + fputs(delimstr, stdout); } - putchar('\n'); } size_t diff --git a/util.h b/util.h @@ -26,7 +26,7 @@ void * xreallocarray(void *m, size_t n, size_t s); size_t allocarr(double **arr, const char *str, size_t maxlen); int scannextval(char **str, double *val); void printarr(double *arr, size_t len); -void printfarr(char *fmtstr, double *arr, size_t len); +void printfarr(char *delimstr, int prec, double *arr, size_t len); size_t fscanmatrix(FILE *stream, double ***arr, size_t *nf); #endif