File: ca.sh 1 #!/bin/sh 2 3 # The MIT License (MIT) 4 # 5 # Copyright (c) 2026 pacman64 6 # 7 # Permission is hereby granted, free of charge, to any person obtaining a copy 8 # of this software and associated documentation files (the "Software"), to deal 9 # in the Software without restriction, including without limitation the rights 10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 # copies of the Software, and to permit persons to whom the Software is 12 # furnished to do so, subject to the following conditions: 13 # 14 # The above copyright notice and this permission notice shall be included in 15 # all copies or substantial portions of the Software. 16 # 17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 # SOFTWARE. 24 25 26 # ca [expressions...] 27 # 28 # CAlculator is an easier-to-use way of running `bc` (basic calculator) where 29 # 30 # - you can calculate multiple different things in one run 31 # - you give the expressions as arguments, while `bc` uses stdin 32 # - you don't need quoting when avoiding parentheses and spaces 33 # - you can use either ** or ^ to raise powers 34 # - you can use [ and ] or ( and ) interchangeably 35 # - the number of max-accuracy decimals is 25 by default 36 # - automatically includes the extended bc math library via option -l 37 # - there are several extra predefined values, functions, and aliases 38 # - unneeded trailing decimal zeros are ignored for final outputs 39 40 41 quiet=0 42 43 case "$1" in 44 -h|--h|-help|--help) 45 awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 46 exit 0 47 ;; 48 49 -q|--q|-quiet|--quiet) 50 quiet=1 51 shift 52 ;; 53 esac 54 55 [ "$1" = '--' ] && shift 56 57 if [ $# -eq 0 ]; then 58 awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 59 exit 0 60 fi 61 62 # default max-accuracy decimals to use for calculations 63 scale=25 64 65 src=' 66 femto = 0.000000000000001; 67 pico = 0.000000000001; 68 nano = 0.000000001; 69 micro = 0.000001; 70 milli = 0.001; 71 72 kilo = 1000; 73 mega = 1000 * kilo; 74 giga = 1000 * mega; 75 tera = 1000 * giga; 76 peta = 1000 * tera; 77 exa = 1000 * peta; 78 zetta = 1000 * exa; 79 80 binkilo = 1024; 81 binmega = 1024 * binkilo; 82 bingiga = 1024 * binmega; 83 bintera = 1024 * bingiga; 84 binpeta = 1024 * bintera; 85 binexa = 1024 * binpeta; 86 binzetta = 1024 * binexa; 87 88 kb = 1024; 89 mb = 1024 * kb; 90 gb = 1024 * mb; 91 tb = 1024 * gb; 92 pb = 1024 * tb; 93 eb = 1024 * pb; 94 zb = 1024 * eb; 95 96 kib = 1024; 97 mib = 1024 * kib; 98 gib = 1024 * mib; 99 tib = 1024 * gib; 100 pib = 1024 * tib; 101 zib = 1024 * pib; 102 103 mol = 602214076000000000000000; 104 mole = 602214076000000000000000; 105 106 cup = 0.23658824; 107 cup2l = 0.23658824; 108 floz2l = 0.0295735295625; 109 floz2ml = 29.5735295625; 110 ft = 0.3048; 111 ft2m = 0.3048; 112 gal = 3.785411784; 113 gal2l = 3.785411784; 114 in = 2.54; 115 in2cm = 2.54; 116 lb = 0.45359237; 117 lb2kg = 0.45359237; 118 mi = 1.609344; 119 mi2km = 1.609344; 120 mpg = 0.425143707; 121 mpg2kpl = 0.425143707; 122 nm = 1.852; 123 nm2km = 1.852; 124 nmi = 1.852; 125 nmi2km = 1.852; 126 oz2g = 28.349523125 127 psi2pa = 6894.757293168; 128 ton = 907.18474; 129 ton2kg = 907.18474; 130 yd = 0.9144; 131 yd2m = 0.9144; 132 133 ga2l = gal2l; 134 nm2km = nmi2km; 135 tn2kg = ton2kg; 136 137 million = 1000000; 138 billion = 1000 * million; 139 trillion = 1000 * billion; 140 141 hour = 3600; 142 day = 24 * hour; 143 week = 7 * day; 144 145 hr = hour; 146 wk = week; 147 148 /* function "choose": "bc" uses "c" for the built-in cosine function */ 149 150 define abs(x) { if (x >= 0) return (x) else return (-x); } 151 define atan(x) { return (a(x)); } 152 define bits(x) { return (log2(x)); } 153 define ceil(x) { return (ceiling(x)); } 154 define choose(n, k) { return (com(n, k)); } 155 define circle(r) { return (4 * a(1) * r * r); } /* circle-area from radius */ 156 define circum(r) { return (8 * a(1) * r); } /* circumference from radius */ 157 define circumference(r) { return (8 * a(1) * r); } 158 define com(n, k) { if (n < k) return (0) else return (per(n, k) / fac(k)); } 159 define comb(n, k) { return (com(n, k)); } 160 define combin(n, k) { return (com(n, k)); } 161 define combinations(n, k) { return (com(n, k)); } 162 define cos(x) { return (c(x)); } 163 define cosh(x) { return ((e(x) + e(-x)) / 2); } 164 define cot(x) { return (c(x) / s(x)); } 165 define coth(x) { return ((e(x) + e(-x)) / (e(x) - e(-x))); } 166 define dbin(x, n, p) { return (dbinom(x, n, p)); } 167 define dbinom(x, n, p) { return (com(n, x) * (p ^ x) * ((1 - p) ^ (n - x))); } 168 define deg(x) { return (180 * x / pi()); } 169 define digits(x) { return (log10(x)); } 170 define degrees(x) { return (deg(x)); } 171 define dexp(x, r) { if (r < 0) return (0) else return (r * e(-r * x)); } 172 define dpois(x, l) { return ((l ^ x) * e(-l) / fac(x)); } 173 define gauss(x) { return (gaussian(x)); } 174 define gaussian(x) { return (e(-(x * x))); } 175 define epa(x) { return (epanechnikov(x)); } 176 define eu() { return (e(1)); } 177 define euler() { return (e(1)); } 178 define exp(x) { return (e(x)); } 179 define f(x) { return (fac(x)); } 180 define fact(x) { return (fac(x)); } 181 define factorial(x) { return (fac(x)); } 182 define ftin(f, i) { return (0.3048 * f + 0.0254 * i); } 183 define gcd(x, y) { return (x * y / lcm(x, y)); } 184 define hypot(x, y) { return (sqrt(x*x + y*y)); } 185 define j0(x) { return (j(0, x)); } 186 define j1(x) { return (j(1, x)); } 187 define lboz(l, o) { return (0.45359237 * l + 0.028349523 * o); } 188 define ln(x) { return (l(x)); } 189 define log(x) { return (l(x)); } 190 define logistic(x) { return (1 / (1 + e(-x))); } 191 define max(x, y) { if (x >= y) return (x) else return (y); } 192 define min(x, y) { if (x <= y) return (x) else return (y); } 193 define mix(x, y, k) { return (x * (1 - k) + y * k); } 194 define mod1(x) { return (mod(x, 1)); } 195 define modf(x) { return (mod(x, 1)); } 196 define p(n, k) { return (per(n, k)); } 197 define pbin(x, n, p) { return (pbinom(x, n, p)); } 198 define perm(n, k) { return (per(n, k)); } 199 define permut(n, k) { return (per(n, k)); } 200 define permutations(n, k) { return (per(n, k)); } 201 define pexp(x, r) { if (r < 0) return (0) else return (1 - e(-r * x)); } 202 define pi() { return (4 * a(1)); } 203 define pow2(x) { return (2 ^ x); } 204 define power2(x) { return (2 ^ x); } 205 define pow10(x) { return (10 ^ x); } 206 define power10(x) { return (10 ^ x); } 207 define r(x, d) { return (round(x, d)); } 208 define r0(x) { return (round0(x)); } 209 define rad(x) { return (pi() * x / 180); } 210 define radians(x) { return (rad(x)); } 211 define sgn(x) { return (sgn(x)); } 212 define sin(x) { return (s(x)); } 213 define sinc(x) { if (x == 0) return (1) else return (s(x) / x); } 214 define sinh(x) { return ((e(x) - e(-x)) / 2); } 215 define tan(x) { return (s(x) / c(x)); } 216 define tanh(x) { return ((e(x) - e(-x)) / (e(x) + e(-x))); } 217 define tau() { return (8 * a(1)); } 218 219 define ceiling(x) { 220 auto s, r; 221 s = scale; 222 scale = 0; 223 r = x % 1; 224 scale = s; 225 if (r == 0) return (x); 226 if (x < 0) return (x - r); 227 return (x - r + 1); 228 } 229 230 define epanechnikov(x) { 231 if ((x < -1) || (x > 1)) return (0); 232 return (3 / 4 * (1 - (x * x))); 233 } 234 235 define fac(x) { 236 auto f, i; 237 if (x < 0) return (0); 238 f = 1; 239 for (i = x; i >= 2; i--) f *= i; 240 return (f); 241 } 242 243 define floor(x) { 244 auto s, r; 245 s = scale; 246 scale = 0; 247 r = x % 1; 248 scale = s; 249 if (r == 0) return (x); 250 if (x < 0) return (x - r - 1); 251 return (x - r); 252 } 253 254 define lcm(x, y) { 255 auto a, b, z; 256 a = x; 257 b = y; 258 if (a > b) { 259 a = y; 260 b = x; 261 } 262 263 /* the LCM is defined only for positive integers */ 264 if (mod(x, 1) != 0 || x < 1 || mod(y, 1) != 0 || y < 1) return (0); 265 266 z = b; 267 while (mod(z, a) != 0) { z += b; } 268 return (z); 269 } 270 271 define log2(x) { 272 auto r, n; 273 if (x <= 0) return (l(x) / l(2)); 274 275 r = 0; 276 for (n = x; n > 1; n /= 2) r += 1; 277 278 if (n == 1) return (r); 279 return (l(x) / l(2)); 280 } 281 282 define log10(x) { 283 auto r, n; 284 if (x <= 0) return (l(x) / l(10)); 285 286 r = 0; 287 for (n = x; n > 1; n /= 10) r += 1; 288 289 if (n == 1) return (r); 290 return (l(x) / l(10)); 291 } 292 293 define mod(x, y) { 294 auto s, m; 295 s = scale; 296 scale = 0; 297 m = x % y; 298 scale = s; 299 return (m); 300 } 301 302 define per(n, k) { 303 auto p, i; 304 if (n < k) return (0); 305 p = 1; 306 for (i = n; i >= n - k + 1; i--) p *= i; 307 return (p); 308 } 309 310 /* pbinom inefficiently repeats calculations for now, which keeps it simple */ 311 define pbinom(x, n, p) { 312 auto k, t; 313 t = 0; 314 for (k = 0; k <= n; k++) t += dbinom(k, n, p); 315 return (t); 316 } 317 318 /* pbinomfast may be wrong, while the simpler pbinom seems correct */ 319 define pbinomfast(x, n, p) { 320 auto a, b, d, q, k, t; 321 if ((p < 0) || (p > 1)) return (0); 322 if (x < 0) return (0); 323 if (x >= n) return (1); 324 a = 1; 325 q = 1 - p; 326 b = b ^ n; 327 d = 1; 328 t = 0; 329 for (k = 0; k < x;) { 330 t += (per(n, k) / d) * a * b; 331 a *= p; 332 b /= q; 333 k++; 334 d *= k; 335 } 336 /* remember the last loop, where k == x */ 337 t += (per(n, k) / d) * a * b; 338 return (t); 339 } 340 341 define ppois(x, l) { 342 auto t, d, i; 343 t = 1; 344 d = 1; 345 for (i = 1; i <= l; i++) { 346 d *= i; 347 t += (l ^ i) / d; 348 } 349 return (e(-l) * t); 350 } 351 352 define round(x, d) { 353 auto k; 354 k = 10 ^ d; 355 return (round0(x * k) / k); 356 } 357 358 define round0(x) { 359 auto i; 360 i = x - mod(x, 1); 361 if (x - i >= 0.5) { 362 return (i + 1); 363 } 364 return (i); 365 } 366 367 define sign(x) { 368 if (x > 0) return (1); 369 if (x < 0) return (-1); 370 return (0); 371 } 372 373 define tricube(x) { 374 auto a, b, c, d; 375 if ((x < -1) || (x > 1)) return (0); 376 if (x >= 0) a = x else a = -x; 377 b = a * a * a; 378 c = 1 - b; 379 d = c * c * c; 380 return (70 / 81 * d); 381 } 382 ' 383 384 code=0 385 386 # ensure each output is all on 1 line, define several funcs and values, then 387 # inject the expressions given as this script's arguments, transforming them 388 # according to the rules described above 389 for arg in "$@"; do 390 if [ "${code}" -ne 0 ]; then 391 break 392 fi 393 394 if [ "${quiet}" -eq 0 ] && [ $# -ge 2 ]; then 395 printf "\e[7m%s\e[0m\n" "${arg}" > /dev/stderr 396 fi 397 398 c="$(echo "${arg}" | sed 's-^+--g; s-_--g; s-\*\*-^-g; s-\[-(-g; s-\]-)-g')" 399 BC_LINE_LENGTH=0 bc -l << EOS 400 scale = ${scale}; 401 ${src} 402 ${c} 403 EOS 404 code=$? 405 done | \ 406 407 # using `sed` ensures the result shows at least a zero before the decimal dot, 408 # then rids the result of trailing zero decimals and/or trailing decimal dots 409 sed -E 's-^\.-0.-; s/^-\./-0./; s-(\.[0-9]*[1-9])0+$-\1-; s-\.0*$--' 410 411 exit "${code}"