#RUN: %fish %s
# OpenBSD doesn't do hex numbers in str/wcstod (like C99 requires).
# So let's skip this.
#REQUIRES: test "$(uname)" != OpenBSD
# Validate basic expressions
math 3 / 2
# CHECK: 1.5
math 10/6
# CHECK: 1.666667
math -s0 10 / 6
# CHECK: 1
math 'floor(10 / 6)'
# CHECK: 1
math -s3 10/6
# CHECK: 1.667
math '10 % 6'
# CHECK: 4
math -s0 '10 % 6'
# CHECK: 4
math '23 % 7'
# CHECK: 2
math --scale=6 '5 / 3 * 0.3'
# CHECK: 0.5
math --scale=max '5 / 3'
# CHECK: 1.666666666666667
math --scale=1 --base=16 "2 / 3 - 1"
# CHECKERR: math: invalid option combination, non-zero scale value only valid for base 10
math --scale=abc '5 / 3'
# CHECKERR: math: abc: invalid scale
math --scale=-1 '5 / 3'
# CHECKERR: math: -1: invalid scale
math --scale=16 '5 / 3'
# CHECKERR: math: 16: invalid scale
math "7^2"
# CHECK: 49
math -1 + 1
# CHECK: 0
math '-2 * -2'
# CHECK: 4
math 5 \* -2
# CHECK: -10
math -- -4 / 2
# CHECK: -2
math -- '-4 * 2'
# CHECK: -8

# Validate max and min
math 'max(1,2)'
math 'min(1,2)'
# CHECK: 2
# CHECK: 1

# Validate some rounding functions
math 'round(3/2)'
math 'floor(3/2)'
math 'ceil(3/2)'
# CHECK: 2
# CHECK: 1
# CHECK: 2
math 'round(-3/2)'
math 'floor(-3/2)'
math 'ceil(-3/2)'
# CHECK: -2
# CHECK: -2
# CHECK: -1

# Validate some integral computations
math 1
math 10
math 100
math 1000
# CHECK: 1
# CHECK: 10
# CHECK: 100
# CHECK: 1000
math '10^15'
math '-10^14'
math '-10^15'
# CHECK: 1000000000000000
# CHECK: 100000000000000
# CHECK: -1000000000000000

# Floating point operations under x86 without SSE2 have reduced accuracy!
# This includes both i586 targets and i686 under Debian, where it's patched to remove SSE2.
# As a result, some floating point tests use regular expressions to loosely match against
# the shape of the expected result.

# NB: The i586 case should also pass under other platforms, but not the other way around.
math "3^0.5^2"
# CHECK: {{1\.316074|1\.316\d+}}

math -2^2
# CHECK: 4

math -s0 '1.0 / 2.0'
math -s0 '3.0 / 2.0'
math -s0 '10^15 / 2.0'
# CHECK: 0
# CHECK: 1
# CHECK: 500000000000000

# Validate how variables in an expression are handled
math $x + 1
set x 1
math $x + 1
set x 3
set y 1.5
math "-$x * $y"
math -s0 "-$x * $y"
# CHECK: 1
# CHECK: 2
# CHECK: -4.5
# CHECK: -4

# Validate math error reporting
# NOTE: The leading whitespace for the carets here is ignored
# by littlecheck.
not math '2 - '
# CHECKERR: math: Error: Too few arguments
# CHECKERR: '2 - '
# CHECKERR:     ^
not math 'ncr(1)'
# CHECKERR: math: Error: Too few arguments
# CHECKERR: 'ncr(1)'
# CHECKERR:       ^

math "min()"
# CHECKERR: math: Error: Too few arguments
# CHECKERR: 'min()'
# CHECKERR:      ^

# There is no "blah" function.
not math 'blah()'
# CHECKERR: math: Error: Unknown function
# CHECKERR: 'blah()'
# CHECKERR:  ^~~^

# There is also no "Blah" function.
not math 'Blah()'
# CHECKERR: math: Error: Unknown function
# CHECKERR: 'Blah()'
# CHECKERR:  ^~~^

math n + 4
# CHECKERR: math: Error: Unknown function
# CHECKERR: 'n + 4'
# CHECKERR:  ^

not math 'sin()'
# CHECKERR: math: Error: Too few arguments
# CHECKERR: 'sin()'
# CHECKERR:      ^
not math '2 + 2 4'
# CHECKERR: math: Error: Missing operator
# CHECKERR: '2 + 2 4'
# This regex to check whitespace - the error appears between the second 2 and the 4!
# (right after the 2)
# CHECKERR: {{^}}      ^
printf '<%s>\n' (math '2 + 2      4' 2>&1)
# CHECK: <math: Error: Missing operator>
# CHECK: <'2 + 2      4'>
# CHECK: <      ^~~~~^>

not math '(1 2)'
# CHECKERR: math: Error: Missing operator
# CHECKERR: '(1 2)'
# CHECKERR:    ^
not math '(1 pi)'
# CHECKERR: math: Error: Missing operator
# CHECKERR: '(1 pi)'
# CHECKERR:    ^
not math '(1 pow 1,2)'
# CHECKERR: math: Error: Missing operator
# CHECKERR: '(1 pow 1,2)'
# CHECKERR:    ^
not math
# CHECKERR: math: expected >= 1 arguments; got 0
not math -s 12
# CHECKERR: math: expected >= 1 arguments; got 0
# XXX FIXME these two should be just "missing argument" errors, the count is not helpful
not math 2^999999
# CHECKERR: math: Error: Result is infinite
# CHECKERR: '2^999999'
not math 'sqrt(-1)'
# CHECKERR: math: Error: Result is not a number
# CHECKERR: 'sqrt(-1)'
math 'sqrt(-0)'
# CHECK: -0
not math 2^53 + 1
# CHECKERR: math: Error: Result magnitude is too large
# CHECKERR: '2^53 + 1'
not math -2^53 - 1
# CHECKERR: math: Error: Result magnitude is too large
# CHECKERR: '-2^53 - 1'
printf '<%s>\n' (not math 1 / 0 2>&1)
# CHECK: <math: Error: Division by zero>
# CHECK: <'1 / 0'>
# CHECK: <   ^>
printf '<%s>\n' (math 1 % 0 - 5 2>&1)
# CHECK: <math: Error: Division by zero>
# CHECK: <'1 % 0 - 5'>
# CHECK: <   ^>
printf '<%s>\n' (math min 1 / 0, 5 2>&1)
# CHECK: <math: Error: Division by zero>
# CHECK: <'min 1 / 0, 5'>
# CHECK: <       ^>

# Validate "x" as multiplier
math 0x2 # Hex
math 5 x 4
math 2x 4
math 2 x4 # ERROR
# CHECKERR: math: Error: Unknown function
# CHECKERR: '2 x4'
# CHECKERR:    ^^
math 0x 3
# CHECK: 2
# CHECK: 20
# CHECK: 8
# CHECK: 0

math "42 >= 1337"
# CHECKERR: math: Error: Logical operations are not supported, use `test` instead
# CHECKERR: '42 >= 1337'
# CHECKERR:     ^

math "bitand(0xFE, 1)"
# CHECK: 0
math "bitor(0xFE, 1)"
# CHECK: 255
math "bitxor(5, 1)"
# CHECK: 4
math "bitand(5.5, 2)"
# CHECK: 0
math "bitand(5.5, 1)"
# CHECK: 1

math "bitor(37 ^ 5, 255)"
# CHECK: 69343999

math 'log 16'
# CHECK: 1.20412

math 'log(16'
# CHECKERR: math: Error: Missing closing parenthesis
# CHECKERR: 'log(16'
# CHECKERR:       ^

math '(2'
# CHECKERR: math: Error: Missing closing parenthesis
# CHECKERR: '(2'
# CHECKERR:   ^

math --base=16 255 / 15
# CHECK: 0x11
math -bhex 16 x 2
# CHECK: 0x20
math --base hex 12 + 0x50
# CHECK: 0x5c
math --base hex 0
# CHECK: 0x0
math --base hex -1
# CHECK: -0x1
math --base hex -15
# CHECK: -0xf
math --base hex 'pow(2,40)'
# CHECK: 0x10000000000
math --base octal 0
# CHECK: 0
math --base octal -1
# CHECK: -01
math --base octal -15
# CHECK: -017
math --base octal 'pow(2,40)'
# CHECK: 020000000000000
math --base octal --scale=0 55
# CHECK: 067
math --base notabase
# CHECKERR: math: notabase: invalid base value
echo $status
# CHECK: 2

math 'log2(8)'
# CHECK: 3

math 'log(8) / log(2)'
# CHECK: 3
math 'log(625) / log(5)'
# CHECK: 4

# same as sin(cos(2 x pi))
math sin cos 2 x pi
# CHECK: 0.841471
# Inner function binds stronger, so this is interpreted as
# pow(sin(3,5))

math pow sin 3, 5
# CHECKERR: math: Error: Too many arguments
# CHECKERR: 'pow sin 3, 5'
# CHECKERR: ^

math pow sin 3, 5 + 2
# CHECKERR: math: Error: Too many arguments
# CHECKERR: 'pow sin 3, 5 + 2'
# CHECKERR:             ^~~~^

math sin pow 3, 5
# CHECK: {{-0\.890009|-0.890\d*}}

math pow 2, cos -pi
# CHECK: 0.5

# pow(2*cos(-pi), 2)
# i.e. 2^2
# i.e. 4
math pow 2 x cos'(-pi)', 2
# CHECK: 4

# This used to take ages, see #8170.
# If this test hangs, that's reintroduced!
math 'ncr(0/0, 1)'
# CHECKERR: math: Error: Division by zero
# CHECKERR: 'ncr(0/0, 1)'
# CHECKERR:       ^

# Variadic functions require at least one argument
math min
# CHECKERR: math: Error: Too few arguments
# CHECKERR: 'min'
# CHECKERR:    ^
math min 2
# CHECK: 2
math min 2, 3, 4, 5, -10, 1
# CHECK: -10

# Parentheses are required to disambiguate function call nested in argument list,
# except when the call is the last argument.
math 'min 5, 4, 3, ncr 2, 1, 5'
# CHECKERR: math: Error: Too many arguments
# CHECKERR:      'min 5, 4, 3, ncr 2, 1, 5'
# CHECKERR: {{^}}                        ^
math 'min 5, 4, 3, ncr(2, 1), 5'
# CHECK: 2
math 'min 5, 4, 3, 5, ncr 2, 1'
# CHECK: 2
# Variadic function consumes all available arguments,
# so it is always the last argument unless parenthesised.
# max(1, 2, min(3, 4, 5))
math 'max 1, 2, min 3, 4, 5'
# CHECK: 3
# max(1, 2, min(3, 4), 5)
math 'max 1, 2, min(3, 4), 5'
# CHECK: 5
math 0_1
# CHECK: 1
math 0x0_A
# CHECK: 10
math 1_000 + 2_000
# CHECK: 3000
math 1_0_0_0
# CHECK: 1000
math 0_0.5_0 + 0_1.0_0
# CHECK: 1.5
math 2e0_0_2
# CHECK: 200
math -0_0.5_0_0E0_0_3
# CHECK: -500
math 20e-0_1
# CHECK: 2
math 0x0_2.0_0_0P0_2
# CHECK: 8
math -0x8p-0_3
# CHECK: -1

echo 5 + 6 | math
# CHECK: 11

# Historical: If we have arguments on stdin and argv,
# the former takes precedence and the latter is ignored entirely.
echo 7 + 6 | math 2 + 2
# CHECK: 13

# It isn't checked at all.
echo 7 + 8 | math not an expression
# CHECK: 15

math (string repeat -n 1000 1) 2>| string shorten -m50 --char=""
# CHECK: math: Error: Number is too large
# CHECK: '1111111111111111111111111111111111111111111111111
# CHECK:  ^

math 0x0_2.0P-0x3
# CHECKERR: math: Error: Unknown function
# CHECKERR: '0x0_2.0P-0x3'
# CHECKERR:            ^^
math 0x0_2.0P-f
# CHECKERR: math: Error: Unexpected token
# CHECKERR: '0x0_2.0P-f'
# CHECKERR:           ^
math "22 / 5 - 5"
# CHECK: -0.6
math -s 0 --scale-mode=truncate "22 / 5 - 5"
# CHECK: -0
math --scale=0 -m truncate "22 / 5 - 5"
# CHECK: -0
math -s 0 --scale-mode=floor "22 / 5 - 5"
# CHECK: -1
math -s 0 --scale-mode=round "22 / 5 - 5"
# CHECK: -1
math -s 0 --scale-mode=ceiling "22 / 5 - 5"
# CHECK: -0
math "1 / 3 - 1"
# CHECK: -0.666667
math --scale-mode=truncate "1 / 3 - 1"
# CHECK: -0.666666
math --scale-mode=floor "1 / 3 - 1"
# CHECK: {{-0.666667|-0.666668}}
math --scale-mode=floor "2 / 3 - 1"
# CHECK: {{-0.333334|-0.333335}}
math --scale-mode=round "1 / 3 - 1"
# CHECK: {{-0.666667|-0.666668}}
math --scale-mode=ceiling "1 / 3 - 1"
# CHECK: -0.666666
math --scale-mode=ceiling "2 / 3 - 1"
# CHECK: -0.333333
math -s 6 --scale-mode=truncate "1 / 3 - 1"
# CHECK: -0.666666
math -s 6 --scale-mode=floor "1 / 3 - 1"
# CHECK: {{-0.666667|-0.666668}}
math -s 6 --scale-mode=floor "2 / 3 - 1"
# CHECK: {{-0.333334|-0.333335}}
math -s 6 --scale-mode=round "1 / 3 - 1"
# CHECK: {{-0.666667|-0.666668}}
math -s 6 --scale-mode=ceiling "1 / 3 - 1"
# CHECK: -0.666666
math -s 6 --scale-mode=ceiling "2 / 3 - 1"
# CHECK: -0.333333

math -s 6 --scale-mode=random "2 / 3 - 1"
# CHECKERR: math: random: invalid mode
