#!/bin/bash
# run with -h to see help
VERSION="0.6.1 [26 Jan 2026]"
set -o pipefail

function domath() {
	tr ' ' '\n' <"${TMPF}1"|sed '/^$/d;s/,//g;s/\r//g'|sort -n|awk -v PRECISION="$PRECISION" '
	  BEGIN {
	    c = 0;
	    sum = 0;
	  }
	  $1 ~ /^(\-)?[0-9]*(\.[0-9]*)?$/ {
	    a[c++] = $1;
	    sum += $1;
	  }
	  END {
			if (c>0) {
				ave = sum / c;
				if( (c % 2) == 1 ) {
					median = a[ int(c/2) ];
				} else {
					median = ( a[c/2] + a[c/2-1] ) / 2;
				}
			}
	    OFS="\t";
		printf "%." PRECISION "f %." PRECISION "f %." PRECISION "f %." PRECISION "f %." PRECISION "f %." PRECISION "f \n",sum,c, ave, median, a[0], a[c-1];
	  }
	'
}

PRECISION=4
while getopts ":dfhlmp:w" optname; do
	case "$optname" in
		"d") DEBUG=y;;
		"f") FORCE=y;;
		"h") HELP=y;;
		"l") CHANGELOG=y;;
		"m") SHOW_ALL_DIGITS=y;;
		"p") PRECISION=$OPTARG;;
		"w") COLUMNS=30000;; #suppress line-breaking
		"?") echo "Unknown option $OPTARG">&2; exit 1;;
		":") echo "No argument value for option $OPTARG">&2; exit 1;;
		*) # Should not occur
			echo "Unknown error while processing options">&2; exit 1;;
	esac
done
shift $((OPTIND-1))

# help/changelog info
[[ -z $FNUM ]] && FNUM="$*"
TMPF=$(mktemp -u)
if [[ -z $CHANGELOG ]]; then
	timeout 5 grep . >"${TMPF}1" # put piped data into ${TMPF}1 or create null file if pipe is missing or empty
	[[ -n $DEBUG ]] && ls -l "${TMPF}1" && cat "${TMPF}1"
	[[ -s ${TMPF}1 || -n $FORCE ]] || { HELP=y; rm -- "${TMPF}1"; }
fi

if [[ -n $HELP$CHANGELOG ]]; then
	THIS=$(basename "$0")
	[[ -z $COLUMNS ]] && { COLUMNS=$(stty size 2>/dev/null||echo 30000); COLUMNS=${COLUMNS##* }; }
	echo -e "\n$THIS v$VERSION by Dominic (try -h for help)\n${THIS//?/=}\n"
fi
if [[ -n $HELP ]]; then
	echo -e "\
Returns statistical value(s) for a piped-in list of numbers - integers or decimals, space or line-separated. 6 values (sum count mean median minimum maximum) \
are output, columnised with a descriptive header; or if one or more of these are added as parameters (case-insensitive and only the first three letters are \
significant, tot(al) can be used instead of sum and ave(rage) instead of mean) then only the specified value(s) are output, with no header or columnising.

Note the optional -p setting for the maximum number of decimal places in each output result (default: $PRECISION). Unless the -m setting is used, results are \
shown with as few decimal places as possible that give an accurate result, subject to the maximum setting (but the calculations are at a much higher level of precision).

Options: -f   - do not show help on null input (recommended that you always use this option)
         -h   - see this help
         -m   - always show the maximum number of decimal places even when not required i.e. with trailing zero(es)
         -p n - show a maximum of n decimal places for output value(s) (default: 4)
         -l   - see changelog

Examples:
    \$ echo \"1 2 3 4 5 10\"|$THIS -f
    sum  count  mean    median  minimum  maximum
    25   6      4.1667  3.5     1        10
    \$ echo \"1 2 3 4 5 10\"|$THIS -f mean minimum max
    4.1667 1 10

Dependencies: awk bash column fold sed

License: Copyright © 2026 Dominic Raferd. Licensed under the Apache License, \
Version 2.0 (the \"License\"); you may not use this file except in compliance \
with the License. You may obtain a copy of the License at \
https://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable \
law or agreed to in writing, software distributed under the License is \
distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY \
KIND, either express or implied. See the License for the specific language \
governing permissions and limitations under the License.
"|fold -sw"$COLUMNS"
fi
if [[ -n $CHANGELOG ]]; then
	[[ -n $HELP ]] && echo "Changelog:"
	echo -e "\
0.6 [05 Jan 2026] - add -f option
0.5 [16 Dec 2025] - show help on invalid or missing input
0.4 [30 Jul 2024] - add -m option
0.3 [12 Jun 2024] - work with null input (output all zeroes)
0.2 [08 Dec 2022] - bugfix showing full decimal results, add -p option, ignore commas or CRs in input, conform with shellcheck, publish
0.1 [08 Jan 2022] - based on an answer at https://unix.stackexchange.com/questions/13731/is-there-a-way-to-get-the-min-max-median-and-average-of-a-list-of-numbers-in
"|fold -sw"$COLUMNS"
fi
[[ -n $HELP$CHANGELOG ]] && exit 0

SED_CMD="s/ $//" # remove trailing space
[[ -z $SHOW_ALL_DIGITS ]] && SED_CMD='s/\(\.[0-9]*[1-9]\)0\+\b/\1/g;s/\.0\+\b//g;'"$SED_CMD"
if [[ -z $FNUM ]]; then
	( echo "sum count mean median minimum maximum"; domath|sed "$SED_CMD" ) | column -t
else
	FNUM=$(echo "$FNUM"|sed 's/sum/1/gi;s/tot\S*/1/gi;s/cou\S*/2/gi;s/mea\S*/3/gi;s/ave\S*/3/gi;s/med\S*/4/gi;s/min\S*/5/gi;s/max\S*/6/gi;s/\([0-9]\+\)/$\1/g;s/ \+/,/g')
	domath|sed "$SED_CMD"|awk "{print $FNUM}"
fi
rm -- "${TMPF}1"
exit 0
