File: ca.sh
   1 #!/bin/sh
   2 
   3 # The MIT License (MIT)
   4 #
   5 # Copyright © 2020-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 # handle help options
  42 case "$1" in
  43     -h|--h|-help|--help)
  44         awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  45         exit 0
  46     ;;
  47 esac
  48 
  49 if [ $# -eq 0 ]; then
  50     awk '/^# +ca /, /^$/ { gsub(/^# ?/, ""); print }' "$0"
  51     exit 0
  52 fi
  53 
  54 # default max-accuracy decimals to use for calculations
  55 scale=25
  56 
  57 # ensure each output is all on 1 line, define several funcs and values, then
  58 # inject the expressions given as this script's arguments, transforming them
  59 # according to the rules described above
  60 for arg in "$@"; do
  61     # printf "\e[7m%s\e[0m\n" "${arg}" > /dev/stderr
  62     [ $# -ge 2 ] && printf "\e[7m%s\e[0m\n" "${arg}" > /dev/stderr
  63 
  64     BC_LINE_LENGTH=0 bc -l << ENDOFSCRIPT
  65 scale = ${scale};
  66 
  67 femto = 0.000000000000001;
  68 pico = 0.000000000001;
  69 nano = 0.000000001;
  70 micro = 0.000001;
  71 milli = 0.001;
  72 
  73 kilo = 1000;
  74 mega = 1000 * kilo;
  75 giga = 1000 * mega;
  76 tera = 1000 * giga;
  77 peta = 1000 * tera;
  78 exa =  1000 * peta;
  79 zetta =  1000 * exa;
  80 
  81 binkilo = 1024;
  82 binmega = 1024 * binkilo;
  83 bingiga = 1024 * binmega;
  84 bintera = 1024 * bingiga;
  85 binpeta = 1024 * bintera;
  86 binexa = 1024 * binpeta;
  87 binzetta = 1024 * binexa;
  88 
  89 kb = 1024;
  90 mb = 1024 * kb;
  91 gb = 1024 * mb;
  92 tb = 1024 * gb;
  93 pb = 1024 * tb;
  94 eb = 1024 * pb;
  95 zb = 1024 * eb;
  96 
  97 kib = 1024;
  98 mib = 1024 * kib;
  99 gib = 1024 * mib;
 100 tib = 1024 * gib;
 101 pib = 1024 * tib;
 102 zib = 1024 * pib;
 103 
 104 mol = 602214076000000000000000;
 105 mole = 602214076000000000000000;
 106 
 107 cup2l = 0.23658824;
 108 floz2l = 0.0295735295625;
 109 floz2ml = 29.5735295625;
 110 ft2m = 0.3048;
 111 gal2l = 3.785411784;
 112 in2cm = 2.54;
 113 lb2kg = 0.45359237;
 114 mi2km = 1.609344;
 115 mpg2kpl = 0.425143707;
 116 nmi2km = 1.852;
 117 oz2g = 28.349523125
 118 psi2pa = 6894.757293168;
 119 ton2kg = 907.18474;
 120 yd2m = 0.9144;
 121 
 122 ga2l = gal2l;
 123 nm2km = nmi2km;
 124 tn2kg = ton2kg;
 125 
 126 hour = 3600;
 127 day = 24 * hour;
 128 week = 7 * day;
 129 
 130 hr = hour;
 131 wk = week;
 132 
 133 define ftin(f, i) {
 134     return (0.3048 * f + 0.0254 * i);
 135 }
 136 
 137 define lboz(l, o) {
 138     return (0.45359237 * l + 0.028349523 * o);
 139 }
 140 
 141 define eu() {
 142     return (e(1));
 143 }
 144 
 145 define euler() {
 146     return (e(1));
 147 }
 148 
 149 define pi() {
 150     return (4*a(1));
 151 }
 152 
 153 define tau() {
 154     return (8*a(1));
 155 }
 156 
 157 define deg(x) {
 158     return (180 * x / pi());
 159 }
 160 
 161 define rad(x) {
 162     return (pi() * x / 180);
 163 }
 164 
 165 define abs(x) {
 166     if (x >= 0) return (x);
 167     return (-x);
 168 }
 169 
 170 define exp(x) {
 171     return (e(x));
 172 }
 173 
 174 define j0(x) {
 175     return (j(0, x));
 176 }
 177 
 178 define j1(x) {
 179     return (j(1, x));
 180 }
 181 
 182 define ln(x) {
 183     return (l(x));
 184 }
 185 
 186 define log(x) {
 187     return (l(x));
 188 }
 189 
 190 # define log2(x) {
 191 #     return (l(x) / l(2));
 192 # }
 193 
 194 define log2(x) {
 195     auto r, n;
 196     if (x <= 0) return (l(x) / l(2));
 197 
 198     r = 0;
 199     for (n = x; n > 1; n /= 2) r += 1;
 200 
 201     if (n == 1) return (r);
 202     return (l(x) / l(2));
 203 }
 204 
 205 # define log10(x) {
 206 #     return (l(x) / l(10));
 207 # }
 208 
 209 define log10(x) {
 210     auto r, n;
 211     if (x <= 0) return (l(x) / l(10));
 212 
 213     r = 0;
 214     for (n = x; n > 1; n /= 10) r += 1;
 215 
 216     if (n == 1) return (r);
 217     return (l(x) / l(10));
 218 }
 219 
 220 define sin(x) {
 221     return (s(x));
 222 }
 223 
 224 define cos(x) {
 225     return (c(x));
 226 }
 227 
 228 define tan(x) {
 229     return (s(x) / c(x));
 230 }
 231 
 232 define cot(x) {
 233     return (c(x) / s(x));
 234 }
 235 
 236 define atan(x) {
 237     return (a(x));
 238 }
 239 
 240 define sinh(x) {
 241     return ((e(x) - e(-x)) / 2);
 242 }
 243 
 244 define cosh(x) {
 245     return ((e(x) + e(-x)) / 2);
 246 }
 247 
 248 define tanh(x) {
 249     return ((e(x) - e(-x)) / (e(x) + e(-x)));
 250 }
 251 
 252 define coth(x) {
 253     return ((e(x) + e(-x)) / (e(x) - e(-x)));
 254 }
 255 
 256 define hypot(x, y) {
 257     return (sqrt(x*x + y*y));
 258 }
 259 
 260 define sinc(x) {
 261     if (x == 0) return (1);
 262     return (s(x) / x);
 263 }
 264 
 265 define min(x, y) {
 266     if (x <= y) return (x);
 267     return (y);
 268 }
 269 
 270 define max(x, y) {
 271     if (x >= y) return (x);
 272     return (y);
 273 }
 274 
 275 define mod(x, y) {
 276     auto s, m;
 277     s = scale;
 278     scale = 0;
 279     m = x % y;
 280     scale = s;
 281     return (m);
 282 }
 283 
 284 define mod1(x) {
 285     return (mod(x, 1));
 286 }
 287 
 288 define modf(x) {
 289     return (mod(x, 1));
 290 }
 291 
 292 define round0(x) {
 293     auto i;
 294     i = x - mod(x, 1);
 295     if (x - i >= 0.5) {
 296         return (i + 1);
 297     }
 298     return (i);
 299 }
 300 
 301 define round(x, d) {
 302     auto k;
 303     k = 10^d;
 304     return (round0(x * k) / k);
 305 }
 306 
 307 define r(x, d) {
 308     return (round(x, d));
 309 }
 310 
 311 define r0(x) {
 312     return (round0(x));
 313 }
 314 
 315 define fac(x) {
 316     auto f, i;
 317     if (x < 0) return (0);
 318     f = 1;
 319     for (i = x; i >= 2; i--) { f *= i; }
 320     return (f);
 321 }
 322 
 323 define f(x) {
 324     return (fac(x));
 325 }
 326 
 327 define fact(x) {
 328     return (fac(x));
 329 }
 330 
 331 define factorial(x) {
 332     return (fac(x));
 333 }
 334 
 335 define per(n, k) {
 336     auto p, i;
 337     if (n < k) return (0);
 338     p = 1;
 339     for (i = n; i >= n - k + 1; i--) { p *= i; }
 340     return (p);
 341 }
 342 
 343 define p(n, k) {
 344     return (per(n, k));
 345 }
 346 
 347 define perm(n, k) {
 348     return (per(n, k));
 349 }
 350 
 351 define com(n, k) {
 352     if (n < k) return (0);
 353     return (per(n, k) / fac(k));
 354 }
 355 
 356 define comb(n, k) {
 357     return (com(n, k));
 358 }
 359 
 360 define gcd(x, y) {
 361     return (x * y / lcm(x, y));
 362 }
 363 
 364 define lcm(x, y) {
 365     auto a, b, z;
 366 
 367     /* the LCM is defined only for positive integers */
 368     # if (mod(x, 1) != 0 || x < 1 || mod(y, 1) != 0 || y < 1) { return 0; }
 369     # if (mod(x, 1) != 0) return (0);
 370     if (x < 1) return (0);
 371     # if (mod(y, 1) != 0) return (0);
 372     if (y < 1) return (0);
 373 
 374     a = min(x, y);
 375     b = max(x, y);
 376 
 377     z = b;
 378     while (mod(z, a) != 0) { z += b; }
 379     return (z);
 380 }
 381 
 382 define sgn(x) {
 383     if (x > 0) return (1);
 384     if (x < 0) return (-1);
 385     return (0);
 386 }
 387 
 388 define sign(x) {
 389     return (sgn(x));
 390 }
 391 
 392 define logistic(x) {
 393     return (1 / (1 + e(-x)));
 394 }
 395 
 396 $(echo "${arg}" | sed 's-^+--g; s-_--g; s-\*\*-^-g; s-\[-(-g; s-\]-)-g')
 397 ENDOFSCRIPT
 398 
 399 done |
 400 # ensure the result shows at least a zero before the decimal dot, then
 401 # rid the result of trailing zero decimals and/or trailing decimal dots
 402 sed -E 's-^\.-0.-; s/^-\./-0./; s-(\.[0-9]+[1-9]+)0+$-\1-; s-\.0*$--'