#! /usr/local/bin/ksh -
echo
echo "This is a test of features of ksh93 and zsh"
echo

if test -n "$ZSH_VERSION"		# zsh only: need to force load of math function features
then
	zmodload zsh/mathfunc || true
fi

fpmacheps()
{
	# set machine epsilon in global variable eps on return

	typeset x

	eps=$1
	x=$1
	while test $(( (x + eps / 2.0) != x )) -eq 1
	do
		eps=$(( eps / 2.0 ))
	done
}

fpprec()
{
	echo ------------------------------------------------------------------------
	echo What is floating-point precision\?
	echo

	fpmacheps 1.0

	printf "Machine epsilon = %.2e\t%.2f decimal digits\n" $eps $(( -log(eps)/log(10) ))
}

fpinfinity()
{
	echo ------------------------------------------------------------------------
	echo Is floating-point infinity supported\?
	echo

	typeset x y Infinity

	x=2.0
	Infinity=$(( x ** 20000))
	printf "Infinity by 2**20000 = %.2e\n" $Infinity
	echo

	x=1.0
	y=0.0
	Infinity=$(( x /y ))
	printf "Infinity by 1/0 = %.2e\n" $Infinity
	test $((Infinity == Infinity)) -eq 1 && printf "Infinity == Infinity is true (OK)"
	test $((Infinity == Infinity)) -ne 1 && printf "Infinity == Infinity is false (WRONG)"
	echo
}

fpnan()
{
	echo ------------------------------------------------------------------------
	echo Is floating-point NaN supported\?
	echo

	typeset x y NaN

	x=0.0
	y=0.0
	NaN=$(( x / y))
	echo   "echo says:   NaN =" $NaN
	printf "printf says: NaN = %.2e\n" $NaN
	test $((NaN != NaN)) -eq 1 && printf "NaN != NaN is true (OK)"
	test $((NaN != NaN)) -ne 1 && printf "NaN != NaN is false (WRONG)"
	echo
}

fpoverflow()
{
	echo ------------------------------------------------------------------------
	echo What is floating-point overflow limit\?
	echo

	typeset x y

	fpmacheps 1.0
	x=$(( 1.0 - eps / 2.0 ))
	y=1.0
	while test $(( (y + y) > y )) -eq 1
	do
		# printf "DEBUG 1: x = %.15e\n" $x
		x=$(( x + x ))
		y=$(( y + y ))
		# echo   "DEBUG 2: x = " $x
		# printf "DEBUG 3: x = %.15e\n" $x
	done
	printf "Overflow limit near %.15e\n" $x
}

fpsignedzero()
{
	echo ------------------------------------------------------------------------
	echo Are signed zeros supported\?
	echo

	typeset x y z

	x=0.0
	printf 'x=0.0:\tmust be 0.0:\tgot %.1f\n' x

	x=-0.0
	printf 'x=-0.0:\tmust be -0.0:\tgot %.1f\n' x

	x=0.0
	x=$(( -x ))
	printf 'x=0.0\tx=$(( -x )):\tmust be -0.0:\tgot %.1f\n' x

	x=0.0
	y=$(( sqrt(x) ))
	printf 'x=0.0\ty=$(( sqrt(x) )):\tmust be 0.0:\tgot %.1f\n' y

	x=0.0
	y=$(( sqrt(-x) ))
	printf 'x=0.0\ty=$(( sqrt(-x) )):\tmust be -0.0:\tgot %.1f\n' y

	x=1.0
	y=$(( 1.0 / 0.0 ))
	z=$(( 1.0 / y ))
	printf 'x=0.0\ty=$(( 1.0 / 0.0 ))\tz=$(( 1.0 / y )):\tmust be 0.0:\tgot %.1f\n' z

	x=1.0
	y=$(( 1.0 / 0.0 ))
	z=$(( -1.0 / y ))
	printf 'x=0.0\ty=$(( 1.0 / 0.0 ))\tz=$(( -1.0 / y )):\tmust be -0.0:\tgot %.1f\n' z

	echo
}

fpsubnormal()
{
	echo ------------------------------------------------------------------------
	echo Are IEEE 754 subnormal numbers supported\?
	echo

	typeset base s x

	base=2.0 			# correct for all UNIX systems (even IBM S/390 with G5 boards)
	fpmacheps 1.0

	# printf "DEBUG: eps =         %.16a\n" $eps

	x=$((1 - eps / base)) 		# x has all significand bits of one: 1.fffff...p+0
	k=0
	for ((s = 1.0; (s > 0.0) && (((x * s) / s) == x) ; s /= base, k--))
	do
		# printf "DEBUG: %d\t%.16e\n" $k $s
		:
	done
	if test $(( (s == 0) || (s/2 == 0) )) -eq 1
	then
		printf "Arithmetic on this system underflows abruptly to zero: no subnormals, sigh...\n"
		# printf "DEBUG: x =         %.16a\n" $x
		printf "x = (1 - eps / %d) = %.16e\n" $base $x
		printf "s                 = %.16e (= 2.0**%d)\n" $s $k
		printf "%d * x * s         = %.16e\n" $base          $(( base * x * s ))
		printf "(1 + eps) * x * s = %.16e\n"                 $(( (1 + eps) * x * s ))
		printf "%d * x * s         = %.16e\n" 1              $(( x * s ))
	else
		printf "This system appears to support subnormals starting at values at or below\n"
		printf "\t%.16e\n\n" $s
		printf "If these really are IEEE 754 64-bit subnormals, then there should be 14 values\n"
		printf "at multiples of 1/16, before we hit zero.  Let's find out!\n\n"
		# k=-1022
		for (( ; s > 0.0; s /= 16, k = k - 4 ))
		do
			printf "%3d\t%d\t%.16e\n" $(( 1 - (k + 1022)/4 )) $k $s
		done
		printf "%3d\t%d\t%.16e\n" $(( 1 - (k + 1022)/4 )) $k $s
	fi

	echo
}

fpunderflow()
{
	echo ------------------------------------------------------------------------
	echo What is floating-point underflow limit\?
	echo

	typeset n x

	let x=1.0
	n=0

	while test $(( (x / 2.0) > 0.0 )) -gt 0
	do
		x=$(( x / 2.0 ))
		n=$(( n - 1 ))
		# printf "DEBUG: x = %.2e\t%d\n" $x $n
	done
	printf "Underflow limit near %.2e\n" $x
}

brace_expansion()
{
	echo ------------------------------------------------------------------------
	echo Is brace expansion supported\?
	echo
	echo 'echo {one,two,three}.ext produces: ' {one,two,three}.ext
	echo
	echo Here is how other shells handle brace expansion:
	echo

	typeset sh

	for sh in /bin/sh \
		/bin/ksh \
		/bin/csh \
		/bin/jsh \
		/bin/zsh \
		/usr/local/bin/bash \
		/usr/local/bin/ksh \
		/usr/local/bin/pdksh \
		/usr/local/bin/tcsh
	do
		if test -x $sh
		then
			printf "%-24s\t" $sh
			echo 'echo {one,two,three}.ext' | $sh
		fi
	done
	echo
}

indexed_arrays()
{
	echo ------------------------------------------------------------------------
	echo Are indexed arrays supported\?
	echo

	typeset i name

	name=(Alice Bob Carol Dave)

	echo "Zeroth name:     "'${name[0]} = '${name[0]}
	echo "First  name:     "'${name[1]} = '${name[1]}
	echo "Second name:     "'${name[2]} = '${name[2]}
	echo "Third  name:     "'${name[3]} = '${name[3]}
	echo "Number of names: "'${#name[*]} = '${#name[*]}

	echo
	echo Test of array element sizes
	echo

	echo "Length of zeroth name:     "'${#name[0]} = '${#name[0]}
	echo "Length of first  name:     "'${#name[1]} = '${#name[1]}
	echo "Length of second name:     "'${#name[2]} = '${#name[2]}
	echo "Length of third  name:     "'${#name[3]} = '${#name[3]}
	echo
}

associative_arrays()
{
	echo ------------------------------------------------------------------------
	echo Are associative arrays and C-style for loops supported\?
	echo

	typeset i name

	name=(Alice Bob Carol Dave)

	for ((i = 0; i < 4; ++i))
	do
		len[${name[$i]}]=${#name[$i]}
		echo 'len[${name['$i']}] = '${len[${name[$i]}]}
	done
	echo
}

fparith()
{
	echo ------------------------------------------------------------------------
	echo Is floating-point arithmetic supported\?
	echo

	typeset x

	x=0.0
	while test $x -lt 5.0
	do
		printf "printf: %s = %.3f\t" x $x
		echo   "echo:   x = $x"
		x=$((x + 0.25))
	done
	echo
}

fpfunctions()
{
	echo ------------------------------------------------------------------------
	echo Are floating-point functions supported\?
	echo

	typeset x

	x=0.125
	printf "%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t%7s\n" x cos exp log sin sqrt tan
	while test $x -le 4.0
	do
		printf "%7.4f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\n" \
			$x $((cos(x))) $((exp(x))) $((log(x))) $((sin(x))) $((sqrt(x))) $((tan(x)))
		x=$((x + 0.125))
	done
	echo

	x=0.0
	printf "%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t%7s\n" x acos asin atan cosh sinh tanh
	while test $x -le 1.0
	do
		printf "%7.4f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\n" \
			$x $((acos(x))) $((asin(x))) $((atan(x))) $((cosh(x))) $((sinh(x))) $((tanh(x)))
		x=$((x + 0.0625))
	done
	echo

	test -z "$ZSH_VERSION" && return

	echo ------------------------------------------------------------------------
	echo Are zsh-extension floating-point functions supported\?
	echo

	# zsh offers additional functions that we can sample (though
	# we don't yet try the functions of two arguments)

	x=0.5
	printf "%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t%7s\n" x acosh asinh atanh cbrt erf erfc
	while test $x -le 2.0
	do
		printf "%7.4f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\n" \
			$x $((acosh(x))) $((asinh(x))) $((atanh(x))) $((cbrt(x))) $((erf(x))) $((erfc(x)))
		x=$((x + 0.0625))
	done
	echo

	x=0.0
	printf "%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t%7s\n" x expm1 ceil fabs floor gamma ilogb
	while test $x -le 1.0
	do
		printf "%7.4f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\n" \
			$x $((expm1(x))) $((ceil(x))) $((fabs(x))) $((floor(x))) $((gamma(x))) $((ilogb(x)))
		x=$((x + 0.0625))
	done
	echo

	x=0.0
	printf "%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t%7s\n" x j0 j1 lgamma log10 log1p logb
	while test $x -le 1.0
	do
		printf "%7.4f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\t%7.3f\n" \
			$x $((j0(x))) $((j1(x))) $((lgamma(x))) $((log10(x))) $((log1p(x))) $((logb(x)))
		x=$((x + 0.0625))
	done
	echo

	x=1.0625
	printf "%7s\t%7s\t%7s\t%7s\t%7s\t%7s\t%7s\n" x rint y0 y1
	while test $x -le 10.0
	do
		printf "%7.4f\t%7.3f\t%7.3f\t%7.3f\n" \
			$x $((rint(x))) $((y0(x))) $((y1(x)))
		x=$((x + 0.5))
	done
	echo

}

# Run the tests: several of them show a botched implementation and
# understanding in ksh of floating-point arithmetic, and they have to
# be disabled, sigh...

fparith			|| true
fpprec			|| true
fpsubnormal
fpunderflow		|| true

if test -n "$ZSH_VERSION"		# zsh only: ksh botches these
then
	fpoverflow		|| true
	fpinfinity		|| true
	fpnan		|| true
fi

fpfunctions		|| true
fpsignedzero		|| true

brace_expansion		|| true
indexed_arrays		|| true
associative_arrays	|| true
