mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-19 10:29:30 +01:00
Update modules/qrpn (#64)
This commit is contained in:
parent
a2111640af
commit
9172bcd29f
@ -1,7 +1,5 @@
|
||||
/* for standalone usage, compile with: cc -Os -Wall -Wextra -Wshadow -march=native qrpn.c -lm -o qrpn */
|
||||
|
||||
/* this is mostly old code and needs to be rewritten but it's too useful to discard */
|
||||
|
||||
#define _DEFAULT_SOURCE
|
||||
#define _XOPEN_SOURCE
|
||||
#include "qrpn.h"
|
||||
@ -47,6 +45,25 @@ static double arcexsecant(const double x) {
|
||||
return atan(sqrt(x * x + x * 2.0));
|
||||
}
|
||||
|
||||
static double tenlog(double x) {
|
||||
return 10.0 * log10(x);
|
||||
}
|
||||
|
||||
static double itenlog(double x) {
|
||||
return pow(10.0, x * 0.1);
|
||||
}
|
||||
|
||||
static double complex cpow_checked(const double complex a, const double complex b) {
|
||||
/* cpow() cannot be trusted to have as much precision as pow() even for integer arguments that fit in 32 bits */
|
||||
|
||||
if (!cimag(a) && !cimag(b)) {
|
||||
const double ar = creal(a), br = creal(b);
|
||||
if (rint(br) == br) return __builtin_powi(ar, br);
|
||||
else return pow(ar, br);
|
||||
}
|
||||
else return cpow(a, b);
|
||||
}
|
||||
|
||||
static unsigned long long gcd(unsigned long long a, unsigned long long b) {
|
||||
while (b) {
|
||||
const unsigned long long t = b;
|
||||
@ -66,29 +83,29 @@ static unsigned long long nchoosek(const unsigned long long n, const unsigned lo
|
||||
return n_choose_k;
|
||||
}
|
||||
|
||||
static double tenlog(double x) {
|
||||
return 10.0 * log10(x);
|
||||
static unsigned long long ceil_isqrt(unsigned long long n) {
|
||||
unsigned long long this = n / 2;
|
||||
if (!this) return n;
|
||||
|
||||
for (unsigned long long next = this; (next = (this + n / this) / 2) < this; this = next);
|
||||
|
||||
return this * this < n ? this + 1 : this;
|
||||
}
|
||||
|
||||
static double itenlog(double x) {
|
||||
return pow(10.0, x * 0.1);
|
||||
}
|
||||
static int isprime(const unsigned long long n) {
|
||||
if (n < 2 || !(n % 2)) return 0;
|
||||
|
||||
static double complex cpow_checked(const double complex a, const double complex b) {
|
||||
/* cpow() cannot be trusted to have as much precision as pow() even for integer arguments that fit in 32 bits */
|
||||
const unsigned long long stop = ceil_isqrt(n);
|
||||
|
||||
if (!cimag(a) && !cimag(b)) {
|
||||
if (rint(b) == b) return __builtin_powi(a, b);
|
||||
else return pow(a, b);
|
||||
}
|
||||
else return cpowl(a, b);
|
||||
}
|
||||
for (unsigned long long m = 3; m < stop; m += 2)
|
||||
if (!(n % m)) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* end simple math functions */
|
||||
|
||||
struct named_quantity {
|
||||
/* ok this is the only other time you will ever catch me using double complex */
|
||||
double complex value;
|
||||
double value;
|
||||
int8_t units[BASEUNITS]; /* metre, kilogram, second, ampere, kelvin, candela, mol */
|
||||
uint8_t flags;
|
||||
char * name;
|
||||
@ -299,7 +316,7 @@ static double datestr_to_unix_seconds(const char * const datestr) {
|
||||
char * after = NULL;
|
||||
microseconds_after_decimal = llrint(strtod(datestr, &after) * 1000000);
|
||||
if (after && *after != '\0')
|
||||
fprintf(stdout, "warning: %s: ignoring \"%s\"\n", __func__, after);
|
||||
fprintf(stderr, "warning: %s: ignoring \"%s\"\n", __func__, after);
|
||||
}
|
||||
|
||||
return (seconds * 1000000 + microseconds_after_decimal) * 1e-6;
|
||||
@ -360,7 +377,7 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
|
||||
|
||||
int units_out[BASEUNITS];
|
||||
for (size_t iu = 0; iu < BASEUNITS; iu++) {
|
||||
units_out[iu] = stack[S - 1].units[iu] + quantity->units[iu] * unit_exponent;
|
||||
units_out[iu] = (int8_t)(stack[S - 1].units[iu] + quantity->units[iu] * unit_exponent);
|
||||
if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW;
|
||||
}
|
||||
|
||||
@ -373,31 +390,6 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
|
||||
else return S;
|
||||
}
|
||||
|
||||
static int evaluate_one_argument_must_be_unitless(struct quantity * const stack, int S, double complex (* op)(double complex)) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
|
||||
stack[S - 1].value = op(stack[S - 1].value);
|
||||
return S;
|
||||
}
|
||||
|
||||
static int evaluate_one_argument_must_be_unitless_real(struct quantity * const stack, int S, double (* op)(double)) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
|
||||
stack[S - 1].value = op(stack[S - 1].value);
|
||||
return S;
|
||||
}
|
||||
|
||||
static int evaluate_one_argument_must_be_unitless_real_nonnegative(struct quantity * const stack, int S, double (* op)(double)) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (creal(stack[S - 1].value) < 0 || cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
|
||||
stack[S - 1].value = op(stack[S - 1].value);
|
||||
return S;
|
||||
}
|
||||
|
||||
static int evaluate_literal(struct quantity * stack, int S, const char * const token) {
|
||||
const int unit_ret = evaluate_unit(stack, S, token, 1);
|
||||
|
||||
@ -426,7 +418,12 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
|
||||
tmp.value = -I;
|
||||
else {
|
||||
char * endptr = NULL;
|
||||
tmp.value = strtod(token, &endptr);
|
||||
const double dv = strtod(token, &endptr);
|
||||
if (fabs(dv) >= (double)(1ULL << 53)) {
|
||||
const long long llv = strtoll(token, NULL, 10);
|
||||
if ((long long)dv != llv) return QRPN_ERROR_INEXACT_LITERAL;
|
||||
}
|
||||
tmp.value = dv;
|
||||
if (!strcmp(endptr, "i"))
|
||||
tmp.value *= I;
|
||||
else if (endptr == token)
|
||||
@ -451,6 +448,32 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
|
||||
return S + 1;
|
||||
}
|
||||
|
||||
static int evaluate_one_argument_must_be_unitless(struct quantity * const stack, int S, double complex (* op)(double complex)) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
|
||||
stack[S - 1].value = op(stack[S - 1].value);
|
||||
return S;
|
||||
}
|
||||
|
||||
static int evaluate_one_argument_must_be_unitless_real(struct quantity * const stack, int S, double (* op)(double)) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
|
||||
stack[S - 1].value = op(creal(stack[S - 1].value));
|
||||
return S;
|
||||
}
|
||||
|
||||
static int evaluate_one_argument_must_be_unitless_real_nonnegative(struct quantity * const stack, int S, double (* op)(double)) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (cimag(stack[S - 1].value) != 0) return QRPN_ERROR_MUST_BE_REAL;
|
||||
if (creal(stack[S - 1].value) < 0) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
|
||||
|
||||
stack[S - 1].value = op(creal(stack[S - 1].value));
|
||||
return S;
|
||||
}
|
||||
|
||||
int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const token) {
|
||||
if (!strcmp(token, "mul") || !strcmp(token, "*")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
@ -489,8 +512,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
|
||||
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
|
||||
const long long a = llrint(stack[S - 2].value);
|
||||
const long long b = llrint(stack[S - 1].value);
|
||||
const long long a = llrint(creal(stack[S - 2].value));
|
||||
const long long b = llrint(creal(stack[S - 1].value));
|
||||
if (!b) return QRPN_ERROR_DOMAIN;
|
||||
const long long c = a / b;
|
||||
stack[S - 2].value = c;
|
||||
@ -512,19 +535,23 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "mod") || !strcmp(token, "%")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
|
||||
stack[S - 2].value = fmod(stack[S - 2].value, stack[S - 1].value);
|
||||
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
|
||||
stack[S - 2].value = fmod(creal(stack[S - 2].value), creal(stack[S - 1].value));
|
||||
return S - 1;
|
||||
}
|
||||
else if (!strcmp(token, "hypot")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
|
||||
stack[S - 2].value = hypot(stack[S - 2].value, stack[S - 1].value);
|
||||
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
|
||||
|
||||
stack[S - 2].value = hypot(creal(stack[S - 2].value), creal(stack[S - 1].value));
|
||||
return S - 1;
|
||||
}
|
||||
else if (!strcmp(token, "atan2")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
|
||||
stack[S - 2].value = atan2(stack[S - 2].value, stack[S - 1].value);
|
||||
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
|
||||
stack[S - 2].value = atan2(creal(stack[S - 2].value), creal(stack[S - 1].value));
|
||||
memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS]));
|
||||
return S - 1;
|
||||
}
|
||||
@ -551,9 +578,10 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "choose")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
|
||||
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
|
||||
|
||||
const unsigned long long n = (unsigned long long)llrint(stack[S - 2].value);
|
||||
const unsigned long long k = (unsigned long long)llrint(stack[S - 1].value);
|
||||
const unsigned long long n = (unsigned long long)llrint(creal(stack[S - 2].value));
|
||||
const unsigned long long k = (unsigned long long)llrint(creal(stack[S - 1].value));
|
||||
if ((double)n != stack[S - 2].value ||
|
||||
(double)k != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
|
||||
stack[S - 2].value = nchoosek(n, k);
|
||||
@ -563,9 +591,11 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "gcd")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
|
||||
if (creal(stack[S - 2].value) < 0 || creal(stack[S - 1].value) < 0 ) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
|
||||
|
||||
const unsigned long long a = (unsigned long long)llrint(stack[S - 2].value);
|
||||
const unsigned long long b = (unsigned long long)lrint(stack[S - 1].value);
|
||||
const unsigned long long a = (unsigned long long)llrint(creal(stack[S - 2].value));
|
||||
const unsigned long long b = (unsigned long long)llrint(creal(stack[S - 1].value));
|
||||
if ((double)a != stack[S - 2].value ||
|
||||
(double)b != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
|
||||
stack[S - 2].value = gcd(a, b);
|
||||
@ -574,9 +604,11 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "lcm")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (cimag(stack[S - 2].value) || cimag(stack[S - 1].value) ) return QRPN_ERROR_MUST_BE_REAL;
|
||||
if (creal(stack[S - 2].value) < 0 || creal(stack[S - 1].value) < 0 ) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
|
||||
|
||||
const unsigned long long a = (unsigned long long)llrint(stack[S - 2].value);
|
||||
const unsigned long long b = (unsigned long long)lrint(stack[S - 1].value);
|
||||
const unsigned long long a = (unsigned long long)llrint(creal(stack[S - 2].value));
|
||||
const unsigned long long b = (unsigned long long)llrint(creal(stack[S - 1].value));
|
||||
if ((double)a != stack[S - 2].value ||
|
||||
(double)b != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
|
||||
|
||||
@ -585,31 +617,19 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
}
|
||||
else if (!strcmp(token, "isprime")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (creal(stack[S - 1].value) < 0 || cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
if (cimag(stack[S - 1].value) != 0) return QRPN_ERROR_MUST_BE_REAL;
|
||||
if (creal(stack[S - 1].value) < 0) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
const unsigned long long x = (unsigned long long)llrint(stack[S - 1].value);
|
||||
if ((double)x != (double)creal(stack[S - 1].value)) {
|
||||
fprintf(stdout, "%s: %.17g %.17g\n", __func__, (double)x, (double)creal(stack[S - 1].value));
|
||||
return QRPN_ERROR_MUST_BE_INTEGER;
|
||||
}
|
||||
if (x < 2) stack[S - 1].value = 0;
|
||||
else {
|
||||
char isprime = 1;
|
||||
for (unsigned long long y = 2; y * y <= x; y++) {
|
||||
if (!(x % y)) {
|
||||
isprime = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
stack[S - 1].value = isprime;
|
||||
}
|
||||
const unsigned long long x = (unsigned long long)llrint(creal(stack[S - 1].value));
|
||||
if (x > 1ULL << 53) return QRPN_ERROR_DOMAIN;
|
||||
if ((double)x != (double)creal(stack[S - 1].value)) return QRPN_ERROR_MUST_BE_INTEGER;
|
||||
|
||||
stack[S - 1].value = isprime(x);
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "swap")) {
|
||||
struct quantity tmp;
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
tmp = stack[S - 1];
|
||||
struct quantity tmp = stack[S - 1];
|
||||
stack[S - 1] = stack[S - 2];
|
||||
stack[S - 2] = tmp;
|
||||
return S;
|
||||
@ -630,6 +650,13 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
stack[S] = stack[S - 2];
|
||||
return S + 1;
|
||||
}
|
||||
else if (!strcmp(token, "2over")) {
|
||||
if (S < 4) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (S + 2 > QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
||||
stack[S] = stack[S - 4];
|
||||
stack[S + 1] = stack[S - 3];
|
||||
return S + 2;
|
||||
}
|
||||
else if (!strcmp(token, "and")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
@ -662,13 +689,13 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "le") || !strcmp(token, "lt") || !strcmp(token, "ge") || !strcmp(token, "gt")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
|
||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
if (cimag(stack[S - 1].value) || cimag(stack[S - 2].value) ) return QRPN_ERROR_DOMAIN;
|
||||
|
||||
memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS]));
|
||||
if (!strcmp(token, "le")) stack[S - 2].value = creal(stack[S - 2].value) <= creal(stack[S - 1].value);
|
||||
else if (!strcmp(token, "lt")) stack[S - 2].value = creal(stack[S - 2].value) < creal(stack[S - 1].value);
|
||||
else if (!strcmp(token, "ge")) stack[S - 2].value = creal(stack[S - 2].value) >= creal(stack[S - 1].value);
|
||||
else if (!strcmp(token, "gt")) stack[S - 2].value = creal(stack[S - 2].value) > creal(stack[S - 1].value);
|
||||
memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS]));
|
||||
return S - 1;
|
||||
}
|
||||
else if (!strcmp(token, "quadratic")) {
|
||||
@ -704,9 +731,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
return S - 1;
|
||||
}
|
||||
else if (!strcmp(token, "rot")) {
|
||||
struct quantity tmp;
|
||||
if (S < 3) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
tmp = stack[S - 3];
|
||||
struct quantity tmp = stack[S - 3];
|
||||
stack[S - 3] = stack[S - 2];
|
||||
stack[S - 2] = stack[S - 1];
|
||||
stack[S - 1] = tmp;
|
||||
@ -715,11 +741,12 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "pow")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
|
||||
if (units_are_dimensionless(stack[S - 2].units))
|
||||
stack[S - 2].value = cpow_checked(stack[S - 2].value, stack[S - 1].value);
|
||||
stack[S - 2].value = cpow_checked(stack[S - 2].value, creal(stack[S - 1].value));
|
||||
else {
|
||||
const long ipowarg = lrint(stack[S - 1].value);
|
||||
const long ipowarg = lrint(creal(stack[S - 1].value));
|
||||
if ((double)ipowarg != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
|
||||
|
||||
long long units_out[BASEUNITS];
|
||||
@ -738,11 +765,12 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "rpow")) {
|
||||
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
|
||||
if (units_are_dimensionless(stack[S - 2].units))
|
||||
stack[S - 2].value = cpow_checked(stack[S - 2].value, 1.0 / stack[S - 1].value);
|
||||
else {
|
||||
const long long ipowarg = llrint(stack[S - 1].value);
|
||||
const long long ipowarg = llrint(creal(stack[S - 1].value));
|
||||
if ((double)ipowarg != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
|
||||
for (size_t iu = 0; iu < BASEUNITS; iu++)
|
||||
if ((stack[S - 2].units[iu] / ipowarg) * ipowarg != stack[S - 2].units[iu])
|
||||
@ -756,7 +784,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "gamma")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
stack[S - 1].value = tgamma(stack[S - 1].value);
|
||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
stack[S - 1].value = tgamma(creal(stack[S - 1].value));
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "br")) {
|
||||
@ -765,9 +794,13 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
!units_are_dimensionless(stack[S - 2].units) ||
|
||||
!units_are_dimensionless(stack[S - 3].units) ||
|
||||
!units_are_dimensionless(stack[S - 4].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (cimag(stack[S - 1].value) ||
|
||||
cimag(stack[S - 2].value) ||
|
||||
cimag(stack[S - 3].value) ||
|
||||
cimag(stack[S - 4].value)) return QRPN_ERROR_DOMAIN;
|
||||
|
||||
const double a[2] = { stack[S - 4].value, stack[S - 3].value };
|
||||
const double b[2] = { stack[S - 2].value, stack[S - 1].value };
|
||||
const double a[2] = { creal(stack[S - 4].value), creal(stack[S - 3].value) };
|
||||
const double b[2] = { creal(stack[S - 2].value), creal(stack[S - 1].value) };
|
||||
const double d[2] = { b[0] - a[0], b[1] - a[1] };
|
||||
double bearing = 0, range = 0;
|
||||
if (d[0] || d[1]) {
|
||||
@ -794,10 +827,14 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
!units_are_dimensionless(stack[S - 2].units) ||
|
||||
!units_are_dimensionless(stack[S - 3].units) ||
|
||||
!units_are_dimensionless(stack[S - 4].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
|
||||
if (cimag(stack[S - 1].value) ||
|
||||
cimag(stack[S - 2].value) ||
|
||||
cimag(stack[S - 3].value) ||
|
||||
cimag(stack[S - 4].value)) return QRPN_ERROR_DOMAIN;
|
||||
|
||||
/* todo */
|
||||
const double in[2] = { stack[S - 4].value, stack[S - 3].value };
|
||||
const double bearing = stack[S - 2].value, range = stack[S - 1].value;
|
||||
const double in[2] = { creal(stack[S - 4].value), creal(stack[S - 3].value) };
|
||||
const double bearing = creal(stack[S - 2].value), range = creal(stack[S - 1].value);
|
||||
/* range, bearing, and declination of start point */
|
||||
const double a = range, B = bearing, c = M_PI_2 - in[1];
|
||||
/* declination of endpoint */
|
||||
@ -814,27 +851,29 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
}
|
||||
else if (!strcmp(token, "nextafter")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
stack[S - 1].value = nextafter(stack[S - 1].value, DBL_MAX);
|
||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
stack[S - 1].value = nextafter(creal(stack[S - 1].value), DBL_MAX);
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "nextafterf")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
stack[S - 1].value = nextafterf(stack[S - 1].value, FLT_MAX);
|
||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
stack[S - 1].value = nextafterf(crealf(stack[S - 1].value), FLT_MAX);
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "arg")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
stack[S - 1].value = cargf(stack[S - 1].value);
|
||||
stack[S - 1].value = carg(stack[S - 1].value);
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "real")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
stack[S - 1].value = crealf(stack[S - 1].value);
|
||||
stack[S - 1].value = creal(stack[S - 1].value);
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "imaginary")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
stack[S - 1].value = cimagf(stack[S - 1].value);
|
||||
stack[S - 1].value = cimag(stack[S - 1].value);
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "hav")) return evaluate_one_argument_must_be_unitless_real(stack, S, hav);
|
||||
@ -886,9 +925,10 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
else if (!strcmp(token, "date")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
if (!units_are_equivalent(stack[S - 1].units, units_of_time)) return QRPN_ERROR_INCONSISTENT_UNITS;
|
||||
if (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
|
||||
/* year, month, day, hour, minute, second */
|
||||
time_t unixtime = floor(stack[S - 1].value);
|
||||
const double remainder = stack[S - 1].value - unixtime;
|
||||
time_t unixtime = floor(creal(stack[S - 1].value));
|
||||
const double remainder = creal(stack[S - 1].value) - unixtime;
|
||||
struct tm unixtime_struct;
|
||||
gmtime_r(&unixtime, &unixtime_struct);
|
||||
if (S + 5 > QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
|
||||
@ -917,13 +957,19 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
|
||||
}
|
||||
return S;
|
||||
}
|
||||
else if (!strcmp(token, "print")) {
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
fprintf_quantity(stderr, stack[S - 1]);
|
||||
fprintf(stderr, "\n");
|
||||
return S;
|
||||
}
|
||||
else
|
||||
return evaluate_literal(stack, S, token);
|
||||
}
|
||||
|
||||
static void fprintf_value(FILE * fh, const double complex value) {
|
||||
if (creal(value) >= 1e6 && !cimag(value))
|
||||
fprintf(fh, "%.15g", creal(value));
|
||||
if (fabs(creal(value)) >= 1e6 && !cimag(value))
|
||||
fprintf(fh, "%.18g", creal(value));
|
||||
|
||||
else if ((!creal(value) && cimag(value)) || fabs(creal(value)) * 1e14 < fabs(cimag(value))) {
|
||||
if (1.0 == cimag(value))
|
||||
@ -1005,83 +1051,66 @@ void fprintf_quantity(FILE * fh, const struct quantity quantity) {
|
||||
}
|
||||
|
||||
void fprintf_stack(FILE * fh, struct quantity * stack, const int S) {
|
||||
if (!S) fprintf(stdout, "[stack is empty]");
|
||||
if (!S) fprintf(fh, "[stack is empty]");
|
||||
else for (int is = 0; is < S; is++) {
|
||||
if (S > 1) fprintf(fh, "[");
|
||||
fprintf_quantity(fh, stack[is]);
|
||||
if (S > 1) fprintf(fh, "]");
|
||||
if (is + 1 < S) fprintf(fh, " ");
|
||||
if (is + 1 < S) fprintf(fh, ", ");
|
||||
}
|
||||
}
|
||||
|
||||
int qrpn_try_token(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const token) {
|
||||
/* ideally we would have a strong guarantee that qrpn_evaluate_token would not mutate the input if it would result in an error */
|
||||
struct quantity stack_copy[QRPN_STACK_SIZE_MAX];
|
||||
memcpy(stack_copy, stack, sizeof(struct quantity) * S);
|
||||
|
||||
return qrpn_evaluate_token(stack_copy, S, token);
|
||||
}
|
||||
|
||||
char * qrpn_strerror(const int status) {
|
||||
if (status >= 0) return "success";
|
||||
else if (QRPN_ERROR_TOKEN_UNRECOGNIZED == status) return "unrecognized";
|
||||
else if (QRPN_ERROR_NOT_ENOUGH_STACK == status) return "not enough args";
|
||||
else if (QRPN_ERROR_INCONSISTENT_UNITS == status) return "inconsistent units";
|
||||
else if (QRPN_ERROR_MUST_BE_INTEGER == status) return "arg must be integer";
|
||||
else if (QRPN_ERROR_MUST_BE_UNITLESS == status) return "arg must be unitless";
|
||||
else if (QRPN_ERROR_TOKEN_UNRECOGNIZED == status) return "unrecognized";
|
||||
else if (QRPN_ERROR_MUST_BE_REAL == status) return "arg must be real-valued";
|
||||
else if (QRPN_ERROR_MUST_BE_NONNEGATIVE == status) return "arg must be nonnegative";
|
||||
else if (QRPN_ERROR_RATIONAL_NOT_IMPLEMENTED == status) return "noninteger units";
|
||||
else if (QRPN_ERROR_DOMAIN == status) return "domain error";
|
||||
else if (QRPN_ERROR_DIMENSION_OVERFLOW == status) return "dimension overflow";
|
||||
else if (QRPN_ERROR_TOO_MUCH_STACK == status) return "insufficient stack space";
|
||||
else if (QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT == status) return "unmatched control statement";
|
||||
else if (QRPN_ERROR_INEXACT_LITERAL == status) return "unrepresentable literal";
|
||||
else return "undefined error";
|
||||
}
|
||||
|
||||
enum control_statement { ELSE_OR_ENDIF, ENDIF, UNTIL_OR_WHILE, REPEAT };
|
||||
enum control_statement { NONE, ELSE_OR_ENDIF, ENDIF, UNTIL_OR_WHILE, REPEAT };
|
||||
|
||||
const char ** find_matching_control_statement(const char ** tp, const enum control_statement looking_for) {
|
||||
for (const char * token; (token = *tp); ) {
|
||||
if (!strcmp(token, "until") || !strcmp(token, "while")) {
|
||||
if (looking_for == UNTIL_OR_WHILE) return tp;
|
||||
else return NULL;
|
||||
}
|
||||
else if (!strcmp(token, "repeat")) {
|
||||
if (looking_for == REPEAT) return tp;
|
||||
else return NULL;
|
||||
}
|
||||
else if (!strcmp(token, "else") || !strcmp(token, "endif")) {
|
||||
if (looking_for == ELSE_OR_ENDIF || looking_for == ENDIF) return tp;
|
||||
else return NULL;
|
||||
}
|
||||
/* used to skip over branches not taken */
|
||||
for (const char * token; (token = *tp); tp++) {
|
||||
if (!strcmp(token, "until") || !strcmp(token, "while"))
|
||||
return UNTIL_OR_WHILE == looking_for ? tp : NULL;
|
||||
else if (!strcmp(token, "repeat"))
|
||||
return REPEAT == looking_for ? tp : NULL;
|
||||
else if (!strcmp(token, "else") || !strcmp(token, "endif"))
|
||||
return ELSE_OR_ENDIF == looking_for || ENDIF == looking_for ? tp : NULL;
|
||||
else if (!strcmp(token, "if")) {
|
||||
const char ** tpc = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF);
|
||||
if (!tpc) return NULL;
|
||||
else if (!strcmp(*tpc, "else")) {
|
||||
tpc = find_matching_control_statement(tpc + 1, ENDIF);
|
||||
if (!tpc) return NULL;
|
||||
tp = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF);
|
||||
if (!tp) return NULL;
|
||||
else if (!strcmp(*tp, "else")) {
|
||||
tp = find_matching_control_statement(tp + 1, ENDIF);
|
||||
if (!tp) return NULL;
|
||||
}
|
||||
tp = tpc + 1;
|
||||
}
|
||||
else if (!strcmp(token, "begin")) {
|
||||
const char ** tpc = find_matching_control_statement(tp + 1, UNTIL_OR_WHILE);
|
||||
if (!tpc) return NULL;
|
||||
else if (!strcmp(*tpc, "while")) {
|
||||
tpc = find_matching_control_statement(tpc + 1, REPEAT);
|
||||
if (!tpc) return NULL;
|
||||
tp = find_matching_control_statement(tp + 1, UNTIL_OR_WHILE);
|
||||
if (!tp) return NULL;
|
||||
else if (!strcmp(*tp, "while")) {
|
||||
tp = find_matching_control_statement(tp + 1, REPEAT);
|
||||
if (!tp) return NULL;
|
||||
}
|
||||
tp = tpc + 1;
|
||||
}
|
||||
else tp++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** const tokens, const char return_on_block_end) {
|
||||
for (const char ** tp = tokens, * token; (token = *tp); ) {
|
||||
if (!strcmp(token, "else") || !strcmp(token, "endif") || !strcmp(token, "until") || !strcmp(token, "while") || !strcmp(token, "repeat")) {
|
||||
if (return_on_block_end) return S;
|
||||
else return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
|
||||
}
|
||||
int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** const tokens, const size_t nest_level) {
|
||||
for (const char ** tp = tokens, * token; (token = *tp); tp++) {
|
||||
if (!strcmp(token, "else") || !strcmp(token, "endif") || !strcmp(token, "until") || !strcmp(token, "while") || !strcmp(token, "repeat"))
|
||||
return nest_level ? S : QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
|
||||
else if (!strcmp(token, "begin")) {
|
||||
const char ** tp_until_or_while = find_matching_control_statement(tp + 1, UNTIL_OR_WHILE);
|
||||
if (!tp_until_or_while) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
|
||||
@ -1091,7 +1120,7 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
|
||||
if (!tp_repeat) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
|
||||
|
||||
while (1) {
|
||||
S = qrpn_evaluate_tokens(stack, S, tp + 1, 1);
|
||||
S = qrpn_evaluate_tokens(stack, S, tp + 1, nest_level + 1);
|
||||
if (S < 0) return S;
|
||||
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
@ -1100,16 +1129,14 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
|
||||
S--;
|
||||
if (!stack[S].value) break;
|
||||
|
||||
S = qrpn_evaluate_tokens(stack, S, tp_while + 1, 1);
|
||||
S = qrpn_evaluate_tokens(stack, S, tp_while + 1, nest_level + 1);
|
||||
}
|
||||
|
||||
tp = tp_repeat + 1;
|
||||
tp = tp_repeat;
|
||||
}
|
||||
else {
|
||||
const char ** tp_until = tp_until_or_while;
|
||||
|
||||
do {
|
||||
S = qrpn_evaluate_tokens(stack, S, tp + 1, 1);
|
||||
S = qrpn_evaluate_tokens(stack, S, tp + 1, nest_level + 1);
|
||||
if (S < 0) return S;
|
||||
|
||||
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
|
||||
@ -1118,7 +1145,7 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
|
||||
S--;
|
||||
} while (!stack[S].value);
|
||||
|
||||
tp = tp_until + 1;
|
||||
tp = tp_until_or_while;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(token, "if")) {
|
||||
@ -1136,32 +1163,66 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
|
||||
}
|
||||
else tp_endif = tp_else_or_endif;
|
||||
|
||||
/* choose which branch to take */
|
||||
const char ** tp_branch = stack[S - 1].value ? tp : tp_else;
|
||||
S--;
|
||||
|
||||
if (stack[S].value)
|
||||
S = qrpn_evaluate_tokens(stack, S, tp + 1, 1);
|
||||
else if (tp_else)
|
||||
S = qrpn_evaluate_tokens(stack, S, tp_else + 1, 1);
|
||||
S = qrpn_evaluate_tokens(stack, S, tp_branch + 1, nest_level + 1);
|
||||
|
||||
tp = tp_endif + 1;
|
||||
tp = tp_endif;
|
||||
}
|
||||
else {
|
||||
S = qrpn_evaluate_token(stack, S, token);
|
||||
if (S < 0) return S;
|
||||
|
||||
tp++;
|
||||
}
|
||||
}
|
||||
return S;
|
||||
}
|
||||
|
||||
int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string) {
|
||||
const char ** tokens = NULL;
|
||||
size_t T = 0;
|
||||
|
||||
char * const copy = strdup(string);
|
||||
for (char * token, * p = copy; (token = strsep(&p, " ")); ) {
|
||||
T++;
|
||||
tokens = realloc(tokens, sizeof(char *) * (T + 1));
|
||||
tokens[T - 1] = token;
|
||||
}
|
||||
|
||||
tokens[T] = NULL;
|
||||
|
||||
S = qrpn_evaluate_tokens(stack, S, tokens, 0);
|
||||
|
||||
free(tokens);
|
||||
free(copy);
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
int qrpn_try_token(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const token) {
|
||||
/* ideally we would have a strong guarantee that qrpn_evaluate_token would not mutate the input if it would result in an error */
|
||||
struct quantity stack_copy[QRPN_STACK_SIZE_MAX];
|
||||
memcpy(stack_copy, stack, sizeof(struct quantity) * S);
|
||||
|
||||
return qrpn_evaluate_token(stack_copy, S, token);
|
||||
}
|
||||
|
||||
int qrpn_try_string(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const string) {
|
||||
/* ideally we would have a strong guarantee that qrpn_evaluate_token would not mutate the input if it would result in an error */
|
||||
struct quantity stack_copy[QRPN_STACK_SIZE_MAX];
|
||||
memcpy(stack_copy, stack, sizeof(struct quantity) * S);
|
||||
|
||||
return qrpn_evaluate_string(stack_copy, S, string);
|
||||
}
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/* if no other main() is linked, this one will be, and provides a simple command line interface */
|
||||
__attribute((weak)) int main(const int argc, const char ** const argv) {
|
||||
if (isatty(STDIN_FILENO) && argc < 2) {
|
||||
/* never reached */
|
||||
fprintf(stdout, "%s: Evaluates an RPN expression with units\n", argv[0]);
|
||||
fprintf(stderr, "%s: Evaluates an RPN expression with units\n", argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -1170,7 +1231,7 @@ __attribute((weak)) int main(const int argc, const char ** const argv) {
|
||||
int S = qrpn_evaluate_tokens(stack, 0, argv + 1, 0);
|
||||
|
||||
if (S < 0) {
|
||||
fprintf(stdout, "error: %s\n", qrpn_strerror(S));
|
||||
fprintf(stderr, "error: %s\n", qrpn_strerror(S));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,10 @@ int qrpn_evaluate_token(struct quantity * stack, int S, const char * const token
|
||||
|
||||
/* same, but operate on a temporary copy of the input and do not mutate the original */
|
||||
int qrpn_try_token(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const token);
|
||||
int qrpn_try_string(const struct quantity stack[static QRPN_STACK_SIZE_MAX], const int S, const char * const string);
|
||||
|
||||
int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string);
|
||||
|
||||
|
||||
/* given the value returned from a function above, return a pointer to a string literal */
|
||||
char * qrpn_strerror(const int status);
|
||||
@ -27,15 +31,17 @@ char * qrpn_strerror(const int status);
|
||||
void fprintf_stack(FILE * fh, struct quantity * stack, const int S);
|
||||
void fprintf_quantity(FILE * fh, const struct quantity quantity);
|
||||
|
||||
/* status codes returned by qrpn_evaluate_token */
|
||||
#define QRPN_ERROR_NOT_ENOUGH_STACK -1
|
||||
#define QRPN_ERROR_INCONSISTENT_UNITS -2
|
||||
#define QRPN_ERROR_MUST_BE_INTEGER -3
|
||||
#define QRPN_ERROR_TOKEN_UNRECOGNIZED -4
|
||||
#define QRPN_ERROR_RATIONAL_NOT_IMPLEMENTED -5
|
||||
#define QRPN_ERROR_MUST_BE_UNITLESS -6
|
||||
#define QRPN_ERROR_DOMAIN -7
|
||||
#define QRPN_ERROR_DIMENSION_OVERFLOW -8
|
||||
#define QRPN_ERROR_TOO_MUCH_STACK -9
|
||||
#define QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT -10
|
||||
#define QRPN_NOT_A_UNIT -11
|
||||
#define QRPN_ERROR_TOKEN_UNRECOGNIZED -1
|
||||
#define QRPN_ERROR_NOT_ENOUGH_STACK -2
|
||||
#define QRPN_ERROR_INCONSISTENT_UNITS -3
|
||||
#define QRPN_ERROR_MUST_BE_INTEGER -4
|
||||
#define QRPN_ERROR_MUST_BE_UNITLESS -5
|
||||
#define QRPN_ERROR_MUST_BE_REAL -6
|
||||
#define QRPN_ERROR_MUST_BE_NONNEGATIVE -7
|
||||
#define QRPN_ERROR_RATIONAL_NOT_IMPLEMENTED -8
|
||||
#define QRPN_ERROR_DOMAIN -9
|
||||
#define QRPN_ERROR_DIMENSION_OVERFLOW -10
|
||||
#define QRPN_ERROR_TOO_MUCH_STACK -11
|
||||
#define QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT -12
|
||||
#define QRPN_ERROR_INEXACT_LITERAL -13
|
||||
#define QRPN_NOT_A_UNIT -14
|
||||
|
Loading…
Reference in New Issue
Block a user