Created
August 8, 2019 21:33
-
-
Save jaytaylor/1166da29fab464c69662b36de61907bd to your computer and use it in GitHub Desktop.
Math functions from python as bash "builtins".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
## | |
# | |
# @author Jay E. Taylor <[email protected]> | |
# | |
# @description Math functions for bash! | |
# | |
## | |
_create_python_based_math_funcs() { | |
# 0-ary | |
for func in math.e math.pi ; do | |
eval " | |
${func/math\./}() { | |
python -c ' | |
import math, sys | |
sys.stdout.write(\"%s\\n\" % (${func},)) | |
' | |
} | |
export -f ${func/math\./}" || echo "ERROR: defining func '${func/math\./}'" 1>&2 | |
done | |
# unary | |
for func in int float math.acos math.acosh math.asin math.asinh math.atan math.atan2 math.atanh math.ceil math.cos math.cosh math.degrees math.factorial math.floor math.gamma math.log math.log10 math.log1p math.modf math.radians math.sin math.sinh math.sqrt math.tan math.tanh ; do | |
eval " | |
${func/math\./}() { | |
python -c ' | |
import math, sys | |
if len(sys.argv) > 1: | |
map(lambda x: sys.stdout.write(\"%s\\n\" % (${func}(float(x.strip())),)), sys.argv[1:]) | |
else: | |
for x in sys.stdin: | |
sys.stdout.write(\"%s\\n\" % (${func}(float(x.strip())),)) | |
' \"\$@\" | |
} | |
export -f ${func/math\./}" || echo "ERROR: defining func '${func/math\./}'" 1>&2 | |
done | |
# binary | |
for func in math.pow math.fmod ; do | |
eval " | |
${func/math\./}() { | |
python -c ' | |
import math, sys | |
if len(sys.argv) > 1: | |
sys.stdout.write(\"%s\\n\" % (${func}(float(sys.argv[1].strip()), float(sys.argv[2].strip())),)) | |
else: | |
x, y = None, None | |
for line in sys.stdin: | |
if x is None: | |
x = line.strip() | |
else: | |
y = line.strip() | |
if None not in (x, y): | |
if 0 not in (len(x), len(y)): | |
sys.stdout.write(\"%s\\n\" % (${func}(float(x), float(y)),)) | |
x, y = None, None | |
' \"\$@\" | |
} | |
export -f ${func/math\./}" || echo "ERROR: defining func '${func/math\./}'" 1>&2 | |
done | |
# n-ary | |
for func in min max math.fsum ; do | |
eval " | |
${func/math\./}() { | |
python -c ' | |
import math, sys | |
if len(sys.argv) > 1: | |
args = sys.argv[1:] | |
else: | |
args = sys.stdin.readlines() | |
sys.stdout.write(\"%s\\n\" % (${func}(map(lambda x: float(x.strip()), filter(lambda x: len(x.strip()) > 0, args))),)) | |
' \"\$@\" | |
} | |
export -f ${func/math\./}" || echo "ERROR: defining func '${func/math\./}'" 1>&2 | |
done | |
} | |
_create_python_based_math_funcs | |
unset _create_python_based_math_funcs | |
#echo "$@" | |
if [ "${BASH_SOURCE[0]}" = "${0}" ] && [[ "$1{:-}" =~ tests? ]] ; then | |
assert() { | |
if [ -z "${2:-}" ] ; then | |
echo 'ERROR: assert: missing operator' 1>&2 | |
echo '' 1>&2 | |
echo 'usage: assert [LHS] [OPERATOR] [RHS]' 1>&2 | |
echo '' 1>&2 | |
echo 'examples:' 1>&2 | |
echo '' 1>&2 | |
echo " assert 2 '==' 3 // \$?=0 " 1>&2 | |
echo '' 1>&2 | |
echo " assert hello '==' hello // \$?=0 " 1>&2 | |
echo '' 1>&2 | |
echo " assert 4 '>' 3" 1>&2 | |
echo ' // output: ASSERTION FAILED: $1 not == $3' 1>&2 | |
echo '' 1>&2 | |
return 1 | |
fi | |
local op | |
op="$2" | |
case "${op}" in | |
=|==|eq) | |
if [ "${1:-}" = "${3:-}" ] ; then | |
return 0 | |
fi | |
;; | |
'!='|ne) | |
if [ "${1:-}" != "${3:-}" ] ; then | |
return 0 | |
fi | |
;; | |
'<'|lt) | |
if [ "${1:-}" -lt "${3:-}" ] ; then | |
return 0 | |
fi | |
;; | |
'<='|le) | |
if [ "${1:-}" -le "${3:-}" ] ; then | |
return 0 | |
fi | |
;; | |
'>'|gt) | |
if [ "${1:-}" -gt "${3:-}" ] ; then | |
return 0 | |
fi | |
;; | |
'>='|ge) | |
if [ "${1:-}" -ge "${3:-}" ] ; then | |
return 0 | |
fi | |
;; | |
*) | |
echo "ERROR: assert: unrecognized operator: ${op}" 1>&2 | |
return 1 | |
;; | |
esac | |
echo "ASSERTION FAILED: '${1:-}' not ${op} '${3:-}'" 1>&2 | |
return 1 | |
} | |
export -f assert | |
run_module_tests() { | |
local rc | |
rc=0 | |
assert "$(min 3 4)" '==' 3.0 | |
rc=$((rc+$?)) | |
assert "$(min 3 4 | int)" '==' 3 | |
rc=$((rc+$?)) | |
assert "$(min 19 3999 24 4)" '==' 4.0 | |
rc=$((rc+$?)) | |
assert "$(min 19 -4 3999 24 | int)" '==' -4 | |
rc=$((rc+$?)) | |
assert "$(max 3 4)" '==' 4.0 | |
rc=$((rc+$?)) | |
assert "$(max 19 3999 24 4)" '==' 3999.0 | |
rc=$((rc+$?)) | |
assert "$(floor 3.50001)" '==' 3.0 | |
rc=$((rc+$?)) | |
assert "$(ceil 3.50001)" '==' 4.0 | |
rc=$((rc+$?)) | |
assert "$(float 11)" '==' 11.0 | |
rc=$((rc+$?)) | |
assert "$(int 11.5)" '==' 11 | |
rc=$((rc+$?)) | |
assert "$(pow 2 5)" '==' 32.0 | |
rc=$((rc+$?)) | |
assert "$(fsum 1 2 3 4)" '==' 10.0 | |
rc=$((rc+$?)) | |
assert "$(echo -e "-1\n0\n99\n91239\n-01249" | min | int)" '==' -1249 | |
rc=$((rc+$?)) | |
assert "$(echo -e " -1 \n 0\n99 \n 91239 \n -01249" | min | int)" '==' -1249 | |
rc=$((rc+$?)) | |
assert "$(echo '3 | |
4 | |
5 | |
6 | |
7 | |
' | min)" '==' 3.0 | |
rc=$((rc+$?)) | |
assert "$(pi)" '==' 3.14159265359 | |
rc=$((rc+$?)) | |
assert "$(echo '4 | |
5 | |
6' | fmod)" '==' 4.0 | |
rc=$((rc+$?)) | |
if [ "${rc}" -ne 0 ] ; then | |
echo "ERROR: 1 or more tests failed, rc=${rc}" 1>&2 | |
return "${rc}" | |
fi | |
echo 'INFO: All tests passed OK' 1>&2 | |
} | |
export -f run_module_tests | |
run_module_tests || echo "FAILED: test suite returned non-zero status code: $?" 1>&2 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment