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