cosmopolitan/tool/build/coefficients.c

231 lines
7.5 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 "dsp/core/half.h"
#include "dsp/core/q.h"
#include "libc/conv/conv.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/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/dtoa/dtoa.h"
#include "third_party/getopt/getopt.h"
#define USAGE \
" [FLAGS] [INT|FLOAT...]\n\
\n\
Example:\n\
\n\
coefficients -n -- 1 4 6 4 1 # Gaussian Blur\n\
coefficients -L16 -H235 -- .299 .587 .114 # BT.601 RGB→Y\n\
\n\
Flags:\n\
-v verbose\n\
-n normalize\n\
-m FLEX explicit Q(bits)\n\
-L FLEX low data value [default 0]\n\
-H FLEX high data value [default 255]\n\
-? shows this information\n\
\n"
static struct Flags {
bool n;
long L, H, m;
} flags_ = {
.L = 0,
.H = 255,
};
static noreturn void PrintUsage(int rc, FILE *f) {
fprintf(f, "Usage: %s%s", program_invocation_name, USAGE);
exit(rc);
}
static void GetOpts(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "?nvrL:H:m:")) != -1) {
switch (opt) {
case 'v':
g_loglevel++;
break;
case 'n':
flags_.n = true;
break;
case 'L':
flags_.L = strtol(optarg, NULL, 0);
break;
case 'H':
flags_.H = strtol(optarg, NULL, 0);
break;
case 'm':
flags_.m = strtol(optarg, NULL, 0);
break;
case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default:
PrintUsage(EX_USAGE, stderr);
}
}
}
static void *Normalize(int n, double A[static 8]) {
int i;
double sum, rnorm;
for (sum = i = 0; i < n; ++i) {
sum += A[i];
}
if (fabs(sum - 1) > DBL_MIN * n) {
rnorm = 1 / sum;
for (i = 0; i < n; ++i) {
A[i] *= rnorm;
}
}
return A;
}
static void GetLimits(int n, const long I[static 8], long m, long L, long H,
long res[2][2]) {
int i, j[8];
long x, p[2] = {L, H};
DCHECK(0 < n && n <= 8);
memset(res, 0, sizeof(long) * 2 * 2);
for (j[0] = 0; j[0] < ARRAYLEN(p); ++j[0]) {
for (j[1] = 0; j[1] < ARRAYLEN(p); ++j[1]) {
for (j[2] = 0; j[2] < ARRAYLEN(p); ++j[2]) {
for (j[3] = 0; j[3] < ARRAYLEN(p); ++j[3]) {
for (j[4] = 0; j[4] < ARRAYLEN(p); ++j[4]) {
for (j[5] = 0; j[5] < ARRAYLEN(p); ++j[5]) {
for (j[6] = 0; j[6] < ARRAYLEN(p); ++j[6]) {
for (j[7] = 0; j[7] < ARRAYLEN(p); ++j[7]) {
x = 0;
for (i = 0; i < ARRAYLEN(j); ++i) {
x += p[j[i]] * I[i];
if (x < res[0][0]) res[0][0] = x;
if (x > res[0][1]) res[0][1] = x;
}
x += 1l << (m - 1);
if (x < res[0][0]) res[0][0] = x;
if (x > res[0][1]) res[0][1] = x;
x >>= m;
if (x < res[1][0]) res[1][0] = x;
if (x > res[1][1]) res[1][1] = x;
}
}
}
}
}
}
}
}
}
static const char *GetFittingMachineWord(long L, long H) {
if (-128 <= L && H <= 127) return "int8";
if (0 <= L && H <= 255) return "uint8";
if (-0x8000 <= L && H <= 0x7fff) return "int16";
if (~0x7fffffff <= L && H <= 0x7fffffff) return "int32";
if (0 <= L && H <= 0xffffffff) return "uint32";
return "INT64";
}
static char *DescribeMachineWord(long L, long H) {
return xasprintf("%s[%,ld..%,ld]", GetFittingMachineWord(L, H), L, H);
}
static void ShowBetterCoefficients(int n, double C[static 8], long L, long H) {
long err, I[8], lim[2][2];
char buf[32], kAlphabet[] = "abcdefgh";
int i, j, m, emitted, count, indices[8];
CHECK_LT(L, H);
DCHECK(0 < n && n <= 8);
for (m = 2; m < 40; ++m) {
memset(I, 0, sizeof(I));
if (C[6] || C[7]) {
err = GetIntegerCoefficients8(I, C, m, L, H);
} else {
err = GetIntegerCoefficients(I, C, m, L, H);
}
GetLimits(n, I, m, L, H, lim);
for (count = i = 0; i < n; ++i) {
if (I[i]) {
indices[count++] = i;
}
}
if (count) {
emitted = 0;
if (m) emitted += printf("(");
for (i = 0; i < count; ++i) {
if (i) emitted += printf(" + ");
if (I[indices[i]] != 1) {
emitted += printf("%ld*%c", I[indices[i]], kAlphabet[indices[i]]);
} else {
emitted += printf("%c", kAlphabet[indices[i]]);
}
}
if (m) {
if (m > 1) {
emitted += printf(" + %ld", 1l << (m - 1));
}
emitted += printf(")>>%d", m);
}
printf("%*s", MAX(0, 80 - emitted), " ");
printf("/* %s %s %s ε=%,ld */\n", gc(DescribeMachineWord(L, H)),
gc(DescribeMachineWord(lim[0][0], lim[0][1])),
gc(DescribeMachineWord(lim[1][0], lim[1][1])), err);
}
}
}
static int ReadIdealCoefficients(double C[static 8], int sn, char *S[sn]) {
int i, n;
if ((n = MIN(8, sn)) > 0) {
C[0] = C[1] = C[2] = C[3] = C[4] = C[5] = 0;
for (i = 0; i < n; ++i) {
C[i] = strtod(S[i], NULL);
}
}
return n;
}
int ToolBuildCoefficients(int argc, char *argv[]) {
int n;
double C[8];
GetOpts(argc, argv);
setvbuf(stdout, malloc(PAGESIZE), _IOLBF, PAGESIZE);
if ((n = ReadIdealCoefficients(C, argc - optind, argv + optind))) {
if (flags_.n) Normalize(n, C);
ShowBetterCoefficients(n, C, flags_.L, flags_.H);
}
return 0;
}
int main(int argc, char *argv[]) {
g_loglevel = kLogWarn;
showcrashreports();
return ToolBuildCoefficients(argc, argv);
}