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 [expression] 27 # 28 # CAlculator is an easier-to-use version of bc (basic calculator) where 29 # 30 # - you give the expression as an argument, while bc reads it from stdin 31 # - you can use either ** or ^ to raise powers 32 # - you can use [ and ] or ( and ) interchangeably 33 # - the number of decimals is 6 by default 34 # - automatically includes the extended bc math library via option -l 35 # - there are several extra predefined values and functions (see below) 36 # 37 # 38 # Predefined functions and values, besides the ones `bc` already comes with 39 # 40 # 41 # nano a billionth 42 # micro a millionth 43 # milli a thousandth 44 # 45 # kilo 1000 46 # mega a million 47 # giga a billion 48 # tera a thousand billion / trillion 49 # peta a million billion / quadrillion 50 # exa a quintillion 51 # zetta a sixtillion 52 # 53 # binkilo 1024 54 # binmega 1024 * 1024 55 # bingiga 1024 * 1024 * 1024 56 # bintera 1024 * 1024 * 1024 * 1024 57 # binpeta 1024 * 1024 * 1024 * 1024 * 1024 58 # binexa 1024 * 1024 * 1024 * 1024 * 1024 * 1024 59 # binzetta 1024 * 1024 * 1024 * 1024 * 1024 * 1024 60 # 61 # kb 1024 62 # mb 1024 * 1024 63 # gb 1024 * 1024 * 1024 64 # tb 1024 * 1024 * 1024 * 1024 65 # pb 1024 * 1024 * 1024 * 1024 * 1024 66 # 67 # kib 1024 68 # mib 1024 * 1024 69 # gib 1024 * 1024 * 1024 70 # tib 1024 * 1024 * 1024 * 1024 71 # pib 1024 * 1024 * 1024 * 1024 * 1024 72 # 73 # mol 1 mole, the exact avogadro number 74 # mole the exact avogadro number 75 # 76 # cup2l liters in 1 cup 77 # floz2ml milliliters in 1 fluid ounce 78 # gal2l liters in 1 gallon 79 # in2cm centimeters in 1 inch 80 # lb2kg kilograms in 1 pound 81 # mi2km kilometers in 1 mile 82 # mpg2kpl kilometers per liter in 1 mile per gallon 83 # nmi2km kilometers in 1 nautical mile 84 # oz2g grams in 1 weight ounce 85 # psi2pa pascals in 1 pound per square-inch 86 # ton2kg kilograms in 1 american ton 87 # yd2m meters in 1 yard 88 # 89 # eu() approximate Euler's number 90 # pi() approximate any circle's circumference/diameter ratio 91 # tau() 2*pi; approximate any circle's circumference/radius ratio 92 # abs(x) the absolute value function 93 # cbrt(x) the cube root function 94 # exp(x) the exponential function; alias for bc's e 95 # ln(x) natural logarithm; an alias for bc's l 96 # log(x) natural logarithm; an alias for bc's l 97 # log2(x) base-2 logarithm 98 # log10(x) base-10 logarithm 99 # sin(x) the sine 100 # cos(x) the cosine 101 # atan(x) the arc-tangent 102 # hypot(x, y) length of the hypothenuse 103 # mod(x, y) modulus function, which is different from the % operator 104 # round0(x) round to integer 105 # round1(x) round to 1 decimal place 106 # ... 107 # round20(x) round to 20 decimal places 108 # round(x, d) round to the given number of decimal digits; can be negative 109 # fac(x) factorial 110 # com(n, k) number of combinations 111 # comb(n, k) number of combinations 112 # p(n, k) number of permutations 113 # per(n, k) number of permutations 114 # perm(n, k) number of permutations 115 # lcm(x, y) least-common multiple 116 # gcd(x, y) greatest-common divisor 117 # min(x, y) the minimum of 2 numbers 118 # max(x, y) the maximum of 2 numbers 119 # isprime(x) check if number is prime 120 # lgamma(x) lanczos approximation of the log-gamma function 121 # lbeta(x, y) lanczos approximation of the log-beta function 122 # gamma(x) approximation of the gamma function 123 # beta(x) approximation of the beta function 124 # logistic(x) the logistic function 125 # mix(x, y, k) interpolate 2 numbers; extrapolates when k is outside [0, 1] 126 # clamp(x, min, max) trap a number in the range given 127 # wrap(x, min, max) normalize a number in the range given 128 129 130 # handle help options 131 case "$1" in 132 -h|--h|-help|--help) 133 # show help message, extracting the info-comment at the start 134 # of this file, and quit 135 awk '/^# +ca/, /^$/ { gsub(/^# ?/, ""); print }' "$0" 136 exit 0 137 ;; 138 esac 139 140 ( 141 142 # ensure output is all on 1 line, define several funcs and values, then 143 # inject the expression given as this script's arguments, transformed 144 # according to the rules described above 145 BC_LINE_LENGTH=0 bc -l << ENDOFSCRIPT 146 scale = 20; 147 148 femto = 0.000000000000001; 149 pico = 0.000000000001; 150 nano = 0.000000001; 151 micro = 0.000001; 152 milli = 0.001; 153 154 kilo = 1000; 155 mega = 1000 * kilo; 156 giga = 1000 * mega; 157 tera = 1000 * giga; 158 peta = 1000 * tera; 159 exa = 1000 * peta; 160 zetta = 1000 * exa; 161 162 binkilo = 1024; 163 binmega = 1024 * binkilo; 164 bingiga = 1024 * binmega; 165 bintera = 1024 * bingiga; 166 binpeta = 1024 * bintera; 167 binexa = 1024 * binpeta; 168 binzetta = 1024 * binexa; 169 170 kb = 1024; 171 mb = 1024 * kb; 172 gb = 1024 * mb; 173 tb = 1024 * gb; 174 pb = 1024 * tb; 175 eb = 1024 * pb; 176 zb = 1024 * eb; 177 178 kib = 1024; 179 mib = 1024 * kib; 180 gib = 1024 * mib; 181 tib = 1024 * gib; 182 pib = 1024 * tib; 183 zib = 1024 * pib; 184 185 mol = 602214076000000000000000; 186 mole = 602214076000000000000000; 187 188 cup2l = 0.23658824; 189 floz2ml = 29.5735295625; 190 ft2mt = 0.3048; 191 gal2l = 3.785411784; 192 in2cm = 2.54; 193 lb2kg = 0.45359237; 194 mi2km = 1.609344; 195 mpg2kpl = 0.425143707; 196 nmi2km = 1.852; 197 oz2g = 28.349523125 198 psi2pa = 6894.757293168; 199 ton2kg = 907.18474; 200 yd2m = 0.9144; 201 202 ft2mt = ft2m; 203 ga2l = gal2l; 204 ga2lt = gal2l; 205 gal2lt = gal2l; 206 lb2k = lb2kg; 207 nm2km = nmi2km; 208 tn2kg = ton2kg; 209 yd2mt = yd2m; 210 211 hour = 3600; 212 day = 24 * hour; 213 week = 7 * day; 214 215 hr = hour; 216 wk = week; 217 218 define eu() { return e(1); } 219 define pi() { return 4*a(1); } 220 define tau() { return 8*a(1); } 221 222 define deg(x) { return 180 * x / pi(); } 223 define rad(x) { return pi() * x / 180; } 224 225 define abs(x) { if (x >= 0) return x else return -x; } 226 define cbrt(x) { return x^(1/3); } 227 define exp(x) { return e(x); } 228 define ln(x) { return l(x); } 229 # define log(base, x) { return l(x) / l(base); } 230 define log(x) { return l(x); } 231 define log2(x) { return l(x) / l(2); } 232 define log10(x) { return l(x) / l(10); } 233 234 define sin(x) { return s(x); } 235 define cos(x) { return c(x); } 236 define tan(x) { return s(x) / c(x); } 237 define cot(x) { return c(x) / s(x); } 238 define atan(x) { return a(x); } 239 define sinh(x) { return (e(x) - e(-x)) / 2; } 240 define cosh(x) { return (e(x) + e(-x)) / 2; } 241 define tanh(x) { return (e(x) - e(-x)) / (e(x) + e(-x)); } 242 define coth(x) { return (e(x) + e(-x)) / (e(x) - e(-x)); } 243 define hypot(x, y) { return sqrt(x*x + y*y); } 244 define sinc(x) { if (x == 0) return 1 else return s(x) / x; } 245 246 define min(x, y) { if (x <= y) return x else return y; } 247 define max(x, y) { if (x >= y) return x else return y; } 248 249 define mod(x, y) { 250 auto s, m; 251 s = scale; 252 scale = 0; 253 m = x % y; 254 scale = s; 255 return m; 256 } 257 258 define round0(x) { 259 auto i; 260 i = x - mod(x, 1); 261 if (x - i >= 0.5) { 262 return i + 1; 263 } 264 return i; 265 } 266 267 define round(x, d) { 268 auto k; 269 k = 10^d; 270 return round0(x * k) / k; 271 } 272 273 define round1(x) { return round(x, 1); } 274 define round2(x) { return round(x, 2); } 275 define round3(x) { return round(x, 3); } 276 define round4(x) { return round(x, 4); } 277 define round5(x) { return round(x, 5); } 278 define round6(x) { return round(x, 6); } 279 define round7(x) { return round(x, 7); } 280 define round8(x) { return round(x, 8); } 281 define round9(x) { return round(x, 9); } 282 define round10(x) { return round(x, 10); } 283 define round11(x) { return round(x, 11); } 284 define round12(x) { return round(x, 12); } 285 define round13(x) { return round(x, 13); } 286 define round14(x) { return round(x, 14); } 287 define round15(x) { return round(x, 15); } 288 define round16(x) { return round(x, 16); } 289 define round17(x) { return round(x, 17); } 290 define round18(x) { return round(x, 18); } 291 define round19(x) { return round(x, 19); } 292 define round20(x) { return round(x, 20); } 293 294 define fac(x) { 295 auto f, i; 296 if (x < 0) { return 0; } 297 f = 1; 298 for (i = x; i >= 2; i--) { f *= i; } 299 return f; 300 } 301 302 define per(n, k) { 303 auto p, i; 304 if (n < k) { return 0; } 305 p = 1; 306 for (i = n; i >= n - k + 1; i--) { p *= i; } 307 return p; 308 } 309 310 define p(n, k) { return per(n, k); } 311 define perm(n, k) { return per(n, k); } 312 313 define com(n, k) { 314 if (n < k) { return 0; } 315 return per(n, k) / fac(k); 316 } 317 318 define comb(n, k) { return com(n, k); } 319 320 define gcd(x, y) { return x * y / lcm(x, y); } 321 322 define lcm(x, y) { 323 auto a, b, z; 324 325 /* the LCM is defined only for positive integers */ 326 if (mod(x, 1) != 0 || x < 1 || mod(y, 1) != 0 || y < 1) { return 0; } 327 328 a = min(x, y); 329 b = max(x, y); 330 331 z = b; 332 while (mod(z, a) != 0) { z += b; } 333 return z; 334 } 335 336 define isprime(n) { 337 auto div; 338 339 if (mod(n, 1) != 0 || n < 2) { return 0; } 340 341 /* 2 is the only even prime number */ 342 if (mod(n, 2) == 0) { return n == 2; } 343 344 /* 345 even numbers have already been handled, so only odd numbers 346 make it here: only check up to the square-root, so the loop 347 has O(n**1.5) time-complexity 348 */ 349 for (div = 3; div <= sqrt(n); div += 2) { 350 if (mod(n, div) == 0) { return 0; } 351 } 352 return 1; 353 } 354 355 /* 356 I adapted an implementation of Lanczos' log-gamma approximation I found at 357 http://introcs.cs.princeton.edu/java/91float/Gamma.java.html 358 */ 359 define lgamma(x) { 360 auto tmp, a, b, c, d; 361 tmp = (x - 0.5) * l(x + 4.5) - (x + 4.5); 362 a = 76.18009173 / (x + 0) - 86.50532033 / (x + 1); 363 b = 24.01409822 / (x + 2) - 1.231739516 / (x + 3); 364 c = 0.00120858003 / (x + 4) - 0.00000536382 / (x + 5); 365 d = sqrt(2 * 3.14159265358979323844); 366 #d = sqrt(2 * pi()); 367 return tmp + l((1.0 + a + b + c) * d); 368 } 369 370 define lbeta(x, y) { return lgamma(x) + lgamma(y) - lgamma(x + y); } 371 372 define gamma(x) { return e(lgamma(x)); } 373 define beta(x, y) { return e(lbeta(x, y)); } 374 define logistic(x) { return 1 / (1 + e(-x)); } 375 376 define mix(x, y, k) { return x + (1 - k) * (y - x); } 377 define clamp(x, min, max) { return max(min(max, x), min); } 378 define wrap(x, min, max) { return (x - min) / (max - min); } 379 define unwrap(x, min, max) { return (max - min) * x + min; } 380 381 $(echo "$@" | sed 's-^+--g; s-_--g; s-\*\*-^-g; s-\[-(-g; s-\]-)-g') 382 ENDOFSCRIPT 383 384 # ensure the result shows at least a zero before the decimal dot, then 385 # rid the result of trailing zero decimals and/or trailing decimal dots 386 ) | sed -E 's-^\.-0.-; s/^-\./-0./; s-(\.[0-9]+[1-9]+)0+$-\1-; s-\.0*$--'