print('testing strings and string library')

assert('alo' < 'alo1')
assert('' < 'a')
assert('alo\0alo' < 'alo\0b')
assert('alo\0alo\0\0' > 'alo\0alo\0')
assert('alo' < 'alo\0')
assert('alo\0' > 'alo')
assert('\0' < '\1')
assert('\0\0' < '\0\1')
assert('\1\0a\0a' <= '\1\0a\0a')
assert(not ('\1\0a\0b' <= '\1\0a\0a'))
assert('\0\0\0' < '\0\0\0\0')
assert(not('\0\0\0\0' < '\0\0\0'))
assert('\0\0\0' <= '\0\0\0\0')
assert(not('\0\0\0\0' <= '\0\0\0'))
assert('\0\0\0' <= '\0\0\0')
assert('\0\0\0' >= '\0\0\0')
assert(not ('\0\0b' < '\0\0a\0'))
print('+')

assert(string.sub("123456789",2,4) == "234")
assert(string.sub("123456789",7) == "789")
assert(string.sub("123456789",7,6) == "")
assert(string.sub("123456789",7,7) == "7")
assert(string.sub("123456789",0,0) == "")
assert(string.sub("123456789",-10,10) == "123456789")
assert(string.sub("123456789",1,9) == "123456789")
assert(string.sub("123456789",-10,-20) == "")
assert(string.sub("123456789",-1) == "9")
assert(string.sub("123456789",-4) == "6789")
assert(string.sub("123456789",-6, -4) == "456")
assert(string.sub("\000123456789",3,5) == "234")
assert(("\000123456789"):sub(8) == "789")
print('+')

assert(string.find("123456789", "345") == 3)
a,b = string.find("123456789", "345")
assert(string.sub("123456789", a, b) == "345")
assert(string.find("1234567890123456789", "345", 3) == 3)
assert(string.find("1234567890123456789", "345", 4) == 13)
assert(string.find("1234567890123456789", "346", 4) == nil)
assert(string.find("1234567890123456789", ".45", -9) == 13)
assert(string.find("abcdefg", "\0", 5, 1) == nil)
assert(string.find("", "") == 1)
assert(string.find('', 'aaa', 1) == nil)
assert(('alo(.)alo'):find('(.)', 1, 1) == 4)
print('+')

assert(string.len("") == 0)
assert(string.len("\0\0\0") == 3)
assert(string.len("1234567890") == 10)

assert(#"" == 0)
assert(#"\0\0\0" == 3)
assert(#"1234567890" == 10)

assert(string.byte("a") == 97)
assert(string.byte("á") > 127)
assert(string.byte(string.char(255)) == 255)
assert(string.byte(string.char(0)) == 0)
assert(string.byte("\0") == 0)
assert(string.byte("\0\0alo\0x", -1) == string.byte('x'))
assert(string.byte("ba", 2) == 97)
assert(string.byte("\n\n", 2, -1) == 10)
assert(string.byte("\n\n", 2, 2) == 10)
assert(string.byte("") == nil)
assert(string.byte("hi", -3) == nil)
assert(string.byte("hi", 3) == nil)
assert(string.byte("hi", 9, 10) == nil)
assert(string.byte("hi", 2, 1) == nil)
assert(string.char() == "")
assert(string.char(0, 255, 0) == "\0\255\0")
assert(string.char(0, string.byte("á"), 0) == "\0á\0")
assert(string.char(string.byte("ál\0óu", 1, -1)) == "ál\0óu")
assert(string.char(string.byte("ál\0óu", 1, 0)) == "")
assert(string.char(string.byte("ál\0óu", -10, 100)) == "ál\0óu")
print('+')

assert(string.upper("ab\0c") == "AB\0C")
assert(string.lower("\0ABCc%$") == "\0abcc%$")
assert(string.rep('teste', 0) == '')
assert(string.rep('tés\00tê', 2) == 'tés\0têtés\000tê')
assert(string.rep('', 10) == '')

assert(string.reverse"" == "")
assert(string.reverse"\0\1\2\3" == "\3\2\1\0")
assert(string.reverse"\0001234" == "4321\0")

for i=0,30 do assert(string.len(string.rep('a', i)) == i) end

assert(type(tostring(nil)) == 'string')
assert(type(tostring(12)) == 'string')
assert(''..12 == '12' and type(12 .. '') == 'string')
assert(string.find(tostring{}, 'table:'))
assert(string.find(tostring(print), 'function:'))
assert(tostring(1234567890123) == '1234567890123')
assert(#tostring('\0') == 1)
assert(tostring(true) == "true")
assert(tostring(false) == "false")
print('+')

x = '"ílo"\n\\'
assert(string.format('%q%s', x, x) == '"\\"ílo\\"\\\n\\\\""ílo"\n\\')
--[[
LuaJIT: LuaJIT since v2.0.0-beta6 has extension from Lua 5.2:
| string.format(): %q reversible.

The q option formats a string in a form suitable to be safely read
back by the Lua interpreter [1].

In Lua 5.1, '\0' was converted to "\000" unconditionally.

In Lua 5.2, control characters are written as \nnn when needed,
see d62a21b9d379a576bae7426c80039ca1a4d2bb07 ("when formatting
with '%q', all control characters are coded as \nnn.") [2].
In this patch, %q specifier starts writing control characters to
a new string in the same way through \d, instead of their binary
representation as it is. If the control character is followed by
a digit, then for the correct work of the parser, it is necessary
to extend the escape sequence to 3 significant characters
(otherwise, the transition "\0002" -> "\02" corrupts string).
For this patch, the expansion is performed unconditionally
(the check for the next symbol is not performed) if this first
symbol is a zero byte.

Consistent work with the zero byte processing omitting the custom
check was added in the commit
658ea8752b979102627e2fede7b7ddfbb67ba6c9 ("no need to handle '\0'
differently from other control chars in format '%q'") [3]. Note,
that this patch does not change the semantics of %q specifier --
the new string can still be "safely read back by the Lua
interpreter".

[1]: https://www.lua.org/manual/5.1/manual.html#pdf-string.format
[2]: https://github.com/lua/lua/commit/d62a21b9d379a576bae7426c80039ca1a4d2bb07
[3]: https://github.com/lua/lua/commit/658ea8752b979102627e2fede7b7ddfbb67ba6c9

Test is adapted from PUC-Rio Lua 5.2 test suite.
--]]
assert(string.format('%q', "\0") == [["\0"]])
assert(string.format("\0%c\0%c%x\0", string.byte("á"), string.byte("b"), 140) ==
              "\0á\0b8c\0")
assert(string.format('') == "")
assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) ==
       string.format("%c%c%c%c", 34, 48, 90, 100))
assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be')
assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023")
assert(tonumber(string.format("%f", 10.3)) == 10.3)
x = string.format('"%-50s"', 'a')
assert(#x == 52)
assert(string.sub(x, 1, 4) == '"a  ')

assert(string.format("-%.20s.20s", string.rep("%", 2000)) == "-"..string.rep("%", 20)..".20s")
assert(string.format('"-%20s.20s"', string.rep("%", 2000)) ==
       string.format("%q", "-"..string.rep("%", 2000)..".20s"))


-- longest number that can be formatted
assert(string.len(string.format('%99.99f', -1e308)) >= 100)

assert(loadstring("return 1\n--comentário sem EOL no final")() == 1)


assert(table.concat{} == "")
assert(table.concat({}, 'x') == "")
assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2")
local a = {}; for i=1,3000 do a[i] = "xuxu" end
assert(table.concat(a, "123").."123" == string.rep("xuxu123", 3000))
assert(table.concat(a, "b", 20, 20) == "xuxu")
assert(table.concat(a, "", 20, 21) == "xuxuxuxu")
assert(table.concat(a, "", 22, 21) == "")
assert(table.concat(a, "3", 2999) == "xuxu3xuxu")

a = {"a","b","c"}
assert(table.concat(a, ",", 1, 0) == "")
assert(table.concat(a, ",", 1, 1) == "a")
assert(table.concat(a, ",", 1, 2) == "a,b")
assert(table.concat(a, ",", 2) == "b,c")
assert(table.concat(a, ",", 3) == "c")
assert(table.concat(a, ",", 4) == "")

local locales = { "ptb", "ISO-8859-1", "pt_BR" }
local function trylocale (w)
  for _, l in ipairs(locales) do
    if os.setlocale(l, w) then return true end
  end
  return false
end

-- LuaJIT: LuaJIT doesn't compare strings by `strcoll()`,
-- like Lua 5.1 does.
-- Tests are disabled for LuaJIT.
--[[
if not trylocale("collate")  then
  print("locale not supported")
else
  assert("alo" < "álo" and "álo" < "amo")
end

if not trylocale("ctype") then
  print("locale not supported")
else
  assert(string.gsub("áéíóú", "%a", "x") == "xxxxx")
  assert(string.gsub("áÁéÉ", "%l", "x") == "xÁxÉ")
  assert(string.gsub("áÁéÉ", "%u", "x") == "áxéx")
  assert(string.upper"áÁé{xuxu}ção" == "ÁÁÉ{XUXU}ÇÃO")
end
--]]

os.setlocale("C")
assert(os.setlocale() == 'C')
assert(os.setlocale(nil, "numeric") == 'C')

print('OK')


