317 lines
9.7 KiB
C
317 lines
9.7 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ This program is free software; you can redistribute it and/or modify │
|
|
│ it under the terms of the GNU General Public License as published by │
|
|
│ the Free Software Foundation; version 2 of the License. │
|
|
│ │
|
|
│ This program is distributed in the hope that it will be useful, but │
|
|
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
|
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
|
│ General Public License for more details. │
|
|
│ │
|
|
│ You should have received a copy of the GNU General Public License │
|
|
│ along with this program; if not, write to the Free Software │
|
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
|
│ 02110-1301 USA │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/bits/safemacros.internal.h"
|
|
#include "libc/conv/conv.h"
|
|
#include "libc/fmt/fmt.h"
|
|
#include "libc/limits.h"
|
|
#include "libc/log/check.h"
|
|
#include "libc/log/log.h"
|
|
#include "libc/macros.h"
|
|
#include "libc/math.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/rand/lcg.internal.h"
|
|
#include "libc/rand/rand.h"
|
|
#include "libc/runtime/gc.h"
|
|
#include "libc/runtime/runtime.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/sysv/consts/ex.h"
|
|
#include "libc/sysv/consts/exit.h"
|
|
#include "libc/x/x.h"
|
|
#include "third_party/gdtoa/gdtoa.h"
|
|
#include "third_party/getopt/getopt.h"
|
|
#include "tool/viz/lib/formatstringtable.h"
|
|
|
|
typedef double (*round_f)(double);
|
|
typedef unsigned long (*rand_f)(void);
|
|
|
|
struct Range {
|
|
long double a;
|
|
long double b;
|
|
};
|
|
|
|
short xn_ = 8;
|
|
short yn_ = 8;
|
|
double digs_ = 6;
|
|
rand_f rand_;
|
|
round_f rounder_;
|
|
const char *path_ = "-";
|
|
const char *name_ = "M";
|
|
const char *type_ = "float";
|
|
struct Range r1_ = {LONG_MIN, LONG_MAX};
|
|
struct Range r2_ = {0, 1};
|
|
StringTableFormatter *formatter_ = FormatStringTableAsCode;
|
|
|
|
static wontreturn void PrintUsage(int rc, FILE *f) {
|
|
fprintf(f, "Usage: %s%s", program_invocation_name, "\
|
|
[FLAGS] [FILE]\n\
|
|
\n\
|
|
Flags:\n\
|
|
-u unsigned\n\
|
|
-c char\n\
|
|
-s short\n\
|
|
-i int\n\
|
|
-l long\n\
|
|
-d double\n\
|
|
-b bytes [-uc]\n\
|
|
-g non-deterministic rng\n\
|
|
-S output assembly\n\
|
|
-W output whitespace\n\
|
|
-o PATH output path\n\
|
|
-x FLEX\n\
|
|
-w FLEX width\n\
|
|
-y FLEX\n\
|
|
-h FLEX height\n\
|
|
-N NAME name\n\
|
|
-T NAME type name\n\
|
|
-A FLEX min value\n\
|
|
-B FLEX max value\n\
|
|
-R FUNC round function for indexing\n\
|
|
-D FLEX decimal digits to printout\n\
|
|
-v increases verbosity\n\
|
|
-? shows this information\n\
|
|
\n");
|
|
exit(rc);
|
|
}
|
|
|
|
static bool StringEquals(const char *a, const char *b) {
|
|
return strcasecmp(a, b) == 0;
|
|
}
|
|
|
|
static wontreturn void ShowInvalidArg(const char *name, const char *s,
|
|
const char *type) {
|
|
fprintf(stderr, "error: invalid %s %s: %s\n", type, name, s);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static double ParseFlexidecimalOrDie(const char *name, const char *s,
|
|
double min, double max) {
|
|
double x;
|
|
s = firstnonnull(s, "NULL");
|
|
if (strchr(s, '.') || strchr(s, 'e')) {
|
|
x = strtod(s, NULL);
|
|
} else {
|
|
x = strtol(s, NULL, 0);
|
|
}
|
|
if (min <= x && x <= max) {
|
|
return x;
|
|
} else {
|
|
ShowInvalidArg(name, s, "flexidecimal");
|
|
}
|
|
}
|
|
|
|
static round_f ParseRoundingFunctionOrDie(const char *s) {
|
|
if (isempty(s) || StringEquals(s, "none") || StringEquals(s, "null")) {
|
|
return NULL;
|
|
} else if (StringEquals(s, "round")) {
|
|
return round;
|
|
} else if (StringEquals(s, "rint")) {
|
|
return rint;
|
|
} else if (StringEquals(s, "nearbyint")) {
|
|
return nearbyint;
|
|
} else if (StringEquals(s, "trunc")) {
|
|
return trunc;
|
|
} else if (StringEquals(s, "floor")) {
|
|
return floor;
|
|
} else if (StringEquals(s, "ceil")) {
|
|
return ceil;
|
|
} else {
|
|
ShowInvalidArg("round", s, "func");
|
|
}
|
|
}
|
|
|
|
static void ConfigureIntegralRange(const char *type, long min, long max) {
|
|
type_ = type;
|
|
r1_.a = min;
|
|
r1_.b = max;
|
|
r2_.a = min;
|
|
r2_.b = max;
|
|
if (!rounder_) rounder_ = round;
|
|
}
|
|
|
|
void GetOpts(int argc, char *argv[]) {
|
|
int opt;
|
|
bool want_unsigned, want_char, want_short, want_int, want_long, want_double;
|
|
want_unsigned = false;
|
|
want_char = false;
|
|
want_short = false;
|
|
want_int = false;
|
|
want_long = false;
|
|
want_double = false;
|
|
if (argc == 2 &&
|
|
(StringEquals(argv[1], "--help") || StringEquals(argv[1], "-help"))) {
|
|
PrintUsage(EXIT_SUCCESS, stdout);
|
|
}
|
|
while ((opt = getopt(argc, argv, "?vubcsildgSWo:x:w:y:h:N:A:B:C:E:T:R:D:")) !=
|
|
-1) {
|
|
switch (opt) {
|
|
case 'b':
|
|
want_unsigned = true;
|
|
want_char = true;
|
|
break;
|
|
case 'u':
|
|
want_unsigned = true;
|
|
break;
|
|
case 'c':
|
|
want_char = true;
|
|
break;
|
|
case 's':
|
|
want_short = true;
|
|
break;
|
|
case 'i':
|
|
want_int = true;
|
|
break;
|
|
case 'l':
|
|
want_long = true;
|
|
break;
|
|
case 'd':
|
|
want_double = true;
|
|
break;
|
|
case 'g':
|
|
rand_ = rand64;
|
|
break;
|
|
case 'N':
|
|
name_ = optarg;
|
|
break;
|
|
case 'o':
|
|
path_ = optarg;
|
|
break;
|
|
case 'S':
|
|
formatter_ = FormatStringTableAsAssembly;
|
|
break;
|
|
case 'W':
|
|
formatter_ = FormatStringTableBasic;
|
|
break;
|
|
case 't':
|
|
type_ = optarg;
|
|
break;
|
|
case 'x':
|
|
case 'w':
|
|
xn_ = ParseFlexidecimalOrDie("width", optarg, 1, SHRT_MAX);
|
|
break;
|
|
case 'y':
|
|
case 'h':
|
|
yn_ = ParseFlexidecimalOrDie("height", optarg, 1, SHRT_MAX);
|
|
break;
|
|
case 'D':
|
|
digs_ = ParseFlexidecimalOrDie("digs", optarg, 0, 15.95);
|
|
break;
|
|
case 'r':
|
|
rounder_ = ParseRoundingFunctionOrDie(optarg);
|
|
break;
|
|
case 'A':
|
|
r1_.a = ParseFlexidecimalOrDie("r1_.a", optarg, INT_MIN, INT_MAX);
|
|
break;
|
|
case 'B':
|
|
r1_.b = ParseFlexidecimalOrDie("r1_.b", optarg, INT_MIN, INT_MAX);
|
|
break;
|
|
case 'C':
|
|
r2_.a = ParseFlexidecimalOrDie("r2_.a", optarg, INT_MIN, INT_MAX);
|
|
break;
|
|
case 'E':
|
|
r2_.b = ParseFlexidecimalOrDie("r2_.b", optarg, INT_MIN, INT_MAX);
|
|
break;
|
|
case '?':
|
|
PrintUsage(EXIT_SUCCESS, stdout);
|
|
default:
|
|
PrintUsage(EX_USAGE, stderr);
|
|
}
|
|
}
|
|
if (want_unsigned && want_char) {
|
|
ConfigureIntegralRange("unsigned char", 0, 255);
|
|
} else if (want_char) {
|
|
ConfigureIntegralRange("signed char", -128, 127);
|
|
} else if (want_unsigned && want_short) {
|
|
ConfigureIntegralRange("unsigned short", USHRT_MIN, USHRT_MAX);
|
|
} else if (want_short) {
|
|
ConfigureIntegralRange("short", SHRT_MIN, SHRT_MAX);
|
|
} else if (want_unsigned && want_int) {
|
|
ConfigureIntegralRange("unsigned", UINT_MIN, UINT_MAX);
|
|
} else if (want_int) {
|
|
ConfigureIntegralRange("int", INT_MIN, INT_MAX);
|
|
} else if (want_unsigned && want_long) {
|
|
ConfigureIntegralRange("unsigned long", ULONG_MIN, ULONG_MAX);
|
|
} else if (want_long) {
|
|
ConfigureIntegralRange("long", LONG_MIN, LONG_MAX);
|
|
} else if (want_double) {
|
|
type_ = "double";
|
|
r1_.a = LONG_MIN;
|
|
r1_.b = LONG_MAX;
|
|
digs_ = 19;
|
|
}
|
|
}
|
|
|
|
static void *SetRandom(long n, long p[n]) {
|
|
long i;
|
|
uint64_t r;
|
|
if (rand_) {
|
|
for (r = 1, i = 0; i < n; ++i) {
|
|
p[i] = rand_();
|
|
}
|
|
} else {
|
|
for (r = 1, i = 0; i < n; ++i) {
|
|
p[i] = KnuthLinearCongruentialGenerator(&r) >> 32 |
|
|
KnuthLinearCongruentialGenerator(&r) >> 32 << 32;
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static long double ConvertRange(long double x, long double a, long double b,
|
|
long double c, long double d) {
|
|
return (d - c) / (b - a) * (x - a) + c;
|
|
}
|
|
|
|
static double Compand(long x, double a, double b, double c, double d) {
|
|
return ConvertRange(ConvertRange(x, LONG_MIN, LONG_MAX, a, b), a, b, c, d);
|
|
}
|
|
|
|
static void GenerateMatrixImpl(long I[yn_][xn_], double M[yn_][xn_], FILE *f) {
|
|
long y, x;
|
|
for (y = 0; y < yn_; ++y) {
|
|
for (x = 0; x < xn_; ++x) {
|
|
M[y][x] = Compand(I[y][x], r1_.a, r1_.b, r2_.a, r2_.b);
|
|
}
|
|
if (rounder_) {
|
|
for (x = 0; x < xn_; ++x) {
|
|
M[y][x] = rounder_(M[y][x]);
|
|
}
|
|
}
|
|
}
|
|
FormatMatrixDouble(yn_, xn_, M, fputs, f, formatter_, type_, name_, NULL,
|
|
digs_, round);
|
|
}
|
|
|
|
void GenerateMatrix(FILE *f) {
|
|
GenerateMatrixImpl(SetRandom(yn_ * xn_, gc(calloc(yn_ * xn_, sizeof(long)))),
|
|
gc(calloc(yn_ * xn_, sizeof(double))), f);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int i;
|
|
FILE *f;
|
|
showcrashreports();
|
|
GetOpts(argc, argv);
|
|
CHECK_NOTNULL((f = fopen(path_, "w")));
|
|
if (optind < argc) FATALF("TODO(jart): support input files");
|
|
GenerateMatrix(f);
|
|
return fclose(f);
|
|
}
|