File: ca.sh 1 #!/bin/sh 2 3 # The MIT License (MIT) 4 # 5 # Copyright © 2025 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` reads it from 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 and functions 39 40 41 case "$1" in 42 -h|--h|-help|--help) 43 awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 44 exit 0 45 ;; 46 esac 47 48 [ "$1" = "--" ] && shift 49 50 if [ $# -eq 0 ]; then 51 awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0" 52 exit 0 53 fi 54 55 # default max-accuracy decimals to use for calculations 56 scale=25 57 58 # ensure each output is all on 1 line, define several funcs and values, then 59 # inject the expressions given as this script's arguments, transforming them 60 # according to the rules described above 61 for arg in "$@"; do 62 # printf "\e[7m%s\e[0m\n" "${arg}" > /dev/stderr 63 [ $# -ge 2 ] && printf "\e[7m%s\e[0m\n" "${arg}" > /dev/stderr 64 65 BC_LINE_LENGTH=0 bc -l << ENDOFSCRIPT 66 scale = ${scale}; 67 68 femto = 0.000000000000001; 69 pico = 0.000000000001; 70 nano = 0.000000001; 71 micro = 0.000001; 72 milli = 0.001; 73 74 kilo = 1000; 75 mega = 1000 * kilo; 76 giga = 1000 * mega; 77 tera = 1000 * giga; 78 peta = 1000 * tera; 79 exa = 1000 * peta; 80 zetta = 1000 * exa; 81 82 binkilo = 1024; 83 binmega = 1024 * binkilo; 84 bingiga = 1024 * binmega; 85 bintera = 1024 * bingiga; 86 binpeta = 1024 * bintera; 87 binexa = 1024 * binpeta; 88 binzetta = 1024 * binexa; 89 90 kb = 1024; 91 mb = 1024 * kb; 92 gb = 1024 * mb; 93 tb = 1024 * gb; 94 pb = 1024 * tb; 95 eb = 1024 * pb; 96 zb = 1024 * eb; 97 98 kib = 1024; 99 mib = 1024 * kib; 100 gib = 1024 * mib; 101 tib = 1024 * gib; 102 pib = 1024 * tib; 103 zib = 1024 * pib; 104 105 mol = 602214076000000000000000; 106 mole = 602214076000000000000000; 107 108 cup = 0.23658824; 109 cup2l = 0.23658824; 110 floz2l = 0.0295735295625; 111 floz2ml = 29.5735295625; 112 ft = 0.3048; 113 ft2m = 0.3048; 114 gal = 3.785411784; 115 gal2l = 3.785411784; 116 in = 2.54; 117 in2cm = 2.54; 118 lb = 0.45359237; 119 lb2kg = 0.45359237; 120 mi = 1.609344; 121 mi2km = 1.609344; 122 mpg = 0.425143707; 123 mpg2kpl = 0.425143707; 124 nm = 1.852; 125 nm2km = 1.852; 126 nmi = 1.852; 127 nmi2km = 1.852; 128 oz2g = 28.349523125 129 psi2pa = 6894.757293168; 130 ton = 907.18474; 131 ton2kg = 907.18474; 132 yd = 0.9144; 133 yd2m = 0.9144; 134 135 ga2l = gal2l; 136 nm2km = nmi2km; 137 tn2kg = ton2kg; 138 139 million = 1000000 140 billion = 1000 * million 141 trillion = 1000 * billion 142 143 hour = 3600; 144 day = 24 * hour; 145 week = 7 * day; 146 147 hr = hour; 148 wk = week; 149 150 define ftin(f, i) { 151 return (0.3048 * f + 0.0254 * i); 152 } 153 154 define lboz(l, o) { 155 return (0.45359237 * l + 0.028349523 * o); 156 } 157 158 define bits(x) { 159 return (log2(x)); 160 } 161 162 define digits(x) { 163 return (log10(x)); 164 } 165 166 define eu() { 167 return (e(1)); 168 } 169 170 define euler() { 171 return (e(1)); 172 } 173 174 define pi() { 175 return (4 * a(1)); 176 } 177 178 define tau() { 179 return (8 * a(1)); 180 } 181 182 define deg(x) { 183 return (180 * x / pi()); 184 } 185 186 define rad(x) { 187 return (pi() * x / 180); 188 } 189 190 define degrees(x) { 191 return (deg(x)); 192 } 193 194 define radians(x) { 195 return (rad(x)); 196 } 197 198 define abs(x) { 199 if (x >= 0) return (x); 200 return (-x); 201 } 202 203 define exp(x) { 204 return (e(x)); 205 } 206 207 define j0(x) { 208 return (j(0, x)); 209 } 210 211 define j1(x) { 212 return (j(1, x)); 213 } 214 215 define ln(x) { 216 return (l(x)); 217 } 218 219 define log(x) { 220 return (l(x)); 221 } 222 223 define log2(x) { 224 auto r, n; 225 if (x <= 0) return (l(x) / l(2)); 226 227 r = 0; 228 for (n = x; n > 1; n /= 2) r += 1; 229 230 if (n == 1) return (r); 231 return (l(x) / l(2)); 232 } 233 234 define log10(x) { 235 auto r, n; 236 if (x <= 0) return (l(x) / l(10)); 237 238 r = 0; 239 for (n = x; n > 1; n /= 10) r += 1; 240 241 if (n == 1) return (r); 242 return (l(x) / l(10)); 243 } 244 245 define sin(x) { 246 return (s(x)); 247 } 248 249 define cos(x) { 250 return (c(x)); 251 } 252 253 define tan(x) { 254 return (s(x) / c(x)); 255 } 256 257 define cot(x) { 258 return (c(x) / s(x)); 259 } 260 261 define atan(x) { 262 return (a(x)); 263 } 264 265 define sinh(x) { 266 return ((e(x) - e(-x)) / 2); 267 } 268 269 define cosh(x) { 270 return ((e(x) + e(-x)) / 2); 271 } 272 273 define tanh(x) { 274 return ((e(x) - e(-x)) / (e(x) + e(-x))); 275 } 276 277 define coth(x) { 278 return ((e(x) + e(-x)) / (e(x) - e(-x))); 279 } 280 281 define hypot(x, y) { 282 return (sqrt(x*x + y*y)); 283 } 284 285 define sinc(x) { 286 if (x == 0) return (1); 287 return (s(x) / x); 288 } 289 290 define min(x, y) { 291 if (x <= y) return (x); 292 return (y); 293 } 294 295 define max(x, y) { 296 if (x >= y) return (x); 297 return (y); 298 } 299 300 define mod(x, y) { 301 auto s, m; 302 s = scale; 303 scale = 0; 304 m = x % y; 305 scale = s; 306 return (m); 307 } 308 309 define mod1(x) { 310 return (mod(x, 1)); 311 } 312 313 define modf(x) { 314 return (mod(x, 1)); 315 } 316 317 define mix(x, y, k) { 318 return (x * (1 - k) + y * k); 319 } 320 321 define round0(x) { 322 auto i; 323 i = x - mod(x, 1); 324 if (x - i >= 0.5) { 325 return (i + 1); 326 } 327 return (i); 328 } 329 330 define round(x, d) { 331 auto k; 332 k = 10 ^ d; 333 return (round0(x * k) / k); 334 } 335 336 define r(x, d) { 337 return (round(x, d)); 338 } 339 340 define r0(x) { 341 return (round0(x)); 342 } 343 344 define epa(x) { 345 return (epanechnikov(x)); 346 } 347 348 define epanechnikov(x) { 349 if ((x < -1) || (x > 1)) return (0); 350 return (3 / 4 * (1 - (x * x))); 351 } 352 353 define gauss(x) { 354 return (gaussian(x)); 355 } 356 357 define gaussian(x) { 358 return (e(-(x * x))); 359 } 360 361 define tricube(x) { 362 auto a, b, c, d; 363 if ((x < -1) || (x > 1)) return (0); 364 if (x >= 0) a = x else a = -x; 365 b = a * a * a; 366 c = 1 - b; 367 d = c * c * c; 368 return (70 / 81 * d); 369 } 370 371 define fac(x) { 372 auto f, i; 373 if (x < 0) return (0); 374 f = 1; 375 for (i = x; i >= 2; i--) f *= i; 376 return (f); 377 } 378 379 define f(x) { 380 return (fac(x)); 381 } 382 383 define fact(x) { 384 return (fac(x)); 385 } 386 387 define factorial(x) { 388 return (fac(x)); 389 } 390 391 define per(n, k) { 392 auto p, i; 393 if (n < k) return (0); 394 p = 1; 395 for (i = n; i >= n - k + 1; i--) p *= i; 396 return (p); 397 } 398 399 define p(n, k) { 400 return (per(n, k)); 401 } 402 403 define perm(n, k) { 404 return (per(n, k)); 405 } 406 407 define permut(n, k) { 408 return (per(n, k)); 409 } 410 411 define permutations(n, k) { 412 return (per(n, k)); 413 } 414 415 /* the name "c" is already used for the cosine function in "bc" */ 416 417 define choose(n, k) { 418 return (com(n, k)); 419 } 420 421 define com(n, k) { 422 if (n < k) return (0); 423 return (per(n, k) / fac(k)); 424 } 425 426 define comb(n, k) { 427 return (com(n, k)); 428 } 429 430 define combin(n, k) { 431 return (com(n, k)); 432 } 433 434 define combinations(n, k) { 435 return (com(n, k)); 436 } 437 438 define gcd(x, y) { 439 return (x * y / lcm(x, y)); 440 } 441 442 define lcm(x, y) { 443 auto a, b, z; 444 445 /* the LCM is defined only for positive integers */ 446 /* if (mod(x, 1) != 0 || x < 1 || mod(y, 1) != 0 || y < 1) return (0); */ 447 /* if (mod(x, 1) != 0) return (0); */ 448 if (x < 1) return (0); 449 /* if (mod(y, 1) != 0) return (0); */ 450 if (y < 1) return (0); 451 452 a = min(x, y); 453 b = max(x, y); 454 455 z = b; 456 while (mod(z, a) != 0) { z += b; } 457 return (z); 458 } 459 460 define dbin(x, n, p) { 461 return (dbinom(x, n, p)); 462 } 463 464 define pbin(x, n, p) { 465 return (pbinom(x, n, p)); 466 } 467 468 define dbinom(x, n, p) { 469 return (com(n, x) * (p ^ x) * ((1 - p) ^ (n - x))); 470 } 471 472 /* pbinom inefficiently repeats calculations for now, which keeps it simple */ 473 define pbinom(x, n, p) { 474 auto k, t; 475 t = 0; 476 for (k = 0; k <= n; k++) t += dbinom(k, n, p); 477 return (t); 478 } 479 480 /* pbinomfast may be wrong, while the simpler pbinom seems correct */ 481 define pbinomfast(x, n, p) { 482 auto a, b, d, q, k, t; 483 if ((p < 0) || (p > 1)) return (0); 484 if (x < 0) return (0); 485 if (x >= n) return (1); 486 a = 1; 487 q = 1 - p; 488 b = b ^ n; 489 d = 1; 490 t = 0; 491 for (k = 0; k < x;) { 492 t += (per(n, k) / d) * a * b; 493 a *= p; 494 b /= q; 495 k++; 496 d *= k; 497 } 498 /* remember the last loop, where k == x */ 499 t += (per(n, k) / d) * a * b; 500 return (t); 501 } 502 503 define dexp(x, r) { 504 if (r < 0) return (0); 505 return (r * e(-r * x)); 506 } 507 508 define pexp(x, r) { 509 if (r < 0) return (0); 510 return (1 - e(-r * x)); 511 } 512 513 define dpois(x, l) { 514 return ((l ^ x) * e(-l) / fac(x)); 515 } 516 517 define ppois(x, l) { 518 auto t, d, i; 519 t = 1; 520 d = 1; 521 for (i = 1; i <= l; i++) { 522 d *= i; 523 t += (l ^ i) / d; 524 } 525 return (e(-l) * t); 526 } 527 528 define sgn(x) { 529 if (x > 0) return (1); 530 if (x < 0) return (-1); 531 return (0); 532 } 533 534 define sign(x) { 535 return (sgn(x)); 536 } 537 538 define logistic(x) { 539 return (1 / (1 + e(-x))); 540 } 541 542 $(echo "${arg}" | sed 's-^+--g; s-_--g; s-\*\*-^-g; s-\[-(-g; s-\]-)-g') 543 ENDOFSCRIPT 544 545 done | 546 # ensure the result shows at least a zero before the decimal dot, then 547 # rid the result of trailing zero decimals and/or trailing decimal dots 548 sed -E 's-^\.-0.-; s/^-\./-0./; s-(\.[0-9]*[1-9])0+$-\1-; s-\.0*$--'