3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-11-19 10:29:30 +01:00

modules/qrpn: replace stderr with stdout; remove extraneous whitespace throughout

PBot modules do not send the stderr stream to the channel. Instead, the output
is logged in <module-filename>-stderr, for private debugging purposes. PBot
expects output intended for channels to be on stdout.
This commit is contained in:
Pragmatic Software 2021-11-14 09:18:47 -08:00
parent 9172bcd29f
commit da7d31229b

View File

@ -70,7 +70,7 @@ static unsigned long long gcd(unsigned long long a, unsigned long long b) {
b = a % b; b = a % b;
a = t; a = t;
} }
return a; return a;
} }
@ -79,7 +79,7 @@ static unsigned long long nchoosek(const unsigned long long n, const unsigned lo
unsigned long long n_choose_k = n * (n - 1) / 2; unsigned long long n_choose_k = n * (n - 1) / 2;
for (size_t kr = 3; kr <= k; kr++) for (size_t kr = 3; kr <= k; kr++)
n_choose_k *= (n + 1 - kr) / kr; n_choose_k *= (n + 1 - kr) / kr;
return n_choose_k; return n_choose_k;
} }
@ -88,15 +88,15 @@ static unsigned long long ceil_isqrt(unsigned long long n) {
if (!this) return n; if (!this) return n;
for (unsigned long long next = this; (next = (this + n / this) / 2) < this; this = next); for (unsigned long long next = this; (next = (this + n / this) / 2) < this; this = next);
return this * this < n ? this + 1 : this; return this * this < n ? this + 1 : this;
} }
static int isprime(const unsigned long long n) { static int isprime(const unsigned long long n) {
if (n < 2 || !(n % 2)) return 0; if (n < 2 || !(n % 2)) return 0;
const unsigned long long stop = ceil_isqrt(n); const unsigned long long stop = ceil_isqrt(n);
for (unsigned long long m = 3; m < stop; m += 2) for (unsigned long long m = 3; m < stop; m += 2)
if (!(n % m)) return 0; if (!(n % m)) return 0;
@ -158,7 +158,7 @@ static const struct named_quantity named_quantities[] = {
{ .value = 1.0, .units = { 0, 0, 0, 0, 1, 0, 0 }, .name = "kelvin", .abrv = "K", .flags = FLAG_SI_BASE_UNIT }, { .value = 1.0, .units = { 0, 0, 0, 0, 1, 0, 0 }, .name = "kelvin", .abrv = "K", .flags = FLAG_SI_BASE_UNIT },
{ .value = 1.0, .units = { 0, 0, 0, 0, 0, 1, 0 }, .name = "candela", .abrv = "Cd", .flags = FLAG_SI_BASE_UNIT }, { .value = 1.0, .units = { 0, 0, 0, 0, 0, 1, 0 }, .name = "candela", .abrv = "Cd", .flags = FLAG_SI_BASE_UNIT },
{ .value = 1.0, .units = { 0, 0, 0, 0, 0, 0, 1 }, .name = "mole", .abrv = "mol", .flags = FLAG_SI_BASE_UNIT }, { .value = 1.0, .units = { 0, 0, 0, 0, 0, 0, 1 }, .name = "mole", .abrv = "mol", .flags = FLAG_SI_BASE_UNIT },
/* si derived units */ /* si derived units */
{ .value = 1.0, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "hertz", .abrv = "Hz", .flags = FLAG_SI_DERIVED_UNIT }, { .value = 1.0, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "hertz", .abrv = "Hz", .flags = FLAG_SI_DERIVED_UNIT },
{ .value = 1.0, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "newton", .abrv = "N", .flags = FLAG_SI_DERIVED_UNIT }, { .value = 1.0, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "newton", .abrv = "N", .flags = FLAG_SI_DERIVED_UNIT },
@ -182,7 +182,7 @@ static const struct named_quantity named_quantities[] = {
// { .value = 1.0, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "gray", .abrv = "Gy" }, // { .value = 1.0, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "gray", .abrv = "Gy" },
{ .value = 100e3, { -1, 1, -2, 0, 0, 0, 0 }, .name = "bar" }, { .value = 100e3, { -1, 1, -2, 0, 0, 0, 0 }, .name = "bar" },
{ .value = 60.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "minute", .abrv = "min" }, { .value = 60.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "minute", .abrv = "min" },
{ .value = 3600.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "hour", .abrv = "h" }, { .value = 3600.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "hour", .abrv = "h" },
{ .value = 86400.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "day" }, { .value = 86400.0, .units = { 0, 0, 1, 0, 0, 0, 0 }, .name = "day" },
@ -201,9 +201,9 @@ static const struct named_quantity named_quantities[] = {
{ .value = 3600.0, .units = { 0, 0, 1, 1, 0, 0, 0 }, .abrv = "Ah" }, { .value = 3600.0, .units = { 0, 0, 1, 1, 0, 0, 0 }, .abrv = "Ah" },
{ .value = 1.0e-2, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "rad" }, { .value = 1.0e-2, .units = { 2, 0, -2, 0, 0, 0, 0 }, .name = "rad" },
{ .value = 10e-6, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "dyne" }, { .value = 10e-6, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "dyne" },
{ .value = 3.7e10, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "curie", .abrv = "Ci" }, { .value = 3.7e10, .units = { 0, 0, -1, 0, 0, 0, 0 }, .name = "curie", .abrv = "Ci" },
{ .value = 4.92892159375e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "teaspoon", .abrv = "tsp" }, { .value = 4.92892159375e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "teaspoon", .abrv = "tsp" },
{ .value = 14.78676478125e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "tablespoon", .abrv = "Tbsp" }, { .value = 14.78676478125e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "tablespoon", .abrv = "Tbsp" },
{ .value = 29.5735295625e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "floz" }, { .value = 29.5735295625e-6, .units = { 3, 0, 0, 0, 0, 0, 0 }, .name = "floz" },
@ -249,13 +249,13 @@ static const struct named_quantity named_quantities[] = {
{ .value = 101.325e3, .units = { -1, 1, -2, 0, 0, 0, 0 }, .name = "atmosphere", .abrv = "atm" }, { .value = 101.325e3, .units = { -1, 1, -2, 0, 0, 0, 0 }, .name = "atmosphere", .abrv = "atm" },
{ .value = 745.699872, .units = { 2, 1, -3, 0, 0, 0, 0 }, .name = "horsepower", .abrv = "hp" }, { .value = 745.699872, .units = { 2, 1, -3, 0, 0, 0, 0 }, .name = "horsepower", .abrv = "hp" },
{ .value = 0.3048 * 6.0, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "fathom" }, { .value = 0.3048 * 6.0, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "fathom" },
{ .value = 0.0254, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "inch", .abrv = "in" }, { .value = 0.0254, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "inch", .abrv = "in" },
{ .value = 0.3048, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "foot", .abrv = "ft" }, { .value = 0.3048, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "foot", .abrv = "ft" },
{ .value = 0.9144, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "yard", .abrv = "yd" }, { .value = 0.9144, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "yard", .abrv = "yd" },
{ .value = 201.168, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "furlong" }, { .value = 201.168, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "furlong" },
{ .value = 3.08567758e16, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "parsec", .abrv = "pc" }, { .value = 3.08567758e16, .units = { 1, 0, 0, 0, 0, 0, 0 }, .name = "parsec", .abrv = "pc" },
{ .value = 0.45359237, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "lbm" }, { .value = 0.45359237, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "lbm" },
{ .value = 4.448222, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "lbf" }, { .value = 4.448222, .units = { 1, 1, -2, 0, 0, 0, 0 }, .name = "lbf" },
{ .value = 6.35029318, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "stone", .abrv = "st" }, { .value = 6.35029318, .units = { 0, 1, 0, 0, 0, 0, 0 }, .name = "stone", .abrv = "st" },
@ -271,7 +271,7 @@ static int units_are_power_of(const struct quantity * const test, const struct n
break; break;
} }
if (!exponent) return 0; if (!exponent) return 0;
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
if (test->units[iu] != base->units[iu] * exponent) return 0; if (test->units[iu] != base->units[iu] * exponent) return 0;
@ -288,11 +288,11 @@ static int units_are_dimensionless(const int8_t in[BASEUNITS]) {
static double datestr_to_unix_seconds(const char * const datestr) { static double datestr_to_unix_seconds(const char * const datestr) {
int64_t seconds = 0, microseconds_after_decimal = 0; int64_t seconds = 0, microseconds_after_decimal = 0;
if (strchr(datestr, 'T') && strchr(datestr, 'Z')) { if (strchr(datestr, 'T') && strchr(datestr, 'Z')) {
/* if input is a date string */ /* if input is a date string */
struct tm unixtime_struct = { 0 }; struct tm unixtime_struct = { 0 };
/* if input has colons and dashes, and a subsecond portion... */ /* if input has colons and dashes, and a subsecond portion... */
if (strchr(datestr, '-') && strchr(datestr, ':') && strchr(datestr, '.')) { if (strchr(datestr, '-') && strchr(datestr, ':') && strchr(datestr, '.')) {
const uint64_t microseconds_remainder_with_integer = strtod(datestr + 18, NULL) * 1000000; const uint64_t microseconds_remainder_with_integer = strtod(datestr + 18, NULL) * 1000000;
@ -309,46 +309,46 @@ static double datestr_to_unix_seconds(const char * const datestr) {
} }
else else
strptime(datestr, "%Y%m%dT%H%M%SZ", &unixtime_struct); strptime(datestr, "%Y%m%dT%H%M%SZ", &unixtime_struct);
seconds = timegm(&unixtime_struct); seconds = timegm(&unixtime_struct);
} else { } else {
/* otherwise, input is a number */ /* otherwise, input is a number */
char * after = NULL; char * after = NULL;
microseconds_after_decimal = llrint(strtod(datestr, &after) * 1000000); microseconds_after_decimal = llrint(strtod(datestr, &after) * 1000000);
if (after && *after != '\0') if (after && *after != '\0')
fprintf(stderr, "warning: %s: ignoring \"%s\"\n", __func__, after); fprintf(stdout, "warning: %s: ignoring \"%s\"\n", __func__, after);
} }
return (seconds * 1000000 + microseconds_after_decimal) * 1e-6; return (seconds * 1000000 + microseconds_after_decimal) * 1e-6;
} }
static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int S, const char * const token, const int exponent_sign) { static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int S, const char * const token, const int exponent_sign) {
const char * slash = strchr(token, '/'); const char * slash = strchr(token, '/');
if (slash && exponent_sign < 0) return QRPN_ERROR_TOKEN_UNRECOGNIZED; if (slash && exponent_sign < 0) return QRPN_ERROR_TOKEN_UNRECOGNIZED;
const char * const carat = strrchr(token, '^'); const char * const carat = strrchr(token, '^');
const size_t bytes_before_carat = carat ? (size_t)(carat - token) : (slash ? (size_t)(slash - token) : strlen(token)); const size_t bytes_before_carat = carat ? (size_t)(carat - token) : (slash ? (size_t)(slash - token) : strlen(token));
const long long unit_exponent = exponent_sign * (carat ? strtoll(carat + 1, NULL, 10) : 1); const long long unit_exponent = exponent_sign * (carat ? strtoll(carat + 1, NULL, 10) : 1);
const struct named_quantity * quantity = NULL; const struct named_quantity * quantity = NULL;
const struct si_prefix * prefix = NULL; const struct si_prefix * prefix = NULL;
/* loop over all known units and prefixes looking for a match */ /* loop over all known units and prefixes looking for a match */
for (const struct named_quantity * possible_quantity = named_quantities; !quantity && possible_quantity < named_quantities + named_quantity_count; possible_quantity++) for (const struct named_quantity * possible_quantity = named_quantities; !quantity && possible_quantity < named_quantities + named_quantity_count; possible_quantity++)
/* loop over [full name, abbreviation, alt spelling] of each unit. this is a bit of a mess */ /* loop over [full name, abbreviation, alt spelling] of each unit. this is a bit of a mess */
for (int ipass = 0; !quantity && ipass < 3; ipass++) { for (int ipass = 0; !quantity && ipass < 3; ipass++) {
const char * const unit_name = 2 == ipass ? possible_quantity->alt_spelling : 1 == ipass ? possible_quantity->abrv : possible_quantity->name; const char * const unit_name = 2 == ipass ? possible_quantity->alt_spelling : 1 == ipass ? possible_quantity->abrv : possible_quantity->name;
if (!unit_name) continue; if (!unit_name) continue;
/* get number of bytes in unit name because we're gonna need it many times */ /* get number of bytes in unit name because we're gonna need it many times */
const size_t unit_len = strlen(unit_name); const size_t unit_len = strlen(unit_name);
if (bytes_before_carat < unit_len || memcmp(token + bytes_before_carat - unit_len, unit_name, unit_len)) continue; if (bytes_before_carat < unit_len || memcmp(token + bytes_before_carat - unit_len, unit_name, unit_len)) continue;
const size_t bytes_before_unit = bytes_before_carat - unit_len; const size_t bytes_before_unit = bytes_before_carat - unit_len;
prefix = NULL; prefix = NULL;
/* loop over known si prefixes */ /* loop over known si prefixes */
for (const struct si_prefix * possible_prefix = si_prefixes; !prefix && possible_prefix < si_prefixes + sizeof(si_prefixes) / sizeof(si_prefixes[0]); possible_prefix++) { for (const struct si_prefix * possible_prefix = si_prefixes; !prefix && possible_prefix < si_prefixes + sizeof(si_prefixes) / sizeof(si_prefixes[0]); possible_prefix++) {
/* if looking for SI unit abbreviations, admit SI prefix abbreviations */ /* if looking for SI unit abbreviations, admit SI prefix abbreviations */
@ -358,48 +358,48 @@ static int evaluate_unit(struct quantity stack[static QRPN_STACK_SIZE_MAX], int
if (bytes_before_unit == prefix_len && !memcmp(token, prefix_name, bytes_before_unit)) if (bytes_before_unit == prefix_len && !memcmp(token, prefix_name, bytes_before_unit))
prefix = possible_prefix; prefix = possible_prefix;
} }
/* if there were bytes before the unit but they didn't match any known prefix, then dont treat as a unit */ /* if there were bytes before the unit but they didn't match any known prefix, then dont treat as a unit */
if (!bytes_before_unit || prefix) if (!bytes_before_unit || prefix)
quantity = possible_quantity; quantity = possible_quantity;
} }
/* not finding anything above is only an error if there was both a numerator and denominator */ /* not finding anything above is only an error if there was both a numerator and denominator */
if (!quantity) return slash ? QRPN_ERROR_TOKEN_UNRECOGNIZED : QRPN_NOT_A_UNIT; if (!quantity) return slash ? QRPN_ERROR_TOKEN_UNRECOGNIZED : QRPN_NOT_A_UNIT;
if (quantity->flags & FLAG_UNIT_ENTERS_AS_OPERAND) { if (quantity->flags & FLAG_UNIT_ENTERS_AS_OPERAND) {
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK; if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
S++; S++;
stack[S - 1] = (struct quantity) { .value = 1 }; stack[S - 1] = (struct quantity) { .value = 1 };
} }
if (!S) return QRPN_ERROR_NOT_ENOUGH_STACK; if (!S) return QRPN_ERROR_NOT_ENOUGH_STACK;
int units_out[BASEUNITS]; int units_out[BASEUNITS];
for (size_t iu = 0; iu < BASEUNITS; iu++) { for (size_t iu = 0; iu < BASEUNITS; iu++) {
units_out[iu] = (int8_t)(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; if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW;
} }
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
stack[S - 1].units[iu] = units_out[iu]; stack[S - 1].units[iu] = units_out[iu];
stack[S - 1].value *= pow(prefix ? prefix->scale * quantity->value : quantity->value, unit_exponent); stack[S - 1].value *= pow(prefix ? prefix->scale * quantity->value : quantity->value, unit_exponent);
if (slash) return evaluate_unit(stack, S, slash + 1, -exponent_sign); if (slash) return evaluate_unit(stack, S, slash + 1, -exponent_sign);
else return S; else return S;
} }
static int evaluate_literal(struct quantity * stack, int S, const char * const token) { static int evaluate_literal(struct quantity * stack, int S, const char * const token) {
const int unit_ret = evaluate_unit(stack, S, token, 1); const int unit_ret = evaluate_unit(stack, S, token, 1);
/* if evaluate unit either errored or succeeded, we're done... */ /* if evaluate unit either errored or succeeded, we're done... */
if (unit_ret != QRPN_NOT_A_UNIT) return unit_ret; if (unit_ret != QRPN_NOT_A_UNIT) return unit_ret;
/* otherwise, token was not a unit name, parse it as a simple literal */ /* otherwise, token was not a unit name, parse it as a simple literal */
struct quantity tmp = { 0 }; struct quantity tmp = { 0 };
double d = 0, m = 0, s = 0; double d = 0, m = 0, s = 0;
if (strpbrk(token + 1, "") && sscanf(token, "%lf%*[d°]%lf%*[m']%lf%*[s\"]", &d, &m, &s)) { if (strpbrk(token + 1, "") && sscanf(token, "%lf%*[d°]%lf%*[m']%lf%*[s\"]", &d, &m, &s)) {
const double leading = strtod(token, NULL); const double leading = strtod(token, NULL);
tmp.value = copysign(fabs(d) + m / 60.0 + s / 3600.0, leading) * M_PI / 180.0; tmp.value = copysign(fabs(d) + m / 60.0 + s / 3600.0, leading) * M_PI / 180.0;
@ -434,15 +434,15 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
if ('k' == endptr[0]) prefix_scale = 1e3; if ('k' == endptr[0]) prefix_scale = 1e3;
else if ('M' == endptr[0]) prefix_scale = 1e6; else if ('M' == endptr[0]) prefix_scale = 1e6;
else if ('G' == endptr[0]) prefix_scale = 1e9; else if ('G' == endptr[0]) prefix_scale = 1e9;
/* special case: trailing 'f' from floating point literals copied and pasted from C code should be ignored */ /* special case: trailing 'f' from floating point literals copied and pasted from C code should be ignored */
else if ('f' == endptr[0]) prefix_scale = 1.0; else if ('f' == endptr[0]) prefix_scale = 1.0;
else return QRPN_ERROR_TOKEN_UNRECOGNIZED; else return QRPN_ERROR_TOKEN_UNRECOGNIZED;
tmp.value *= prefix_scale; tmp.value *= prefix_scale;
} }
} }
if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK; if (S >= QRPN_STACK_SIZE_MAX) return QRPN_ERROR_TOO_MUCH_STACK;
stack[S] = tmp; stack[S] = tmp;
return S + 1; return S + 1;
@ -451,7 +451,7 @@ static int evaluate_literal(struct quantity * stack, int S, const char * const t
static int evaluate_one_argument_must_be_unitless(struct quantity * const stack, int S, double complex (* op)(double complex)) { 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 (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = op(stack[S - 1].value); stack[S - 1].value = op(stack[S - 1].value);
return S; return S;
} }
@ -459,7 +459,7 @@ static int evaluate_one_argument_must_be_unitless(struct quantity * const stack,
static int evaluate_one_argument_must_be_unitless_real(struct quantity * const stack, int S, double (* op)(double)) { 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 (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = op(creal(stack[S - 1].value)); stack[S - 1].value = op(creal(stack[S - 1].value));
return S; return S;
} }
@ -469,7 +469,7 @@ static int evaluate_one_argument_must_be_unitless_real_nonnegative(struct quanti
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; 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 (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 (creal(stack[S - 1].value) < 0) return QRPN_ERROR_MUST_BE_NONNEGATIVE;
stack[S - 1].value = op(creal(stack[S - 1].value)); stack[S - 1].value = op(creal(stack[S - 1].value));
return S; return S;
} }
@ -486,10 +486,10 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
/* note that we perform all possible validation before we mutate any state */ /* note that we perform all possible validation before we mutate any state */
stack[S - 2].value *= stack[S - 1].value; stack[S - 2].value *= stack[S - 1].value;
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
stack[S - 2].units[iu] = units_out[iu]; stack[S - 2].units[iu] = units_out[iu];
return S - 1; return S - 1;
} }
else if (!strcmp(token, "div") || !strcmp(token, "/")) { else if (!strcmp(token, "div") || !strcmp(token, "/")) {
@ -499,7 +499,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
units_out[iu] = stack[S - 2].units[iu] - stack[S - 1].units[iu]; units_out[iu] = stack[S - 2].units[iu] - stack[S - 1].units[iu];
if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW; if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW;
} }
stack[S - 2].value /= stack[S - 1].value; stack[S - 2].value /= stack[S - 1].value;
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
@ -509,7 +509,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
} }
else if (!strcmp(token, "idiv")) { else if (!strcmp(token, "idiv")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; 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 (!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(creal(stack[S - 2].value)); const long long a = llrint(creal(stack[S - 2].value));
@ -569,7 +569,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "chs")) { else if (!strcmp(token, "chs")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
stack[S - 1].value *= -1; stack[S - 1].value *= -1;
/* always choose positive imaginary side of negative real line because [1] [chs] [sqrt] is pretty common */ /* always choose positive imaginary side of negative real line because [1] [chs] [sqrt] is pretty common */
if (__imag__ stack[S - 1].value == -0) if (__imag__ stack[S - 1].value == -0)
__imag__ stack[S - 1].value = 0; __imag__ stack[S - 1].value = 0;
@ -585,7 +585,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
if ((double)n != stack[S - 2].value || if ((double)n != stack[S - 2].value ||
(double)k != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER; (double)k != stack[S - 1].value) return QRPN_ERROR_MUST_BE_INTEGER;
stack[S - 2].value = nchoosek(n, k); stack[S - 2].value = nchoosek(n, k);
return S - 1; return S - 1;
} }
else if (!strcmp(token, "gcd")) { else if (!strcmp(token, "gcd")) {
@ -660,28 +660,28 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "and")) { else if (!strcmp(token, "and")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; 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 (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 2].value = (!!stack[S - 2].value) && (!!stack[S - 1].value); stack[S - 2].value = (!!stack[S - 2].value) && (!!stack[S - 1].value);
return S - 1; return S - 1;
} }
else if (!strcmp(token, "or")) { else if (!strcmp(token, "or")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; 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 (!units_are_dimensionless(stack[S - 1].units) || !units_are_dimensionless(stack[S - 2].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 2].value = (!!stack[S - 2].value) || (!!stack[S - 1].value); stack[S - 2].value = (!!stack[S - 2].value) || (!!stack[S - 1].value);
return S - 1; return S - 1;
} }
else if (!strcmp(token, "not")) { else if (!strcmp(token, "not")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
stack[S - 1].value = !stack[S - 1].value; stack[S - 1].value = !stack[S - 1].value;
return S; return S;
} }
else if (!strcmp(token, "eq")) { else if (!strcmp(token, "eq")) {
if (S < 2) return QRPN_ERROR_NOT_ENOUGH_STACK; 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 (!units_are_equivalent(stack[S - 2].units, stack[S - 1].units)) return QRPN_ERROR_INCONSISTENT_UNITS;
stack[S - 2].value = stack[S - 2].value == stack[S - 1].value; stack[S - 2].value = stack[S - 2].value == stack[S - 1].value;
memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS])); memset(stack[S - 2].units, 0, sizeof(int8_t[BASEUNITS]));
return S - 1; return S - 1;
@ -713,16 +713,16 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
const double complex b = stack[S - 2].value; const double complex b = stack[S - 2].value;
const double complex c = stack[S - 1].value; const double complex c = stack[S - 1].value;
const double complex discriminant = b * b - 4.0 * a * c; const double complex discriminant = b * b - 4.0 * a * c;
const double complex d = 0.5 / a; const double complex d = 0.5 / a;
const double complex e = csqrt(discriminant); const double complex e = csqrt(discriminant);
/* well-conditioned floating point method of getting roots, which avoids subtracting two nearly equal magnitude numbers */ /* well-conditioned floating point method of getting roots, which avoids subtracting two nearly equal magnitude numbers */
const double complex r1 = __real__ e > 0 ? (-b - e) * d : (-b + e) * d; const double complex r1 = __real__ e > 0 ? (-b - e) * d : (-b + e) * d;
const double complex r0 = c / (r1 * a); const double complex r0 = c / (r1 * a);
stack[S - 3].value = r1; stack[S - 3].value = r1;
stack[S - 2].value = r0; stack[S - 2].value = r0;
for (size_t iu = 0; iu < BASEUNITS; iu++) { for (size_t iu = 0; iu < BASEUNITS; iu++) {
stack[S - 3].units[iu] = units_out[iu]; stack[S - 3].units[iu] = units_out[iu];
stack[S - 2].units[iu] = units_out[iu]; stack[S - 2].units[iu] = units_out[iu];
@ -754,7 +754,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
units_out[iu] = stack[S - 2].units[iu] * ipowarg; units_out[iu] = stack[S - 2].units[iu] * ipowarg;
if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW; if (units_out[iu] > INT8_MAX || units_out[iu] < INT8_MIN) return QRPN_ERROR_DIMENSION_OVERFLOW;
} }
stack[S - 2].value = cpow_checked(stack[S - 2].value, stack[S - 1].value); stack[S - 2].value = cpow_checked(stack[S - 2].value, stack[S - 1].value);
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
@ -783,7 +783,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
} }
else if (!strcmp(token, "gamma")) { else if (!strcmp(token, "gamma")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; 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 (cimag(stack[S - 1].value)) return QRPN_ERROR_DOMAIN;
stack[S - 1].value = tgamma(creal(stack[S - 1].value)); stack[S - 1].value = tgamma(creal(stack[S - 1].value));
return S; return S;
@ -822,7 +822,7 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
stack[S - 1].units[0] = 0; stack[S - 1].units[0] = 0;
stack[S - 1].value /= 6371000.0; stack[S - 1].value /= 6371000.0;
} }
if (!units_are_dimensionless(stack[S - 1].units) || if (!units_are_dimensionless(stack[S - 1].units) ||
!units_are_dimensionless(stack[S - 2].units) || !units_are_dimensionless(stack[S - 2].units) ||
!units_are_dimensionless(stack[S - 3].units) || !units_are_dimensionless(stack[S - 3].units) ||
@ -841,12 +841,12 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
const double b = ahav( hav(a - c) + sin(a) * sin(c) * hav(B) ); const double b = ahav( hav(a - c) + sin(a) * sin(c) * hav(B) );
/* change in longitude */ /* change in longitude */
const double A = atan2( sin(B) * sin(a) * sin(c), cos(a) - cos(c) * cos(b) ); const double A = atan2( sin(B) * sin(a) * sin(c), cos(a) - cos(c) * cos(b) );
/* endpoint longitude is start plus delta */ /* endpoint longitude is start plus delta */
stack[S - 4].value = in[0] + A; stack[S - 4].value = in[0] + A;
/* endpoint latitude is 90 degrees minus endpoint declination */ /* endpoint latitude is 90 degrees minus endpoint declination */
stack[S - 3].value = M_PI_2 - b; stack[S - 3].value = M_PI_2 - b;
return S - 2; return S - 2;
} }
else if (!strcmp(token, "nextafter")) { else if (!strcmp(token, "nextafter")) {
@ -901,11 +901,11 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
else if (!strcmp(token, "itenlog")) return evaluate_one_argument_must_be_unitless_real_nonnegative(stack, S, itenlog); else if (!strcmp(token, "itenlog")) return evaluate_one_argument_must_be_unitless_real_nonnegative(stack, S, itenlog);
else if (!strcmp(token, "square")) { else if (!strcmp(token, "square")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
if (stack[S - 1].units[iu] * 2 > INT8_MAX || stack[S - 1].units[iu] * 2 < INT8_MIN) if (stack[S - 1].units[iu] * 2 > INT8_MAX || stack[S - 1].units[iu] * 2 < INT8_MIN)
return QRPN_ERROR_DIMENSION_OVERFLOW; return QRPN_ERROR_DIMENSION_OVERFLOW;
stack[S - 1].value = stack[S - 1].value * stack[S - 1].value; stack[S - 1].value = stack[S - 1].value * stack[S - 1].value;
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
stack[S - 1].units[iu] *= 2; stack[S - 1].units[iu] *= 2;
@ -959,8 +959,8 @@ int qrpn_evaluate_token(struct quantity * const stack, int S, const char * const
} }
else if (!strcmp(token, "print")) { else if (!strcmp(token, "print")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
fprintf_quantity(stderr, stack[S - 1]); fprintf_quantity(stdout, stack[S - 1]);
fprintf(stderr, "\n"); fprintf(stdout, "\n");
return S; return S;
} }
else else
@ -980,10 +980,10 @@ static void fprintf_value(FILE * fh, const double complex value) {
fprintf(fh, "%gi", cimag(value)); fprintf(fh, "%gi", cimag(value));
} else { } else {
fprintf(fh, "%g", creal(value)); fprintf(fh, "%g", creal(value));
if (cimag(value) && fabs(cimag(value)) * 1e14 > fabs(creal(value))) { if (cimag(value) && fabs(cimag(value)) * 1e14 > fabs(creal(value))) {
fprintf(fh, " %c ", cimag(value) > 0 ? '+' : '-'); fprintf(fh, " %c ", cimag(value) > 0 ? '+' : '-');
fprintf(fh, "%gi", fabs(cimag(value))); fprintf(fh, "%gi", fabs(cimag(value)));
} }
} }
@ -992,17 +992,17 @@ static void fprintf_value(FILE * fh, const double complex value) {
static void fprintf_quantity_si_base(FILE * fh, const struct quantity quantity) { static void fprintf_quantity_si_base(FILE * fh, const struct quantity quantity) {
/* only use si base units */ /* only use si base units */
fprintf_value(fh, quantity.value); fprintf_value(fh, quantity.value);
static const char * si_base_unit_abbreviations[BASEUNITS] = { "m", "kg", "s", "A", "K", "Cd", "mol" }; static const char * si_base_unit_abbreviations[BASEUNITS] = { "m", "kg", "s", "A", "K", "Cd", "mol" };
for (size_t iu = 0; iu < BASEUNITS; iu++) { for (size_t iu = 0; iu < BASEUNITS; iu++) {
if (quantity.units[iu] > 0) if (quantity.units[iu] > 0)
fprintf(fh, " %s", si_base_unit_abbreviations[iu]); fprintf(fh, " %s", si_base_unit_abbreviations[iu]);
if (quantity.units[iu] > 1) if (quantity.units[iu] > 1)
fprintf(fh, "^%d", quantity.units[iu]); fprintf(fh, "^%d", quantity.units[iu]);
} }
for (size_t iu = 0; iu < BASEUNITS; iu++) for (size_t iu = 0; iu < BASEUNITS; iu++)
if (quantity.units[iu] < 0) if (quantity.units[iu] < 0)
fprintf(fh, " %s^%d", si_base_unit_abbreviations[iu], quantity.units[iu]); fprintf(fh, " %s^%d", si_base_unit_abbreviations[iu], quantity.units[iu]);
@ -1016,14 +1016,14 @@ static void fprintf_quantity_si(FILE * fh, const struct quantity quantity) {
/* i hate everything about this and so should you */ /* i hate everything about this and so should you */
if (named->flags & (FLAG_SI_BASE_UNIT | FLAG_SI_DERIVED_UNIT) && (exponent = units_are_power_of(&quantity, named)) * sign > 0) { if (named->flags & (FLAG_SI_BASE_UNIT | FLAG_SI_DERIVED_UNIT) && (exponent = units_are_power_of(&quantity, named)) * sign > 0) {
fprintf_value(fh, quantity.value / named->value); fprintf_value(fh, quantity.value / named->value);
fprintf(fh, " %s", named->name ? named->name : named->abrv); fprintf(fh, " %s", named->name ? named->name : named->abrv);
if (1 != exponent) fprintf(fh, "^%d", exponent); if (1 != exponent) fprintf(fh, "^%d", exponent);
return; return;
} }
} }
/* if we get here we're just looping through the SI base units */ /* if we get here we're just looping through the SI base units */
return fprintf_quantity_si_base(fh, quantity); return fprintf_quantity_si_base(fh, quantity);
} }
@ -1138,20 +1138,20 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
do { do {
S = qrpn_evaluate_tokens(stack, S, tp + 1, nest_level + 1); S = qrpn_evaluate_tokens(stack, S, tp + 1, nest_level + 1);
if (S < 0) return S; if (S < 0) return S;
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
S--; S--;
} while (!stack[S].value); } while (!stack[S].value);
tp = tp_until_or_while; tp = tp_until_or_while;
} }
} }
else if (!strcmp(token, "if")) { else if (!strcmp(token, "if")) {
if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK; if (S < 1) return QRPN_ERROR_NOT_ENOUGH_STACK;
if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS; if (!units_are_dimensionless(stack[S - 1].units)) return QRPN_ERROR_MUST_BE_UNITLESS;
const char ** tp_else_or_endif = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF); const char ** tp_else_or_endif = find_matching_control_statement(tp + 1, ELSE_OR_ENDIF);
const char ** tp_else = NULL, ** tp_endif; const char ** tp_else = NULL, ** tp_endif;
@ -1162,11 +1162,11 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
if (!tp_endif) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT; if (!tp_endif) return QRPN_ERROR_UNMATCHED_CONTROL_STATEMENT;
} }
else tp_endif = tp_else_or_endif; else tp_endif = tp_else_or_endif;
/* choose which branch to take */ /* choose which branch to take */
const char ** tp_branch = stack[S - 1].value ? tp : tp_else; const char ** tp_branch = stack[S - 1].value ? tp : tp_else;
S--; S--;
S = qrpn_evaluate_tokens(stack, S, tp_branch + 1, nest_level + 1); S = qrpn_evaluate_tokens(stack, S, tp_branch + 1, nest_level + 1);
tp = tp_endif; tp = tp_endif;
@ -1182,21 +1182,21 @@ int qrpn_evaluate_tokens(struct quantity * const stack, int S, const char ** con
int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string) { int qrpn_evaluate_string(struct quantity * const stack, int S, const char * string) {
const char ** tokens = NULL; const char ** tokens = NULL;
size_t T = 0; size_t T = 0;
char * const copy = strdup(string); char * const copy = strdup(string);
for (char * token, * p = copy; (token = strsep(&p, " ")); ) { for (char * token, * p = copy; (token = strsep(&p, " ")); ) {
T++; T++;
tokens = realloc(tokens, sizeof(char *) * (T + 1)); tokens = realloc(tokens, sizeof(char *) * (T + 1));
tokens[T - 1] = token; tokens[T - 1] = token;
} }
tokens[T] = NULL; tokens[T] = NULL;
S = qrpn_evaluate_tokens(stack, S, tokens, 0); S = qrpn_evaluate_tokens(stack, S, tokens, 0);
free(tokens); free(tokens);
free(copy); free(copy);
return S; return S;
} }
@ -1222,21 +1222,21 @@ int qrpn_try_string(const struct quantity stack[static QRPN_STACK_SIZE_MAX], con
__attribute((weak)) int main(const int argc, const char ** const argv) { __attribute((weak)) int main(const int argc, const char ** const argv) {
if (isatty(STDIN_FILENO) && argc < 2) { if (isatty(STDIN_FILENO) && argc < 2) {
/* never reached */ /* never reached */
fprintf(stderr, "%s: Evaluates an RPN expression with units\n", argv[0]); fprintf(stdout, "%s: Evaluates an RPN expression with units\n", argv[0]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
struct quantity stack[QRPN_STACK_SIZE_MAX]; struct quantity stack[QRPN_STACK_SIZE_MAX];
int S = qrpn_evaluate_tokens(stack, 0, argv + 1, 0); int S = qrpn_evaluate_tokens(stack, 0, argv + 1, 0);
if (S < 0) { if (S < 0) {
fprintf(stderr, "error: %s\n", qrpn_strerror(S)); fprintf(stdout, "error: %s\n", qrpn_strerror(S));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
fprintf_stack(stdout, stack, S); fprintf_stack(stdout, stack, S);
fprintf(stdout, "\n"); fprintf(stdout, "\n");
return 0; return 0;
} }