#!/bin/bash

# ULP error check script.
#
# Copyright (c) 2019-2024, Arm Limited.
# SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception

#set -x
set -eu

# cd to bin directory.
cd "${0%/*}"

rmodes='n u d z'
#rmodes=n
flags="${ULPFLAGS:--q}"
emu="$@"

FAIL=0
PASS=0

t() {
    # First argument: routine name
    routine=$1; shift
    # Second and third argument: lo and hi bounds
    # Extra processing needed for bivariate routines
    IFS=',' read -ra LO <<< "$1"; shift
    IFS=',' read -ra HI <<< "$1"; shift
    ITV="${LO[0]} ${HI[0]}"
    for i in "${!LO[@]}"; do
	[[ "$i" -eq "0" ]] || ITV="$ITV x ${LO[$i]} ${HI[$i]}"
    done
    # Fourth argument: number of test points
    n=$1; shift
    # Any remaining arguments forwards directly to ulp tool
    extra_flags="$@"

    # Read ULP limits, fenv expectation and control values from autogenerated files
    limits_file=$LIMITS
    [ $r == "n" ] || limits_file=${limits_file}_nn
    L=$(grep "^$routine " $limits_file | awk '{print $2}')
    [ -n "$L" ] || { echo ERROR: Could not determine ULP limit for $routine in $limits_file && false; }
    cvals=($(grep "^$routine " $CVALS | awk '{print $2}'))

    if grep -q "^$routine$" $DISABLE_FENV; then extra_flags="$extra_flags -f"; fi 
    # Emulate a do-while loop to loop over cvals, but still execute once if it is empty
    while : ; do
	# Empty string if we are at the end of cvals array
	c_arg=""
	[ -z "${cvals[0]:-}" ] || c_arg="-c ${cvals[0]}"
	$emu ./ulp -e $L $flags $extra_flags -r $r $c_arg $routine $ITV $n && PASS=$((PASS+1)) || FAIL=$((FAIL+1))
	# Shift cvals by 1, and break if it is now empty
	cvals=("${cvals[@]:1}")
	[ -n "${cvals[0]:-}" ] || break
    done

    # Run ULP tool

}

check() {
	$emu ./ulp -f -q "$@"
}

if [[ $WANT_EXPERIMENTAL_MATH -eq 1 ]] && [[ $WANT_SVE_TESTS -eq 1 ]] && [[ $USE_MPFR -eq 0 ]]; then
    # No guarantees about powi accuracy, so regression-test for exactness
    # w.r.t. the custom reference impl in ulp_wrappers.h
    if [ -z "$FUNC" ] || [ "$FUNC" == "_ZGVsMxvv_powi" ]; then
	check -q -f -e 0 _ZGVsMxvv_powi  0  inf x  0  1000 100000
	check -q -f -e 0 _ZGVsMxvv_powi -0 -inf x  0  1000 100000
	check -q -f -e 0 _ZGVsMxvv_powi  0  inf x -0 -1000 100000
	check -q -f -e 0 _ZGVsMxvv_powi -0 -inf x -0 -1000 100000
    fi
    if [ -z "$FUNC" ] || [ "$FUNC" == "_ZGVsMxvv_powk" ]; then
	check -q -f -e 0 _ZGVsMxvv_powk  0  inf x  0  1000 100000
	check -q -f -e 0 _ZGVsMxvv_powk -0 -inf x  0  1000 100000
	check -q -f -e 0 _ZGVsMxvv_powk  0  inf x -0 -1000 100000
	check -q -f -e 0 _ZGVsMxvv_powk -0 -inf x -0 -1000 100000
    fi
fi

# Test generic routines in all rounding modes
for r in $rmodes
do
  while read F LO HI N
  do
	[[ -z $F ]] || t $F $LO $HI $N
  done << EOF
$(grep "\b$FUNC\b" $GEN_ITVS)
EOF
done

# Only test arch-specific routines in round-to-nearest, with sign of zero ignored (-z flag)
r=n
while read F LO HI N
do
	[[ -z $F ]] || t $F $LO $HI $N -z
done << EOF
$(grep "\b$FUNC\b" $ARCH_ITVS)
EOF

[ 0 -eq $FAIL ] || {
	echo "FAILED $FAIL PASSED $PASS"
	exit 1
}
