# $NetBSD: t_arith.sh,v 1.8 2017/07/15 18:50:42 kre Exp $
#
# Copyright (c) 2016 The NetBSD Foundation, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# the implementation of "sh" to test
: ${TEST_SH:="/bin/sh"}

# Requirement is to support at least "signed long" whatever that means
# (number of bits in "long" is not specified - but should be at least 32).

# These tests use -o inline:"..." rather than -o match:'...' as we have
# only digits to examine, and it is good to be sure that 1 + 1 really gives 2
# and that 42 or 123 don't look like success because there is a 2 in them.

ARITH_BITS='?'
discover_range()
{
	# cannot use arithmetic "test" operators, range of test in
	# ATF_SHELL (or even TEST_SH) might not be as big as that
	# supported by $(( )) in TEST_SH

	if ! ${TEST_SH} -c ': $(( 0x10000 ))' 2>/dev/null
	then
		# 16 bits or less, or hex unsupported, just give up...
		return
	fi
	test $( ${TEST_SH} -c 'echo $(( 0x1FFFF ))' ) = 131071 || return

	# when attempting to exceed the number of available bits
	# the shell may react in any of 3 (rational) ways
	# 1. syntax error (maybe even core dump...) and fail
	# 2. represent a positive number input as negative value
	# 3. keep the number positive, but not the value expected
	#    (perhaps pegged at the max possible value)
	# any of those may be accompanied by a message to stderr

	# Must check all 3 possibilities for each plausible size
	# Tests do not use 0x8000... because that value can have weird
	# other side effects that are not relevant to discover here.
	# But we do want to try and force the sign bit set.

	if ! ${TEST_SH} -c ': $(( 0xC0000000 ))' 2>/dev/null
	then
		# proobably shell detected overflow and complained
		ARITH_BITS=32
		return
	fi
	if ${TEST_SH} 2>/dev/null \
	    -c 'case $(( 0xC0000000 )); in (-*) exit 0;; esac; exit 1'
	then
		ARITH_BITS=32
		return
	fi
	if ${TEST_SH} -c '[ $(( 0xC0000000 )) != 3221225472 ]' 2>/dev/null
	then
		ARITH_BITS=32
		return
	fi

	if ! ${TEST_SH} -c ': $(( 0xC000000000000000 ))' 2>/dev/null
	then
		ARITH_BITS=64
		return
	fi
	if ${TEST_SH} 2>/dev/null \
	    -c 'case $(( 0xC000000000000000 )); in (-*) exit 0;; esac; exit 1'
	then
		ARITH_BITS=64
		return
	fi
	if ${TEST_SH} 2>/dev/null \
	    -c '[ $((0xC000000000000000)) != 13835058055282163712 ]'
	then
		ARITH_BITS=64
		return
	fi

	if ${TEST_SH} 2>/dev/null -c \
	   '[ $((0x123456781234567812345678)) = 5634002657842756053938493048 ]'
	then
		# just assume... (for now anyway, revisit when it happens...)
		ARITH_BITS=96
		return
	fi
}

atf_test_case constants
constants_head()
{
        atf_set "descr" "Tests that arithmetic expansion can handle constants"
}
constants_body()
{
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $((0x0))'

	# atf_expect_fail "PR bin/50959"
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $((0X0))'
	# atf_expect_pass

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $((000))'

	atf_check -s exit:0 -o inline:'1\n' -e empty \
		${TEST_SH} -c 'echo $(( 000000001 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty \
		${TEST_SH} -c 'echo $(( 0x000000 ))'

	atf_check -s exit:0 -o inline:'99999\n' -e empty \
		${TEST_SH} -c 'echo $((99999))'

	[ ${ARITH_BITS} -gt 44 ] &&
		atf_check -s exit:0 -o inline:'9191919191919\n' -e empty \
			${TEST_SH} -c 'echo $((9191919191919))'

	atf_check -s exit:0 -o inline:'13\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xD ))'
	atf_check -s exit:0 -o inline:'11\n' -e empty ${TEST_SH} -c \
		'echo $(( 013 ))'
	atf_check -s exit:0 -o inline:'7\n' -e empty ${TEST_SH} -c \
		'x=7;echo $(($x))'
	atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \
		'x=9;echo $((x))'

	atf_check -s exit:0 -o inline:'11\n' -e empty \
		${TEST_SH} -c 'x=0xB; echo $(( $x ))'
	atf_check -s exit:0 -o inline:'27\n' -e empty \
		${TEST_SH} -c 'x=0X1B; echo $(( x ))'
	atf_check -s exit:0 -o inline:'27\n' -e empty \
		${TEST_SH} -c 'X=033; echo $(( $X ))'
	atf_check -s exit:0 -o inline:'219\n' -e empty \
		${TEST_SH} -c 'X=0333; echo $(( X ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty \
		${TEST_SH} -c 'NULL=; echo $(( NULL ))'

	# Not clear if this is 0, nothing, or an error, so omit for now
	# atf_check -s exit:0 -o inline:'0\n' -e empty \
	# 	${TEST_SH} -c 'echo $(( ))'

	# not clear whether this should return 0 or an error, so omit for now
	# atf_check -s exit:0 -o inline:'0\n' -e empty \
	# 	${TEST_SH} -c 'echo $(( UNDEFINED_VAR ))'
}


atf_test_case do_unary_plus
do_unary_plus_head()
{
        atf_set "descr" "Tests that unary plus works as expected"
}
do_unary_plus_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( +0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( +1 ))'
	atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \
		'echo $(( + 6 ))'
	atf_check -s exit:0 -o inline:'4321\n' -e empty ${TEST_SH} -c \
		'echo $(( + 4321 ))'
	atf_check -s exit:0 -o inline:'17185\n' -e empty ${TEST_SH} -c \
		'echo $(( + 0x4321 ))'
}

atf_test_case do_unary_minus
do_unary_minus_head()
{
        atf_set "descr" "Tests that unary minus works as expected"
}
do_unary_minus_body()
{
	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( -1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( - 0 ))'
	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( - 1 ))'
	atf_check -s exit:0 -o inline:'-6\n' -e empty ${TEST_SH} -c \
		'echo $(( - 6 ))'
	atf_check -s exit:0 -o inline:'-4321\n' -e empty ${TEST_SH} -c \
		'echo $(( - 4321 ))'
	atf_check -s exit:0 -o inline:'-2257\n' -e empty ${TEST_SH} -c \
		'echo $(( - 04321 ))'
	atf_check -s exit:0 -o inline:'-7\n' -e empty ${TEST_SH} -c \
		'echo $((-7))'
}

atf_test_case do_unary_not
do_unary_not_head()
{
        atf_set "descr" "Tests that unary not (boolean) works as expected"
}
do_unary_not_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( ! 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( ! 0 ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( !1234 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( !0xFFFF ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( ! 000000 ))'
}

atf_test_case do_unary_tilde
do_unary_tilde_head()
{
        atf_set "descr" "Tests that unary not (bitwise) works as expected"
}
do_unary_tilde_body()
{
	# definitely 2's complement arithmetic here...

	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( ~ 0 ))'
	atf_check -s exit:0 -o inline:'-2\n' -e empty ${TEST_SH} -c \
		'echo $(( ~ 1 ))'

	atf_check -s exit:0 -o inline:'-1235\n' -e empty ${TEST_SH} -c \
		'echo $(( ~1234 ))'
	atf_check -s exit:0 -o inline:'-256\n' -e empty ${TEST_SH} -c \
		'echo $(( ~0xFF ))'
}

atf_test_case elementary_add
elementary_add_head()
{
        atf_set "descr" "Tests that simple addition works as expected"
}
elementary_add_body()
{
	# some of these tests actually test unary ops &  op precedence...

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 + 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 + 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 + 1 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 + 1 ))'
	atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \
		'echo $(( 4 + 6 ))'
	atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \
		'echo $(( 6 + 4 ))'
	atf_check -s exit:0 -o inline:'5555\n' -e empty ${TEST_SH} -c \
		'echo $(( 1234 + 4321 ))'
	atf_check -s exit:0 -o inline:'3333\n' -e empty ${TEST_SH} -c \
		'echo $((1111+2222))'
	atf_check -s exit:0 -o inline:'5555\n' -e empty ${TEST_SH} -c \
		'echo $((+3333+2222))'
	atf_check -s exit:0 -o inline:'7777\n' -e empty ${TEST_SH} -c \
		'echo $((+3333 + +4444))'
	atf_check -s exit:0 -o inline:'-7777\n' -e empty ${TEST_SH} -c \
		'echo -$((+4125+ +3652))'
}

atf_test_case elementary_sub
elementary_sub_head()
{
        atf_set "descr" "Tests that simple subtraction works as expected"
}
elementary_sub_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 - 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 - 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 - 1 ))'
	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 - 1 ))'
	atf_check -s exit:0 -o inline:'488\n' -e empty ${TEST_SH} -c \
		'echo $(( 1066 - 578 ))'
	atf_check -s exit:0 -o inline:'-3662\n' -e empty ${TEST_SH} -c \
		'echo $(( 2016-5678 ))'
	atf_check -s exit:0 -o inline:'-3662\n' -e empty ${TEST_SH} -c \
		'echo $(( 2016+-5678 ))'
	atf_check -s exit:0 -o inline:'-3662\n' -e empty ${TEST_SH} -c \
		'echo $(( 2016-+5678 ))'
	atf_check -s exit:0 -o inline:'-7694\n' -e empty ${TEST_SH} -c \
		'echo $(( -2016-5678 ))'
	atf_check -s exit:0 -o inline:'--1\n' -e empty ${TEST_SH} -c \
		'echo -$(( -1018 - -1017 ))'
}

atf_test_case elementary_mul
elementary_mul_head()
{
        atf_set "descr" "Tests that simple multiplication works as expected"
}
elementary_mul_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 * 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 * 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 * 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 * 1 ))'
	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( -1 * 1 ))'
	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 * -1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( -1 * -1 ))'
	atf_check -s exit:0 -o inline:'391\n' -e empty ${TEST_SH} -c \
		'echo $(( 17 * 23 ))'
	atf_check -s exit:0 -o inline:'169\n' -e empty ${TEST_SH} -c \
		'echo $(( 13*13 ))'
	atf_check -s exit:0 -o inline:'-11264\n' -e empty ${TEST_SH} -c \
		'echo $(( -11 *1024 ))'
	atf_check -s exit:0 -o inline:'-16983\n' -e empty ${TEST_SH} -c \
		'echo $(( 17* -999 ))'
	atf_check -s exit:0 -o inline:'9309\n' -e empty ${TEST_SH} -c \
		'echo $(( -29*-321 ))'
}

atf_test_case elementary_div
elementary_div_head()
{
        atf_set "descr" "Tests that simple division works as expected"
}
elementary_div_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 / 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 / 1 ))'
	test ${ARITH_BITS} -ge 38 &&
	    atf_check -s exit:0 -o inline:'99999999999\n' -e empty \
		${TEST_SH} -c 'echo $(( 99999999999 / 1 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 / 1 ))'

	atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 / 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 / 2 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 / 3 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 / 4 ))'

	atf_check -s exit:0 -o inline:'173\n' -e empty ${TEST_SH} -c \
		'echo $(( 123456 / 713 ))'
	atf_check -s exit:0 -o inline:'13\n' -e empty ${TEST_SH} -c \
		'echo $(( 169 / 13 ))'
}

atf_test_case elementary_rem
elementary_rem_head()
{
        atf_set "descr" "Tests that simple modulus works as expected"
}
elementary_rem_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 % 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 % 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 % 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 9999 % 1 ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 % 2 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 % 2 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 % 2 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xFFFF % 2 ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 % 3 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 % 3 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 % 3 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 % 3 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 3123 % 3 ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 9999 % 2 ))'

	atf_check -s exit:0 -o inline:'107\n' -e empty ${TEST_SH} -c \
		'echo $(( 123456%173 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $((169%13))'
}

atf_test_case elementary_shl
elementary_shl_head()
{
        atf_set "descr" "Tests that simple shift left works as expected"
}
elementary_shl_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 << 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 << 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 << 17 ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 << 0 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 << 1 ))'
	atf_check -s exit:0 -o inline:'131072\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 << 17 ))'

	atf_check -s exit:0 -o inline:'2021161080\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x3C3C3C3C << 1 ))'

	test "${ARITH_BITS}" -ge 40 &&
	    atf_check -s exit:0 -o inline:'129354309120\n' -e empty \
		${TEST_SH} -c 'echo $(( 0x3C3C3C3C << 7 ))'
	test "${ARITH_BITS}" -ge 72 &&
	    atf_check -s exit:0 -o inline:'1111145054534149079040\n' \
		-e empty ${TEST_SH} -c 'echo $(( 0x3C3C3C3C << 40 ))'

	return 0
}

atf_test_case elementary_shr
elementary_shr_head()
{
        atf_set "descr" "Tests that simple shift right works as expected"
}
elementary_shr_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 >> 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 >> 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 >> 17 ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 >> 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 >> 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 >> 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 >> 1 ))'

	atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x10 >> 2 ))'
	atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \
		'echo $(( 022 >> 2 ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 131072 >> 17 ))'

	test ${ARITH_BITS} -ge 40 &&
		atf_check -s exit:0 -o inline:'8\n' -e empty ${TEST_SH} -c \
			'echo $(( 0x4000000000 >> 35 ))'
	test ${ARITH_BITS} -ge 80 &&
		atf_check -s exit:0 -o inline:'4464\n' -e empty ${TEST_SH} -c \
			'echo $(( 0x93400FACE005C871000 >> 64 ))'

	return 0
}

atf_test_case elementary_eq
elementary_eq_head()
{
        atf_set "descr" "Tests that simple equality test works as expected"
}
elementary_eq_body()
{
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 == 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 == 0000 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 == 0x00 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 == 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'X=30; Y=0x1E; echo $(( X == Y ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1234 == 4660 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1234 == 011064 ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 == 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 == 0000000000000001 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 == 0x10000000000000 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 == 2 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'X=3; Y=7; echo $(( X == Y ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1234 == 0x4660 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 01234 == 0x11064 ))'
}
atf_test_case elementary_ne
elementary_ne_head()
{
        atf_set "descr" "Tests that simple inequality test works as expected"
}
elementary_ne_body()
{
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 != 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x71 != 17 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1234 != 01234 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1234 != 01234 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'X=3; echo $(( X != 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'X=3; Y=0x11; echo $(( X != Y ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 != 3 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 != 0x0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xA != 012 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'X=1; echo $(( X != 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'X=0xC; Y=014; echo $(( X != Y ))'
}
atf_test_case elementary_lt
elementary_lt_head()
{
        atf_set "descr" "Tests that simple less than test works as expected"
}
elementary_lt_body()
{
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 < 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( -1 < 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 < 10 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 100 < 101 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xA1 < 200 ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 < 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 < 0 ))'

	test ${ARITH_BITS} -ge 40 &&
	    atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1BEEFF00D < 0x1FACECAFE ))'

	return 0
}
atf_test_case elementary_le
elementary_le_head()
{
        atf_set "descr" "Tests that simple less or equal test works as expected"
}
elementary_le_body()
{
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 <= 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( -1 <= 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 <= 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 <= 10 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 100 <= 101 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xA1 <= 161 ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 <= 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( -100 <= -200 ))'

	test ${ARITH_BITS} -ge 40 &&
	    atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'cost=; AUD=; echo $(( $cost 0x2FEEDBABE <= $AUD 12866927294 ))'

	return 0
}
atf_test_case elementary_gt
elementary_gt_head()
{
        atf_set "descr" "Tests that simple greater than works as expected"
}
elementary_gt_body()
{
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 > 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 > -1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 11 > 012 ))'

	# atf_expect_fail "PR bin/50959"
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 2147483647 > 0X7FFFFF0 ))'
	# atf_expect_pass

	test ${ARITH_BITS} -gt 32 &&
	    atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x80000000 > 0x7FFFFFFF ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 > 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 > 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( -1 > 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 > 10 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 2015 > 2016 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xA1 > 200 ))'

	test ${ARITH_BITS} -ge 44 &&
	    atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x7F07F07F0 > 34099628014 ))'

	return 0
}
atf_test_case elementary_ge
elementary_ge_head()
{
        atf_set "descr" "Tests that simple greater or equal works as expected"
}
elementary_ge_body()
{
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 >= 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 >= 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( -100 >= -101 ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( -1 >= 0 ))'
}

atf_test_case fiddle_bits_and
fiddle_bits_and_head()
{
	atf_set "descr" "Test bitwise and operations in arithmetic expressions"
}
fiddle_bits_and_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 & 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 & 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 & 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 & 1 ))'

	atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xFF & 0xFF ))'
	atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xFFFF & 0377 ))'

	test "${ARITH_BITS}" -ge 48 &&
	    atf_check -s exit:0 -o inline:'70377641607203\n' -e empty \
		${TEST_SH} -c 'echo $(( 0x5432FEDC0123 & 0x42871357BAB3 ))'

	return 0
}
atf_test_case fiddle_bits_or
fiddle_bits_or_head()
{
	atf_set "descr" "Test bitwise or operations in arithmetic expressions"
}
fiddle_bits_or_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 | 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 | 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 | 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 | 1 ))'

	atf_check -s exit:0 -o inline:'4369\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1111 | 0x1111 ))'
	atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xAA | 0125 ))'

	test "${ARITH_BITS}" -ge 48 &&
	    atf_check -s exit:0 -o inline:'95348271856563\n' -e empty \
		${TEST_SH} -c 'echo $(( 0x5432FEDC0123 | 0x42871357BAB3 ))'

	return 0
}
atf_test_case fiddle_bits_xor
fiddle_bits_xor_head()
{
	atf_set "descr" "Test bitwise xor operations in arithmetic expressions"
}
fiddle_bits_xor_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 ^ 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 ^ 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 ^ 1 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 ^ 1 ))'

	atf_check -s exit:0 -o inline:'255\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xF0 ^ 0x0F ))'
	atf_check -s exit:0 -o inline:'15\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xF0 ^ 0xFF ))'

	test "${ARITH_BITS}" -ge 48 &&
	    atf_check -s exit:0 -o inline:'24970630249360\n' -e empty \
		${TEST_SH} -c 'echo $(( 0x5432FEDC0123 ^ 0x42871357BAB3 ))'

	return 0
}

atf_test_case logical_and
logical_and_head()
{
	atf_set "descr" "Test logical and operations in arithmetic expressions"
}
logical_and_body()
{
	# cannot test short-circuit eval until sh implements side effects...

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 && 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 && 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 && 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 && 1 ))'

	# atf_expect_fail "PR bin/50960"
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1111 && 01234 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xFFFF && 0xF0F0 ))'
}
atf_test_case logical_or
logical_or_head()
{
	atf_set "descr" "Test logical or operations in arithmetic expressions"
}
logical_or_body()
{
	# cannot test short-circuit eval until sh implements side effects...

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 || 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 || 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 || 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 || 1 ))'

	# atf_expect_fail "PR bin/50960"
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1111 || 01234 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x33 || 0xF0F0 ))'
}

atf_test_case nested_arith
nested_arith_head()
{
	atf_set "descr" 'Test nested arithmetic $(( $(( )) ))'
}
nested_arith_body()
{
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( $(( 0 )) ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 + $(( 2 - 2 )) ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( $(( 3 / 3 )) + $((1*1*1)) - $(( 7 % 6 ))))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(($(($(($(($((1))))))))))'

	atf_check -s exit:0 -o inline:'246\n' -e empty ${TEST_SH} -c \
		'echo $(( 2$((2 * 2))6 ))'
	atf_check -s exit:0 -o inline:'291117\n' -e empty ${TEST_SH} -c \
		'echo $(( $((1 + 1))$((3 * 3))$(( 99-88 ))$(( 17))))'
	atf_check -s exit:0 -o inline:'123456789\n' -e empty ${TEST_SH} -c \
	  'echo $(( 1$((2$((1+2))4$((2 + 2 + 1))6))7$((4 * 2))$(($((81/9))))))'
}

atf_test_case make_selection
make_selection_head()
{
	atf_set "descr" "Test ?: operator in arithmetic expressions"
}
make_selection_body()
{
	# atf_expect_fail "PR bin/50958"

	atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \
		'echo $(( 0 ? 2 : 3 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 ? 2 : 3 ))'

	atf_check -s exit:0 -o inline:'111\n' -e empty ${TEST_SH} -c \
		'echo $(( 0x1234 ? 111 : 222 ))'

	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 < 1 ? -1 : 1 > 1 ? 1 : 0 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 < 1 ? -1 : 2 > 1 ? 1 : 0 ))'
}

atf_test_case operator_precedence
operator_precedence_head()
{
	atf_set "descr" "Test operator precedence without parentheses"
}
operator_precedence_body()
{
	# NB: apart from $(( ))  ** NO ** parentheses in the expressions.

	atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 + 2 + 3 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 - 2 + 3 ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 - 2 - 1 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 - 2 + 1 ))'

	atf_check -s exit:0 -o inline:'-1\n' -e empty ${TEST_SH} -c \
		'echo $(( - 2 + 1 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 + -1 ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( ! 2 + 1 ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 + !1 ))'

	atf_check -s exit:0 -o inline:'8\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 * 2 + 2 ))'
	atf_check -s exit:0 -o inline:'7\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 + 2 * 2 ))'
	atf_check -s exit:0 -o inline:'12\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 * 2 * 2 ))'

	atf_check -s exit:0 -o inline:'5\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 / 3 + 2 ))'
	atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 + 3 / 2 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 / 3 / 2 ))'

	atf_check -s exit:0 -o inline:'72\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 << 1 + 2 ))'
	atf_check -s exit:0 -o inline:'48\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 + 3 << 2 ))'
	atf_check -s exit:0 -o inline:'288\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 << 3 << 2 ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 >> 1 + 2 ))'
	atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 + 3 >> 2 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 19 >> 3 >> 1 ))'

	atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \
		'echo $(( 19 >> 3 << 1 ))'
	atf_check -s exit:0 -o inline:'76\n' -e empty ${TEST_SH} -c \
		'echo $(( 19 << 3 >> 1 ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 + 3 < 3 * 2 ))'
	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 << 3 >= 3 << 2 ))'

	# sh inherits C's crazy operator precedence...

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 0xfD & 0xF == 0xF ))'

	${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null  && {
		atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
			'echo $(( 3 * 7 , 2 << 8 ,  9 - 7 ))'
		atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \
			'echo $(( 1 ? 2 : 3 , 0 ? 1 : 4 ))'
	}

	return 0
}

atf_test_case optional_comma
optional_comma_head()
{
	atf_set "descr" "Test the optional comma operator"
}
optional_comma_body()
{
	# First, see if it is supported or not.
	${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null  || atf_skip \
	    "${TEST_SH} does not implement the ',' operator in"' $(( ))'


	# Note ',' should be set off from numbers by spaces, as in some
	# locales it is a valid chacacter in a number, and we want to
	# avoid any possibility of confusing the parser.

	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 , 2 ))'
	atf_check -s exit:0 -o inline:'3\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 , 2 , 3 ))'
	atf_check -s exit:0 -o inline:'4\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 , 2 , 3 , 4 ))'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'echo $(( , 2 ))'
	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'echo $(( 2 , ))'
	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'echo $(( 1 , , 2 ))'
}

parentheses_head()
{
	atf_set "descr" "Test use of () to group sub-expressions"
}
parentheses_body()
{
	atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \
		'echo $(( (1 + 2) + 3 ))'
	atf_check -s exit:0 -o inline:'-4\n' -e empty ${TEST_SH} -c \
		'echo $(( 1 - (2 + 3) ))'
	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 - (2 - 1) ))'
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 - ( 2 + 1 ) ))'

	atf_check -s exit:0 -o inline:'-3\n' -e empty ${TEST_SH} -c \
		'echo $(( - (2 + 1) ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( ! (2 + 1) ))'

	atf_check -s exit:0 -o inline:'12\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 * (2 + 2) ))'
	atf_check -s exit:0 -o inline:'10\n' -e empty ${TEST_SH} -c \
		'echo $(( (3 + 2) * 2 ))'
	atf_check -s exit:0 -o inline:'12\n' -e empty ${TEST_SH} -c \
		'echo $(( 3 * (2 * 2) ))'

	atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 / (3 + 2) ))'
	atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \
		'echo $(( ( 9 + 3 ) / 2 ))'
	atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 / ( 3 / 2 ) ))'

	atf_check -s exit:0 -o inline:'20\n' -e empty ${TEST_SH} -c \
		'echo $(( ( 9 << 1 ) + 2 ))'
	atf_check -s exit:0 -o inline:'21\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 + (3 << 2) ))'
	atf_check -s exit:0 -o inline:'36864\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 << (3 << 2) ))'

	atf_check -s exit:0 -o inline:'6\n' -e empty ${TEST_SH} -c \
		'echo $(( (9 >> 1) + 2 ))'
	atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \
		'echo $(( 9 + (3 >> 2) ))'
	atf_check -s exit:0 -o inline:'9\n' -e empty ${TEST_SH} -c \
		'echo $(( 19 >> (3 >> 1) ))'

	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( 19 >> (3 << 1) ))'
	atf_check -s exit:0 -o inline:'38\n' -e empty ${TEST_SH} -c \
		'echo $(( 19 << (3 >> 1) ))'

	atf_check -s exit:0 -o inline:'2\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 + (3 < 3) * 2 ))'
	atf_check -s exit:0 -o inline:'32\n' -e empty ${TEST_SH} -c \
		'echo $(( 2 << ((3 >= 3) << 2) ))'

	# sh inherits C's crazy operator precedence...
	atf_check -s exit:0 -o inline:'0\n' -e empty ${TEST_SH} -c \
		'echo $(( (0xfD & 0xF) == 0xF ))'

	${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null  && {
		atf_check -s exit:0 -o inline:'24\n' -e empty ${TEST_SH} -c \
			'echo $(( 3 * (7 , 2) << (8 ,  9 - 7) ))'
		atf_check -s exit:0 -o inline:'1\n' -e empty ${TEST_SH} -c \
			'echo $(( 0 ? 2 : ( ( 0 , 3 ) ? 1 : 4) ))'
	}
	return 0
}

atf_test_case var_assign
var_assign_head()
{
	atf_set "descr" "Test assignment operators in arithmetic expressions"
}
var_assign_body()
{
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'unset x; echo $(( x = 3 )); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'unset x; echo $((x=3)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=5; echo $((x=3)); echo $x'

	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'set +u;unset x; echo $((x+=3)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=2; echo $((x+=1)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=4; echo $((x-=1)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=3; echo $((x*=1)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=3; echo $((x/=1)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=28; echo $((x%=5)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=7; echo $((x&=3)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=2; echo $((x|=1)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=6; echo $((x^=5)); echo $x'
	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'x=7; echo $((x>>=1)); echo $x'
	atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c \
		'x=1; echo $((x<<=1)); echo $x'

	atf_check -s exit:0 -o inline:'2\n3\n' -e empty ${TEST_SH} -c \
		'x=2; echo $(( (x+=1)-1 )); echo $x'
	atf_check -s exit:0 -o inline:'4\n3\n' -e empty ${TEST_SH} -c \
		'x=4; echo $(( (x-=1)+1 )); echo $x'

	atf_check -s exit:0 -o inline:'36\n5 7\n' -e empty ${TEST_SH} -c \
		'unset x y; echo $(( (x=5) * (y=7) + 1 )); echo $x $y'
	atf_check -s exit:0 -o inline:'36\n5 7\n' -e empty ${TEST_SH} -c \
		'x=99; y=17; echo $(( (x=5) * (y=7) + 1 )); echo $x $y'
	atf_check -s exit:0 -o inline:'36\n5 7\n' -e empty ${TEST_SH} -c \
		'x=4; y=9; echo $(( (x+=1) * (y-=2) + 1 )); echo $x $y'

	atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
		'set -u; unset x; echo $(( x = 3 )); echo $x'
	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
		'set -u; unset x; echo $(( x + 3 )); echo $x'
	atf_check -s not-exit:0 -o ignore -e not-empty ${TEST_SH} -c \
		'set -u; unset x; echo $(( x+=3 )); echo $x'

	${TEST_SH} -c ': $(( 1 , 2 , 3 ))' 2>/dev/null  && {
		atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
			'echo $((x=2 , x|=1)); echo $x'
		atf_check -s exit:0 -o inline:'3\n3\n' -e empty ${TEST_SH} -c \
			'set -u; echo $((x = 2   ,x |= 1)); echo $x'
		atf_check -s exit:0 -o inline:'6\n1:2:3:6\n' -e empty \
			${TEST_SH} -c \
		    'echo $((a=1 , b=2 , c = 3 , x=a+b + c)); echo $a:$b:$c:$x'
		atf_check -s exit:0 -o inline:'6\n1:2:3:6\n' -e empty \
			${TEST_SH} -c \
		    'set -u;echo $((a=1 ,b=2 ,c=3 ,x=a+b+c)); echo $a:$b:$c:$x'
	}
	return 0
}

atf_test_case var_postinc
var_postinc_head()
{
	atf_set "descr" "Test suffix ++ operator"
}
var_postinc_body()
{
	${TEST_SH} -c 'X=1; : $(( X++ ))' 2>/dev/null ||
		atf_skip "${TEST_SH} does not support the suffix ++ operator"

	unset X		; # just in case ...

	atf_check -s exit:0 -o inline:'1\n2\n' -e empty ${TEST_SH} -c \
		'X=1; echo $(( X++ )); echo $X'
	atf_check -s exit:0 -o inline:'0\n1\n' -e empty ${TEST_SH} -c \
		'echo $(( X++ )); echo $X'

	atf_check -s exit:0 -o inline:'0\n1:0\n' -e empty ${TEST_SH} -c \
		'unset Y; echo $(( Y = X++ )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'12\n4:5\n' -e empty ${TEST_SH} -c \
		'X=3 Y=4; echo $(( Y++*X++ )); echo $X:$Y'

	atf_check -s exit:0 -o inline:'1\n2\n' -e empty ${TEST_SH} -c \
		'set -u; X=1; echo $(( X++ )); echo $X'
	atf_check -s exit:0 -o inline:'0\n1:0\n' -e empty ${TEST_SH} -c \
		'set -u; X=0; unset Y; echo $(( Y = X++ )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'12\n4:5\n' -e empty ${TEST_SH} -c \
		'set -u; X=3 Y=4; echo $(( Y++*X++ )); echo $X:$Y'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; echo $(( X++ ))'
	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; unset Y; echo $(( X = Y++ ))'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'X=3; readonly X; echo $(( X++ ))'

}
atf_test_case var_postdec
var_postdec_head()
{
	atf_set "descr" "Test suffix -- operator"
}
var_postdec_body()
{
	${TEST_SH} -c 'X=1; : $(( X-- ))' 2>/dev/null ||
		atf_skip "${TEST_SH} does not support the suffix -- operator"

	unset X		; # just in case ...

	atf_check -s exit:0 -o inline:'1\n0\n' -e empty ${TEST_SH} -c \
		'X=1; echo $(( X-- )); echo $X'
	atf_check -s exit:0 -o inline:'0\n-1\n' -e empty ${TEST_SH} -c \
		'echo $(( X-- )); echo $X'

	atf_check -s exit:0 -o inline:'0\n-1:0\n' -e empty ${TEST_SH} -c \
		'unset Y; echo $(( Y = X-- )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'12\n2:3\n' -e empty ${TEST_SH} -c \
		'X=3 Y=4; echo $(( Y--*X-- )); echo $X:$Y'

	atf_check -s exit:0 -o inline:'1\n0\n' -e empty ${TEST_SH} -c \
		'set -u; X=1; echo $(( X-- )); echo $X'
	atf_check -s exit:0 -o inline:'0\n-1:0\n' -e empty ${TEST_SH} -c \
		'set -u; X=0; unset Y; echo $(( Y = X-- )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'12\n2:3\n' -e empty ${TEST_SH} -c \
		'set -u; X=3 Y=4; echo $(( Y--*X-- )); echo $X:$Y'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; echo $(( X-- ))'
	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; unset Y; echo $(( X = Y-- ))'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'X=3; readonly X; echo $(( X-- ))'

}
atf_test_case var_preinc
var_preinc_head()
{
	atf_set "descr" "Test prefix ++ operator"
}
var_preinc_body()
{
	${TEST_SH} -c 'X=1; : $(( ++X ))' 2>/dev/null ||
		atf_skip "${TEST_SH} does not support the prefix ++ operator"

	unset X		; # just in case ...

	atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c \
		'X=1; echo $(( ++X )); echo $X'
	atf_check -s exit:0 -o inline:'1\n1\n' -e empty ${TEST_SH} -c \
		'echo $(( ++X )); echo $X'

	atf_check -s exit:0 -o inline:'1\n1:1\n' -e empty ${TEST_SH} -c \
		'unset Y; echo $(( Y = ++X )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'20\n4:5\n' -e empty ${TEST_SH} -c \
		'X=3 Y=4; echo $(( ++Y*++X )); echo $X:$Y'

	atf_check -s exit:0 -o inline:'2\n2\n' -e empty ${TEST_SH} -c \
		'set -u; X=1; echo $(( ++X )); echo $X'
	atf_check -s exit:0 -o inline:'1\n1:1\n' -e empty ${TEST_SH} -c \
		'set -u; X=0; unset Y; echo $(( Y = ++X )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'20\n4:5\n' -e empty ${TEST_SH} -c \
		'set -u; X=3 Y=4; echo $(( ++Y*++X )); echo $X:$Y'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; echo $(( ++X ))'
	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; unset Y; echo $(( X = ++Y ))'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'X=3; readonly X; echo $(( ++X ))'

}
atf_test_case var_predec
var_predec_head()
{
	atf_set "descr" "Test prefix -- operator"
}
var_predec_body()
{
	${TEST_SH} -c 'X=1; : $(( --X ))' 2>/dev/null ||
		atf_skip "${TEST_SH} does not support the prefix -- operator"

	unset X		; # just in case ...

	atf_check -s exit:0 -o inline:'0\n0\n' -e empty ${TEST_SH} -c \
		'X=1; echo $(( --X )); echo $X'
	atf_check -s exit:0 -o inline:'-1\n-1\n' -e empty ${TEST_SH} -c \
		'echo $(( --X )); echo $X'

	atf_check -s exit:0 -o inline:'-1\n-1:-1\n' -e empty ${TEST_SH} -c \
		'unset Y; echo $(( Y = --X )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'6\n2:3\n' -e empty ${TEST_SH} -c \
		'X=3 Y=4; echo $(( --Y*--X )); echo $X:$Y'

	atf_check -s exit:0 -o inline:'0\n0\n' -e empty ${TEST_SH} -c \
		'set -u; X=1; echo $(( --X )); echo $X'
	atf_check -s exit:0 -o inline:'-1\n-1:-1\n' -e empty ${TEST_SH} -c \
		'set -u; X=0; unset Y; echo $(( Y = --X )); echo $X:$Y'
	atf_check -s exit:0 -o inline:'6\n2:3\n' -e empty ${TEST_SH} -c \
		'set -u; X=3 Y=4; echo $(( --Y*--X )); echo $X:$Y'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; echo $(( --X ))'
	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'set -u; unset Y; echo $(( X = --Y ))'

	atf_check -s not-exit:0 -o empty -e not-empty ${TEST_SH} -c \
		'X=3; readonly X; echo $(( --X ))'

}

atf_test_case arithmetic_fails
arithmetic_fails_head()
{
	atf_set "descr" "Dummy test to force failure"
}
arithmetic_fails_body()
{
	atf_fail "Cannot estimate number of bits supported by $(( ))"
}

atf_init_test_cases() {

	discover_range

	test "${ARITH_BITS}" = '?' && {
		atf_add_test_case arithmetic_fails
		return 0
	}

	# odd names are to get atf's sort order semi-rational

	atf_add_test_case constants
	atf_add_test_case do_unary_plus
	atf_add_test_case do_unary_minus
	atf_add_test_case do_unary_not
	atf_add_test_case do_unary_tilde
	atf_add_test_case elementary_add
	atf_add_test_case elementary_sub
	atf_add_test_case elementary_mul
	atf_add_test_case elementary_div
	atf_add_test_case elementary_rem
	atf_add_test_case elementary_shl
	atf_add_test_case elementary_shr
	atf_add_test_case elementary_eq
	atf_add_test_case elementary_ne
	atf_add_test_case elementary_lt
	atf_add_test_case elementary_le
	atf_add_test_case elementary_gt
	atf_add_test_case elementary_ge
	atf_add_test_case fiddle_bits_and
	atf_add_test_case fiddle_bits_or
	atf_add_test_case fiddle_bits_xor
	atf_add_test_case logical_and
	atf_add_test_case logical_or
	atf_add_test_case make_selection
	atf_add_test_case nested_arith
	atf_add_test_case operator_precedence
	atf_add_test_case optional_comma
	atf_add_test_case parentheses
	# atf_add_test_case progressive			# build up big expr
	# atf_add_test_case test_errors			# erroneous input
	# atf_add_test_case torture		# hard stuff (if there is any)
	atf_add_test_case var_assign			# assignment ops
	atf_add_test_case var_postinc			# var++
	atf_add_test_case var_postdec			# var--
	atf_add_test_case var_preinc			# ++var
	atf_add_test_case var_predec			# --var
	# atf_add_test_case vulgarity	# truly evil inputs (syntax in vars...)
}
