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