DFT using pure ksh ONLY!

DFT using pure ksh ONLY!

Well ksh[93] now has its own DFT without the aid of AWK. The SINE, COSINE and SQRT functions are in the code directly and NOT sourced.
This is stand alone and requires nothing but ksh[93] itself. Who would have even thought that this was possible until very recently?
Thanks to Corona688 for his major improvements of my original naive working maths functions.

#!/bin/ksh
# KSH Version: 'Version AJM 93u+ 2012-08-01'
# From both ${.sh.version} and ${KSH_VERSION}.
#
# OSX 10.13.6, default bash terminal running ksh.
# Darwin Barrys-MacBook-Pro.local 17.7.0 Darwin Kernel Version 17.7.0:
# Thu Jun 21 22:53:14 PDT 2018; root:xnu-4570.71.2~1/RELEASE_X86_64 x86_64
#
# Licence is CC0, Public Domain.
#
# Thanks to Corona688 for his maths expertise inside the maths functions
# translating my original naive coding.
#
# This is pure ksh93 shell code only.

# The only constant required to 17 decimal places.
PI=3.14159265358979323

# Degrees to radians.
TORADIANS=$(( PI/180.0 ))
# Radians to degrees.
TODEGREES=$(( 180.0/PI ))

# NTH ROOT of a positive floating point number.
# Called as:
# NthRoot NUMBER NTHROOT [PRECISION]
# ALL POSITIVE VALUES.
# NUMBER and NTHROOT can be floating point, optional PRECISION is an integer from 1 to 16.
NthRoot()
{
	# Make these local.
	typeset NUMBER var
	typeset NTHROOT var
	typeset PRECISION var

	NUMBER="$1"
	NTHROOT="$2"
	PRECISION="$3"

	if [ "$PRECISION" = "" ] || [ $PRECISION -lt 1 ]
	then
		# My school log table(s) accuracy. ;o)
		PRECISION=4
	fi

	printf "%.${PRECISION}f\n" "$(( NUMBER**(1.0/NTHROOT) ))"
}

# SQUARE ROOT of a positive floating point number.
# Called as:
# Sqrt NUMBER [PRECISION]
# ALL POSITIVE VALUES.
# NUMBER can be floating point, optional PRECISION is an integer from 1 to 16.
Sqrt()
{
	# Make these local.
	typeset NUMBER var
	typeset NTHROOT var
	typeset PRECISION var

	NUMBER="$1"
	NTHROOT=2.0
	PRECISION="$2"

	NthRoot $NUMBER $NTHROOT $PRECISION
}

# SINE(angle) in degrees.
# Called as:
# Sin some_angle
# Courtesy of Corona688 translated and improved considerably from shell code I wrote.
Sin()
{
	# Make these local.
	typeset ANGLE var
	typeset SIN var

	# ANGLE used instead of RADIANS for consistency in accuracy.
	ANGLE="$1"
	SIN=0.0

	# Compensate for angles less the 0.0 degrees.
	while (( ANGLE<0.0 ))
	do
		(( ANGLE+=360.0 ))
	done

	# Ditto for angles greater than 360.0 degrees.
	while (( ANGLE>=360.0 ))
	do
		(( ANGLE-=360.0 ))
	done

	# Correct for the four quadrants.
	(( ANGLE>270.0 )) && (( ANGLE-=360 ))
	(( ANGLE>180.0 )) && ANGLE=$(( -(ANGLE-180) ))
	(( ANGLE>90.0 )) && ANGLE=$(( 180-ANGLE ))

	# Now convert to RADIANS.
	(( ANGLE*=TORADIANS ))

	# This loop is to calculate this Taylor series:
	# RADIAN-((RADIAN**3)/6.0)+((RADIAN**5)/120.0)-((RADIAN**7)/5040.0)+((RADIAN**9)/362880.0)-((RADIAN**11)/39916800.0)+((RADIAN**13)/6227020800.0) ... etc.
	# Factorial part 'F'.
	F=1
	for (( N=1; N<=13; ))
	do
		(( SIN+=(ANGLE**N)/F ))
		(( F*=++N ))
		(( F*=-(++N) ))
	done

	# Precision returned to 8 Decimal places.
	printf "%.8f\n" "$SIN"
}

# COSINE(angle) in degrees.
# Called as:
# Cos some_angle
Cos()
{
	# Make this local.
	typeset COS var

	COS=$(( "$1" + 90.0 ))
	# Call the 'Sin()' function.
	Sin $COS
}

dft()
{
	# Check for equal length real and imaginary arrays, and minimum number of elements of 1.
	if [ ${#REAL_ARRAY[@]} -ne ${#IMAG_ARRAY[@]} ] || [ ${#REAL_ARRAY[@]} -le 1 ] || [ ${#IMAG_ARRAY[@]} -le 1 ]
	then
		echo "ERROR 1:"
		echo "Number of elements, both real and imaginary, must be the same."
		exit 1
	fi
	# Check for powers of 2.
	N=${#REAL_ARRAY[@]}
	if [ $(( N&(N-1) )) -ne 0 ]
	then
		echo "ERROR 2:"
		echo "Padding or cropping is required to the nearest power of 2 elements."
		exit 2
	fi
	REAL=()
	IMAG=()
	# For each element into REAL() and IMAG() arrays...
	for ((K=0; K<=N-1; K++))
	do
		SUMREAL=0.0
		SUMIMAG=0.0
		# For each element from REAL_ARRAY() and IMAG_ARRAY() arrays...
		for ((T=0; T<=N-1; T++))
		do
			ANGLE=$(( (2.0*PI*T*K)/N ))
			DEGREES=$(( ANGLE*TODEGREES ))
			COS_ANGLE=$( Cos DEGREES )
			SIN_ANGLE=$( Sin DEGREES )
			SUMREAL=$(( SUMREAL+${REAL_ARRAY[T]}*COS_ANGLE+${IMAG_ARRAY[T]}*SIN_ANGLE ))
			SUMIMAG=$(( SUMIMAG-${REAL_ARRAY[T]}*SIN_ANGLE+${IMAG_ARRAY[T]}*COS_ANGLE ))
		done
		REAL[$K]=$SUMREAL
		IMAG[$K]=$SUMIMAG
	done
}

# This is good enough for this DEMO.
abs_complex()
{
	SQUARE=$(( (${REAL[COUNT]}**2)+(${IMAG[COUNT]}**2) ))
	ABS=$( Sqrt $SQUARE 8 )
}

# ALL floating point values must be from zero to positive.
#
# 16 samples of single cycle square wave.
#
# REAL_ARRAY=( 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 )
# IMAG_ARRAY=( 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 )
#
# 8.00000 5.12583 0.00000 1.79995 0.00000 1.20269 0.00000 1.01959
# 0.00000 1.01959 0.00000 1.20269 0.00000 1.79995 0.00000 5.12583

# This has midway '0.5' value padding at the end to bring to the power of 2.
# Note: SIX 1.0s, SIX 0.0s, ONE extra 1.0 and finally the THREE 0.5s padding!
#
REAL_ARRAY=( 1.0 1.0 1.0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.5 0.5 0.5 )
IMAG_ARRAY=( 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 )
#
# 8.50000 4.11842 1.77882 0.24688 1.80278 0.49665 0.57947 1.31567
# 0.50000 1.31567 0.57947 0.49665 1.80278 0.24688 1.77882 4.11842

# Standard test values.
#
# REAL_ARRAY=( 1.0 1.0 1.0 1.0 0.0 0.0 0.0 0.0 )
# IMAG_ARRAY=( 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 )
#
# 4.00000 2.61313 0.00000 1.08239 0.00000 1.08239 0.00000 2.61313

# This assumes some needed variables are global!
dft

# *************************************************
# This lot can be discarded...
echo ""
echo "Real values:		Imaginary Values:"
for n in $( seq 0 1 $(( ${#REAL[@]}-1 )) )
do
	printf "%.6f		%.6f\n" "${REAL[${n}]}" "${IMAG[${n}]}"
done
echo ""
echo "Absolute values of _complex_ numbers:"

for COUNT in $( seq 0 1 $(( ${#REAL[@]}-1 )) )
do
	abs_complex
	printf "%.5f " "$ABS"
done
echo ""
echo ""
echo "Done..."
# *************************************************

Results using OSX 10.13.6, default bash terminal.

Last login: Mon Oct 29 12:52:09 on ttys000
AMIGA:amiga~> cd Desktop/Code/Shell
AMIGA:amiga~/Desktop/Code/Shell> ./DFT_ksh

Real values:		Imaginary Values:
8.500000		0.000000
3.637821		-1.930714
-1.707107		-0.500000
0.051425		-0.241468
1.500000		-1.000000
-0.172746		0.465639
-0.292893		0.500000
0.483499		-1.223608
0.500000		0.000000
0.483499		1.223608
-0.292893		-0.500000
-0.172746		-0.465639
1.500000		1.000000
0.051425		0.241468
-1.707107		0.500000
3.637821		1.930714

Absolute values of _complex_ numbers:
8.50000 4.11842 1.77882 0.24688 1.80278 0.49665 0.57947 1.31567 0.50000 1.31567 0.57947 0.49665 1.80278 0.24688 1.77882 4.11842 

Done...
AMIGA:amiga~/Desktop/Code/Shell> _

As an addendum the maths _library_ on another thread will have functions added to it separately from this, TAN, SEC, etc... etc...

Bazza...

1 Like