#!/bin/sh # The MIT License (MIT) # # Copyright © 2020-2025 pacman64 # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the “Software”), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # ca [expressions...] # # # CAlculator is an easier-to-use way of running `bc` (basic calculator) where # # - you can calculate multiple different things in one run # - you give the expressions as arguments, while `bc` reads it from stdin # - you don't need quoting when avoiding parentheses and spaces # - you can use either ** or ^ to raise powers # - you can use [ and ] or ( and ) interchangeably # - the number of max-accuracy decimals is 25 by default # - automatically includes the extended bc math library via option -l # - there are several extra predefined values and functions case "$1" in -h|--h|-help|--help) awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" exit 0 ;; esac if [ $# -eq 0 ]; then awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" exit 0 fi # default max-accuracy decimals to use for calculations scale=25 # ensure each output is all on 1 line, define several funcs and values, then # inject the expressions given as this script's arguments, transforming them # according to the rules described above for arg in "$@"; do # printf "\e[7m%s\e[0m\n" "${arg}" > /dev/stderr [ $# -ge 2 ] && printf "\e[7m%s\e[0m\n" "${arg}" > /dev/stderr BC_LINE_LENGTH=0 bc -l << ENDOFSCRIPT scale = ${scale}; femto = 0.000000000000001; pico = 0.000000000001; nano = 0.000000001; micro = 0.000001; milli = 0.001; kilo = 1000; mega = 1000 * kilo; giga = 1000 * mega; tera = 1000 * giga; peta = 1000 * tera; exa = 1000 * peta; zetta = 1000 * exa; binkilo = 1024; binmega = 1024 * binkilo; bingiga = 1024 * binmega; bintera = 1024 * bingiga; binpeta = 1024 * bintera; binexa = 1024 * binpeta; binzetta = 1024 * binexa; kb = 1024; mb = 1024 * kb; gb = 1024 * mb; tb = 1024 * gb; pb = 1024 * tb; eb = 1024 * pb; zb = 1024 * eb; kib = 1024; mib = 1024 * kib; gib = 1024 * mib; tib = 1024 * gib; pib = 1024 * tib; zib = 1024 * pib; mol = 602214076000000000000000; mole = 602214076000000000000000; cup = 0.23658824; cup2l = 0.23658824; floz2l = 0.0295735295625; floz2ml = 29.5735295625; ft = 0.3048; ft2m = 0.3048; gal = 3.785411784; gal2l = 3.785411784; in = 2.54; in2cm = 2.54; lb = 0.45359237; lb2kg = 0.45359237; mi = 1.609344; mi2km = 1.609344; mpg = 0.425143707; mpg2kpl = 0.425143707; nm = 1.852; nm2km = 1.852; nmi = 1.852; nmi2km = 1.852; oz2g = 28.349523125 psi2pa = 6894.757293168; ton = 907.18474; ton2kg = 907.18474; yd = 0.9144; yd2m = 0.9144; ga2l = gal2l; nm2km = nmi2km; tn2kg = ton2kg; million = 1000000 billion = 1000 * million trillion = 1000 * billion hour = 3600; day = 24 * hour; week = 7 * day; hr = hour; wk = week; define ftin(f, i) { return (0.3048 * f + 0.0254 * i); } define lboz(l, o) { return (0.45359237 * l + 0.028349523 * o); } define eu() { return (e(1)); } define euler() { return (e(1)); } define pi() { return (4 * a(1)); } define tau() { return (8 * a(1)); } define deg(x) { return (180 * x / pi()); } define rad(x) { return (pi() * x / 180); } define degrees(x) { return (deg(x)); } define radians(x) { return (rad(x)); } define abs(x) { if (x >= 0) return (x); return (-x); } define exp(x) { return (e(x)); } define j0(x) { return (j(0, x)); } define j1(x) { return (j(1, x)); } define ln(x) { return (l(x)); } define log(x) { return (l(x)); } /* define log2(x) { return (l(x) / l(2)); } */ define log2(x) { auto r, n; if (x <= 0) return (l(x) / l(2)); r = 0; for (n = x; n > 1; n /= 2) r += 1; if (n == 1) return (r); return (l(x) / l(2)); } /* define log10(x) { return (l(x) / l(10)); } */ define log10(x) { auto r, n; if (x <= 0) return (l(x) / l(10)); r = 0; for (n = x; n > 1; n /= 10) r += 1; if (n == 1) return (r); return (l(x) / l(10)); } define sin(x) { return (s(x)); } define cos(x) { return (c(x)); } define tan(x) { return (s(x) / c(x)); } define cot(x) { return (c(x) / s(x)); } define atan(x) { return (a(x)); } define sinh(x) { return ((e(x) - e(-x)) / 2); } define cosh(x) { return ((e(x) + e(-x)) / 2); } define tanh(x) { return ((e(x) - e(-x)) / (e(x) + e(-x))); } define coth(x) { return ((e(x) + e(-x)) / (e(x) - e(-x))); } define hypot(x, y) { return (sqrt(x*x + y*y)); } define sinc(x) { if (x == 0) return (1); return (s(x) / x); } define min(x, y) { if (x <= y) return (x); return (y); } define max(x, y) { if (x >= y) return (x); return (y); } define mod(x, y) { auto s, m; s = scale; scale = 0; m = x % y; scale = s; return (m); } define mod1(x) { return (mod(x, 1)); } define modf(x) { return (mod(x, 1)); } define mix(x, y, k) { return (x * (1 - k) + y * k); } define round0(x) { auto i; i = x - mod(x, 1); if (x - i >= 0.5) { return (i + 1); } return (i); } define round(x, d) { auto k; k = 10 ^ d; return (round0(x * k) / k); } define r(x, d) { return (round(x, d)); } define r0(x) { return (round0(x)); } define epa(x) { return (epanechnikov(x)); } define epanechnikov(x) { if ((x < -1) || (x > 1)) return (0); return (3 / 4 * (1 - (x * x))); } define gauss(x) { return (gaussian(x)); } define gaussian(x) { return (e(-(x * x))); } define tricube(x) { auto a, b, c, d; if ((x < -1) || (x > 1)) return (0); if (x >= 0) a = x else a = -x; b = a * a * a; c = 1 - b; d = c * c * c; return (70 / 81 * d); } define fac(x) { auto f, i; if (x < 0) return (0); f = 1; for (i = x; i >= 2; i--) f *= i; return (f); } define f(x) { return (fac(x)); } define fact(x) { return (fac(x)); } define factorial(x) { return (fac(x)); } define per(n, k) { auto p, i; if (n < k) return (0); p = 1; for (i = n; i >= n - k + 1; i--) p *= i; return (p); } define p(n, k) { return (per(n, k)); } define perm(n, k) { return (per(n, k)); } define permut(n, k) { return (per(n, k)); } define permutations(n, k) { return (per(n, k)); } /* the name "c" is already used for the cosine function in "bc" */ define choose(n, k) { return (com(n, k)); } define com(n, k) { if (n < k) return (0); return (per(n, k) / fac(k)); } define comb(n, k) { return (com(n, k)); } define combin(n, k) { return (com(n, k)); } define combinations(n, k) { return (com(n, k)); } define gcd(x, y) { return (x * y / lcm(x, y)); } define lcm(x, y) { auto a, b, z; /* the LCM is defined only for positive integers */ /* if (mod(x, 1) != 0 || x < 1 || mod(y, 1) != 0 || y < 1) return (0); */ /* if (mod(x, 1) != 0) return (0); */ if (x < 1) return (0); /* if (mod(y, 1) != 0) return (0); */ if (y < 1) return (0); a = min(x, y); b = max(x, y); z = b; while (mod(z, a) != 0) { z += b; } return (z); } define dbin(x, n, p) { return (dbinom(x, n, p)); } define pbin(x, n, p) { return (pbinom(x, n, p)); } define dbinom(x, n, p) { return (com(n, x) * (p ^ x) * ((1 - p) ^ (n - x))); } /* pbinom inefficiently repeats calculations for now, which keeps it simple */ define pbinom(x, n, p) { auto k, t; t = 0; for (k = 0; k <= n; k++) t += dbinom(k, n, p); return (t); } /* pbinomfast may be wrong, while the simpler pbinom seems correct */ define pbinomfast(x, n, p) { auto a, b, d, q, k, t; if ((p < 0) || (p > 1)) return (0); if (x < 0) return (0); if (x >= n) return (1); a = 1; q = 1 - p; b = b ^ n; d = 1; t = 0; for (k = 0; k < x;) { t += (per(n, k) / d) * a * b; a *= p; b /= q; k++; d *= k; } /* remember the last loop, where k == x */ t += (per(n, k) / d) * a * b; return (t); } define dexp(x, r) { if (r < 0) return (0); return (r * e(-r * x)); } define pexp(x, r) { if (r < 0) return (0); return (1 - e(-r * x)); } define dpois(x, l) { return ((l ^ x) * e(-l) / fac(x)); } define ppois(x, l) { auto t, d, i; t = 1; d = 1; for (i = 1; i <= l; i++) { d *= i; t += (l ^ i) / d; } return (e(-l) * t); } define sgn(x) { if (x > 0) return (1); if (x < 0) return (-1); return (0); } define sign(x) { return (sgn(x)); } define logistic(x) { return (1 / (1 + e(-x))); } $(echo "${arg}" | sed 's-^+--g; s-_--g; s-\*\*-^-g; s-\[-(-g; s-\]-)-g') ENDOFSCRIPT done | # ensure the result shows at least a zero before the decimal dot, then # rid the result of trailing zero decimals and/or trailing decimal dots sed -E 's-^\.-0.-; s/^-\./-0./; s-(\.[0-9]*[1-9])0+$-\1-; s-\.0*$--'