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 # 29 # CAlculator is an easier-to-use way of running `bc` (basic calculator) where 30 # 31 # - you can calculate multiple different things in one run 32 # - you give the expressions as arguments, while `bc` uses stdin 33 # - you don't need quoting when avoiding parentheses and spaces 34 # - you can use either ** or ^ to raise powers 35 # - you can use [ and ] or ( and ) interchangeably 36 # - the number of max-accuracy decimals is 25 by default 37 # - automatically includes the extended bc math library via option -l 38 # - there are several extra predefined values, functions, and aliases 39 # - unneeded trailing decimal zeros are ignored for final outputs 40 # 41 # The options are, available both in single and double-dash versions 42 # 43 # -h, -help show this help message 44 # -v, -verbose show an ANSI-styled banner for each command being run 45 46 47 verbose=0 48 49 case "$1" in 50 -h|--h|-help|--help) 51 awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 52 exit 0 53 ;; 54 55 -v|--v|-verbose|--verbose) 56 verbose=1 57 shift 58 ;; 59 esac 60 61 [ "$1" = '--' ] && shift 62 63 if [ $# -eq 0 ]; then 64 awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 65 exit 0 66 fi 67 68 # default max-accuracy decimals to use for calculations 69 scale=25 70 71 src=' 72 femto = 0.000000000000001; 73 pico = 0.000000000001; 74 nano = 0.000000001; 75 micro = 0.000001; 76 milli = 0.001; 77 78 kilo = 1000; 79 mega = 1000 * kilo; 80 giga = 1000 * mega; 81 tera = 1000 * giga; 82 peta = 1000 * tera; 83 exa = 1000 * peta; 84 zetta = 1000 * exa; 85 86 binkilo = 1024; 87 binmega = 1024 * binkilo; 88 bingiga = 1024 * binmega; 89 bintera = 1024 * bingiga; 90 binpeta = 1024 * bintera; 91 binexa = 1024 * binpeta; 92 binzetta = 1024 * binexa; 93 94 kb = 1024; 95 mb = 1024 * kb; 96 gb = 1024 * mb; 97 tb = 1024 * gb; 98 pb = 1024 * tb; 99 eb = 1024 * pb; 100 zb = 1024 * eb; 101 102 kib = 1024; 103 mib = 1024 * kib; 104 gib = 1024 * mib; 105 tib = 1024 * gib; 106 pib = 1024 * tib; 107 zib = 1024 * pib; 108 109 mol = 602214076000000000000000; 110 mole = 602214076000000000000000; 111 112 cup = 0.23658824; 113 cup2l = 0.23658824; 114 floz2l = 0.0295735295625; 115 floz2ml = 29.5735295625; 116 ft = 0.3048; 117 ft2m = 0.3048; 118 gal = 3.785411784; 119 gal2l = 3.785411784; 120 in = 2.54; 121 in2cm = 2.54; 122 lb = 0.45359237; 123 lb2kg = 0.45359237; 124 mi = 1.609344; 125 mi2km = 1.609344; 126 mpg = 0.425143707; 127 mpg2kpl = 0.425143707; 128 nm = 1.852; 129 nm2km = 1.852; 130 nmi = 1.852; 131 nmi2km = 1.852; 132 oz2g = 28.349523125 133 psi2pa = 6894.757293168; 134 ton = 907.18474; 135 ton2kg = 907.18474; 136 yd = 0.9144; 137 yd2m = 0.9144; 138 139 ga2l = gal2l; 140 nm2km = nmi2km; 141 tn2kg = ton2kg; 142 143 million = 1000000; 144 billion = 1000 * million; 145 trillion = 1000 * billion; 146 147 hour = 3600; 148 day = 24 * hour; 149 week = 7 * day; 150 151 hr = hour; 152 wk = week; 153 154 /* function "choose": "bc" uses "c" for the built-in cosine function */ 155 156 define abs(x) { if (x >= 0) return (x) else return (-x); } 157 define atan(x) { return (a(x)); } 158 define bits(x) { return (log2(x)); } 159 define ceil(x) { return (ceiling(x)); } 160 define choose(n, k) { return (com(n, k)); } 161 define circle(r) { return (4 * a(1) * r * r); } /* circle-area from radius */ 162 define circum(r) { return (8 * a(1) * r); } /* circumference from radius */ 163 define circumference(r) { return (8 * a(1) * r); } 164 define com(n, k) { if (n < k) return (0) else return (per(n, k) / fac(k)); } 165 define comb(n, k) { return (com(n, k)); } 166 define combin(n, k) { return (com(n, k)); } 167 define combinations(n, k) { return (com(n, k)); } 168 define cos(x) { return (c(x)); } 169 define cosh(x) { return ((e(x) + e(-x)) / 2); } 170 define cot(x) { return (c(x) / s(x)); } 171 define coth(x) { return ((e(x) + e(-x)) / (e(x) - e(-x))); } 172 define dbin(x, n, p) { return (dbinom(x, n, p)); } 173 define dbinom(x, n, p) { return (com(n, x) * (p ^ x) * ((1 - p) ^ (n - x))); } 174 define deg(x) { return (180 * x / pi()); } 175 define digits(x) { return (log10(x)); } 176 define degrees(x) { return (deg(x)); } 177 define dexp(x, r) { if (r < 0) return (0) else return (r * e(-r * x)); } 178 define dpois(x, l) { return ((l ^ x) * e(-l) / fac(x)); } 179 define gauss(x) { return (gaussian(x)); } 180 define gaussian(x) { return (e(-(x * x))); } 181 define epa(x) { return (epanechnikov(x)); } 182 define eu() { return (e(1)); } 183 define euler() { return (e(1)); } 184 define exp(x) { return (e(x)); } 185 define f(x) { return (fac(x)); } 186 define fact(x) { return (fac(x)); } 187 define factorial(x) { return (fac(x)); } 188 define ftin(f, i) { return (0.3048 * f + 0.0254 * i); } 189 define gcd(x, y) { return (x * y / lcm(x, y)); } 190 define hypot(x, y) { return (sqrt(x*x + y*y)); } 191 define j0(x) { return (j(0, x)); } 192 define j1(x) { return (j(1, x)); } 193 define lboz(l, o) { return (0.45359237 * l + 0.028349523 * o); } 194 define ln(x) { return (l(x)); } 195 define log(x) { return (l(x)); } 196 define logistic(x) { return (1 / (1 + e(-x))); } 197 define max(x, y) { if (x >= y) return (x) else return (y); } 198 define min(x, y) { if (x <= y) return (x) else return (y); } 199 define mix(x, y, k) { return (x * (1 - k) + y * k); } 200 define mod1(x) { return (mod(x, 1)); } 201 define modf(x) { return (mod(x, 1)); } 202 define p(n, k) { return (per(n, k)); } 203 define pbin(x, n, p) { return (pbinom(x, n, p)); } 204 define perm(n, k) { return (per(n, k)); } 205 define permut(n, k) { return (per(n, k)); } 206 define permutations(n, k) { return (per(n, k)); } 207 define pexp(x, r) { if (r < 0) return (0) else return (1 - e(-r * x)); } 208 define pi() { return (4 * a(1)); } 209 define pow2(x) { return (2 ^ x); } 210 define power2(x) { return (2 ^ x); } 211 define pow10(x) { return (10 ^ x); } 212 define power10(x) { return (10 ^ x); } 213 define prime(n) { return (isprime(n)); } 214 define r(x, d) { return (round(x, d)); } 215 define r0(x) { return (round0(x)); } 216 define rad(x) { return (pi() * x / 180); } 217 define radians(x) { return (rad(x)); } 218 define sgn(x) { return (sgn(x)); } 219 define sin(x) { return (s(x)); } 220 define sinc(x) { if (x == 0) return (1) else return (s(x) / x); } 221 define sinh(x) { return ((e(x) - e(-x)) / 2); } 222 define tan(x) { return (s(x) / c(x)); } 223 define tanh(x) { return ((e(x) - e(-x)) / (e(x) + e(-x))); } 224 define tau() { return (8 * a(1)); } 225 226 define ceiling(x) { 227 auto s, r; 228 s = scale; 229 scale = 0; 230 r = x % 1; 231 scale = s; 232 if (r == 0) return (x); 233 if (x < 0) return (x - r); 234 return (x - r + 1); 235 } 236 237 define epanechnikov(x) { 238 if ((x < -1) || (x > 1)) return (0); 239 return (3 / 4 * (1 - (x * x))); 240 } 241 242 define fac(x) { 243 auto f, i; 244 if (x < 0) return (0); 245 f = 1; 246 for (i = x; i >= 2; i--) f *= i; 247 return (f); 248 } 249 250 define floor(x) { 251 auto s, r; 252 s = scale; 253 scale = 0; 254 r = x % 1; 255 scale = s; 256 if (r == 0) return (x); 257 if (x < 0) return (x - r - 1); 258 return (x - r); 259 } 260 261 define isprime(n) { 262 auto i, m, s; 263 if ((n % 1) != 0) return (0); 264 if (n < 2) return (0); 265 266 s = scale; 267 scale = 0; 268 269 if ((n % 2) == 0) { 270 scale = s; 271 return (n == 2); 272 } 273 274 m = sqrt(n); 275 for (i = 3; i <= m; i += 2) { 276 if ((n % i) == 0) { 277 scale = s; 278 return (0); 279 } 280 } 281 282 scale = s; 283 return (1); 284 } 285 286 define lcm(x, y) { 287 auto a, b, z; 288 a = x; 289 b = y; 290 if (a > b) { 291 a = y; 292 b = x; 293 } 294 295 /* the LCM is defined only for positive integers */ 296 if (mod(x, 1) != 0 || x < 1 || mod(y, 1) != 0 || y < 1) return (0); 297 298 z = b; 299 while (mod(z, a) != 0) z += b; 300 return (z); 301 } 302 303 define log2(x) { 304 auto r, n; 305 if (x <= 0) return (l(x) / l(2)); 306 307 r = 0; 308 for (n = x; n > 1; n /= 2) r += 1; 309 310 if (n == 1) return (r); 311 return (l(x) / l(2)); 312 } 313 314 define log10(x) { 315 auto r, n; 316 if (x <= 0) return (l(x) / l(10)); 317 318 r = 0; 319 for (n = x; n > 1; n /= 10) r += 1; 320 321 if (n == 1) return (r); 322 return (l(x) / l(10)); 323 } 324 325 define mod(x, y) { 326 auto s, m; 327 s = scale; 328 scale = 0; 329 m = x % y; 330 scale = s; 331 return (m); 332 } 333 334 define per(n, k) { 335 auto p, i; 336 if (n < k) return (0); 337 p = 1; 338 for (i = n; i >= n - k + 1; i--) p *= i; 339 return (p); 340 } 341 342 /* pbinom inefficiently repeats calculations for now, which keeps it simple */ 343 define pbinom(x, n, p) { 344 auto k, t; 345 t = 0; 346 for (k = 0; k <= n; k++) t += dbinom(k, n, p); 347 return (t); 348 } 349 350 /* pbinomfast may be wrong, while the simpler pbinom seems correct */ 351 define pbinomfast(x, n, p) { 352 auto a, b, d, q, k, t; 353 if ((p < 0) || (p > 1)) return (0); 354 if (x < 0) return (0); 355 if (x >= n) return (1); 356 357 a = 1; 358 q = 1 - p; 359 b = b ^ n; 360 d = 1; 361 t = 0; 362 for (k = 0; k < x;) { 363 t += (per(n, k) / d) * a * b; 364 a *= p; 365 b /= q; 366 k++; 367 d *= k; 368 } 369 /* remember the last loop, where k == x */ 370 t += (per(n, k) / d) * a * b; 371 return (t); 372 } 373 374 define ppois(x, l) { 375 auto t, d, i; 376 t = 1; 377 d = 1; 378 for (i = 1; i <= l; i++) { 379 d *= i; 380 t += (l ^ i) / d; 381 } 382 return (e(-l) * t); 383 } 384 385 define round(x, d) { 386 auto k; 387 k = 10 ^ d; 388 return (round0(x * k) / k); 389 } 390 391 define round0(x) { 392 auto i; 393 i = x - mod(x, 1); 394 if (x - i >= 0.5) return (i + 1); 395 return (i); 396 } 397 398 define sign(x) { 399 if (x > 0) return (1); 400 if (x < 0) return (-1); 401 return (0); 402 } 403 404 define tricube(x) { 405 auto a, b, c, d; 406 if ((x < -1) || (x > 1)) return (0); 407 if (x >= 0) a = x else a = -x; 408 b = a * a * a; 409 c = 1 - b; 410 d = c * c * c; 411 return (70 / 81 * d); 412 } 413 ' 414 415 # `sed` code to adapt `ca` expressions into valid `bc` expressions 416 fix_code='s-^+--g; s-_--g; s-\*\*-^-g; s-\[-(-g; s-\]-)-g' 417 418 # `sed` code to ensure the results never start with a decimal dot, and to 419 # rid the result of any trailing zero decimals and/or trailing decimal dots 420 clean_trails='s-^\.-0.-; s/^-\./-0./; s-(\.[0-9]*[1-9])0+$-\1-; s-\.0*$--' 421 422 # `bc` code with all the extra settings and definitions, which comes before 423 # each expression to evaluate 424 prelude="$(printf "scale = %s;\n%s\n" "${scale}" "${src}")" 425 426 # ensure each output is all on 1 line, define several funcs and values, then 427 # inject the expressions given as this script's arguments, transforming them 428 # according to the rules described above 429 for arg in "$@"; do 430 if [ "${verbose}" -eq 1 ]; then 431 printf "\e[7m%s\e[0m\n" "${arg}" >&2 432 fi 433 434 exp="$(echo "${arg}" | sed "${fix_code}")" 435 printf "%s\n%s\n" "${prelude}" "${exp}"| BC_LINE_LENGTH=0 bc -l | \ 436 sed -E "${clean_trails}" 437 done