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 7a3d0ac06b0b914fbd04bd19e76fc682fcb38a20
parent 1fb885b94e493d9d36da5ddf5d2cd8fb8a99510f
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Tue, 31 Aug 2021 10:41:51 +0200

rangetest: switch to C implementation

Diffstat:
MMakefile | 51+++++++++++++++++++++++++++++++++++++++++----------
Drangetest | 75---------------------------------------------------------------------------
Arangetest.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 178 insertions(+), 85 deletions(-)

diff --git a/Makefile b/Makefile @@ -8,25 +8,53 @@ PREFIX = /usr/local MANPREFIX = ${PREFIX}/man DOCPREFIX = ${PREFIX}/share/doc/${NAME} -SCRIPTS = \ +SCRIPTS =\ histpdf\ max\ mean\ min\ - rangetest\ sum\ - transpose + transpose\ + +BIN =\ + rangetest\ + +SRC = ${BIN:=.c} -MAN1 = ${SCRIPTS:=.1} -DOC = \ +HDR =\ + arg.h\ + +LIBS = -lm + +_CFLAGS = ${CFLAGS} ${INCS} -DVERSION=\"${VERSION}\" +_LDFLAGS = ${LDFLAGS} ${LIBS} +_CPPFLAGS = ${CPPFLAGS} + +MAN1 = ${BIN:=.1} ${SCRIPTS:=.1} +DOC =\ LICENSE\ - README + README\ + +all: ${BIN} + +${BIN}: ${@:=.o} + +OBJ = ${SRC:.c=.o} + +${OBJ}: ${HDR} + +.o: + ${CC} -o $@ $< ${_LDFLAGS} + +.c.o: + ${CC} ${_CFLAGS} ${_CPPFLAGS} -o $@ -c $< + install: # installing executable files and scripts. mkdir -p "${DESTDIR}${PREFIX}/bin" cp -f ${SCRIPTS} "${DESTDIR}${PREFIX}/bin" - for f in ${SCRIPTS}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done + for f in ${BIN} ${SCRIPTS}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done # installing documentation files. mkdir -p "${DESTDIR}${DOCPREFIX}" cp -f ${DOC} "${DESTDIR}${DOCPREFIX}" @@ -38,7 +66,7 @@ install: uninstall: # removing executable files and scripts. - for f in ${SCRIPTS}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done + for f in ${BIN} ${SCRIPTS}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done # removing example files. for d in ${DOC}; do rm -f "${DESTDIR}${DOCPREFIX}/$$d"; done -rmdir "${DESTDIR}${DOCPREFIX}" @@ -48,10 +76,13 @@ uninstall: dist: rm -rf "${NAME}-${VERSION}" mkdir -p "${NAME}-${VERSION}" - cp -rf ${MAN1} ${DOC} ${SCRIPTS} "${NAME}-${VERSION}" + cp -rf ${MAN1} ${DOC} ${BIN} ${SCRIPTS} "${NAME}-${VERSION}" # make tarball tar cf - "${NAME}-${VERSION}" | \ gzip -c > "${NAME}-${VERSION}.tar.gz" rm -rf "${NAME}-${VERSION}" -.PHONY: install uninstall dist +clean: + rm -f ${BIN} ${OBJ} + +.PHONY: install uninstall dist clean diff --git a/rangetest b/rangetest @@ -1,75 +0,0 @@ -#!/usr/bin/awk -f -# uses a binary search to run a "cmd" where the first occurence of -# string @VAL@ is substituted for a value between "min_val" and -# "max_val". Successful runs are reported on stdout, failed runs -# on stderr. The "cmd" command must successfully run with either -# "min_val" or "max_val". - -function die(s) { - printf "error: %s\n", s > "/dev/stderr" - exit 1 -} - -function launch(cmd, val) { - sub(/@VAL@/, val, cmd) - if (system(cmd)) { - printf "%g\n", val > "/dev/stderr" - return 1 - } else { - printf "%g\n", val > "/dev/stdout" - return 0 - } -} - -function binary_search(cmd, min, max, maxiter) { - - minfail = launch(cmd, min) - maxfail = launch(cmd, max) - - if (minfail && maxfail) - die("both min_val and max_val runs errored") - - if (!minfail && !maxfail) - die("both min_val and max_val ran successfully") - - while (min <= max && iter < maxiter) { - val = min + 0.5 * (max - min) - - if (launch(cmd, val)) { # the cmd fails - if (maxfail) { - max = val - maxfail = 1 - } else { - min = val - minfail = 1 - } - } else { # the cmd is ok - if (maxfail) { - min = val - minfail = 0 - } else { - max = val - maxfail = 0 - } - } - iter++ - } -} - -BEGIN { - - if (ARGC != 4) - die("usage: rangetest cmd min_val max_val") - - cmd = ARGV[1] - min = ARGV[2] - max = ARGV[3] - - if (!match(cmd, /@VAL@/)) - die("@VAL@ not found in cmd") - - if (min >= max) - die("min_val must be smaller than max_val") - - binary_search(cmd, min, max, 10) -} diff --git a/rangetest.c b/rangetest.c @@ -0,0 +1,137 @@ +#include <stdio.h> +#include <stdlib.h> +#include <err.h> +#include <limits.h> +#include <string.h> + +#include "arg.h" + +#define VALUESTR "@VAL@" + +char *argv0; + +#ifdef NEED_STRLCPY /* OpenBSD implementation */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) { + const char *osrc = src; + size_t nleft = dsize; + + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++= *src++) == '\0') + break; + } + } + + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; + while (*src++) + ; + } + + return(src - osrc - 1); +} +#endif /* NEED_STRLCPY */ + +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); +} + +static int +launch(char *cmd, char *cmd0, double val) +{ + char *c; + + if ((c = strstr(cmd0, VALUESTR)) == NULL) + errx(1, VALUESTR " not found in cmd"); + + if (strlcpy(cmd, cmd0, PATH_MAX) >= PATH_MAX) + err(1, "cmd too long"); + + sprintf(cmd + (c - cmd0), "%g%s >/dev/null", val, c + strnlen(VALUESTR, sizeof(cmd))); + if (system(cmd)) { + fprintf(stderr, "%g\n", val); + return 1; + } else { + printf("%g\n", val); + return 0; + } +} + +static void +binary_search(char *cmd, char *cmd0, double minv, double maxv, int maxiter) +{ + int minfail, maxfail; + int iter; + double val; + + minfail = launch(cmd, cmd0, minv); + maxfail = launch(cmd, cmd0, maxv); + + if (minfail && maxfail) + errx(2, "both min_val and max_val runs errored"); + + else if (!minfail && !maxfail) + errx(3, "both min_val and max_val ran successfully"); + + while (minv <= maxv && iter < maxiter) { + val = minv + 0.5 * (maxv - minv); + + if (launch(cmd, cmd0, val)) { + if (maxfail) { + maxv = val; + maxfail = 1; + } else { + minv = val; + minfail = 1; + } + } else { + if (maxfail) { + minv = val; + minfail = 0; + } else { + maxv = val; + maxfail = 0; + } + } + iter++; + } +} + +int +main(int argc, char *argv[]) +{ + int maxiter = 10; + double minv, maxv; + char cmd0[PATH_MAX] = "", cmd[PATH_MAX] = ""; + + 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; + + if (argc == 3) { + if (strlcpy(cmd0, argv[0], sizeof(cmd0)) >= sizeof(cmd0)) + err(1, "cmd too long"); + minv = atof(argv[1]); + maxv = atof(argv[2]); + if (minv >= maxv) + errx(1, "min_val must be smaller than max_val"); + binary_search(cmd, cmd0, minv, maxv, maxiter); + } else + usage(); + + return 0; +}