local J=string.char local H=type local Q=string.sub local m=table.concat local D={}local n={}for H=0,255,1 do local Q,m=J(H),J(H,0)D[Q]=m n[m]=Q end local function Z(H,Q,m,D)if m>=256 then m,D=0,D+1 if D>=256 then Q={}D=1 end end Q[H]=J(m,D)m=m+1 return Q,m,D end local function l(J)if H(J)~="\115\116\114\105\110\103"then return nil,"\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 "..H(J)end local n=#J if n<=1 then return"\117"..J end local l={}local z,E=0,1 local b={"\099"}local F=1 local G=2 local g=""for H=1,n,1 do local m=Q(J,H,H)local q=g..m if not(D[q]or l[q])then local H=D[g]or l[g]if not H then return nil,"\097\108\103\111\114\105\116\104\109 \101\114\114\111\114\044 \099\111\117\108\100 \110\111\116 \102\101\116\099\104 \119\111\114\100"end b[G]=H F=F+#H G=G+1 if n<=F then return"\117"..J end l,z,E=Z(q,l,z,E)g=m else g=q end end b[G]=D[g]or l[g]F=F+#b[G]G=G+1 if n<=F then return"\117"..J end return m(b)end local function z(H,Q,m,D)if m>=256 then m,D=0,D+1 if D>=256 then Q={}D=1 end end Q[J(m,D)]=H m=m+1 return Q,m,D end local function E(J)if H(J)~="\115\116\114\105\110\103"then return nil,"\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 "..H(J)end if#J<1 then return nil,"\105\110\118\097\108\105\100 \105\110\112\117\116 - \110\111\116 \097 \099\111\109\112\114\101\115\115\101\100 \115\116\114\105\110\103"end local D=Q(J,1,1)if D=="\117"then return Q(J,2)elseif D~="\099"then return nil,"\105\110\118\097\108\105\100 \105\110\112\117\116 - \110\111\116 \097 \099\111\109\112\114\101\115\115\101\100 \115\116\114\105\110\103"end J=Q(J,2)local Z=#J if Z<2 then return nil,"\105\110\118\097\108\105\100 \105\110\112\117\116 - \110\111\116 \097 \099\111\109\112\114\101\115\115\101\100 \115\116\114\105\110\103"end local l={}local E,b=0,1 local F={}local G=1 local g=Q(J,1,2)F[G]=n[g]or l[g]G=G+1 for H=3,Z,2 do local m=Q(J,H,H+1)local D=n[g]or l[g]if not D then return nil,"\099\111\117\108\100 \110\111\116 \102\105\110\100 \108\097\115\116 \102\114\111\109 \100\105\099\116\046 \073\110\118\097\108\105\100 \105\110\112\117\116\063"end local Z=n[m]or l[m]if Z then F[G]=Z G=G+1 l,E,b=z(D..Q(Z,1,1),l,E,b)else local J=D..Q(D,1,1)F[G]=J G=G+1 l,E,b=z(J,l,E,b)end g=m end return m(F)end return{compress=l;decompress=E}
--[[
MIT License

Copyright (c) 2016 Rochet2

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]

local char = string.char
local type = type
local sub = string.sub
local tconcat = table.concat

local basedictcompress = {}
local basedictdecompress = {}
for i = 0, 255 do
	local ic, iic = char(i), char(i, 0)
	basedictcompress[ic] = iic
	basedictdecompress[iic] = ic
end

local function dictAddA(str, dict, a, b)
	if a >= 256 then
		a, b = 0, b + 1
		if b >= 256 then
			dict = {}
			b = 1
		end
	end
	dict[str] = char(a, b)
	a = a + 1
	return dict, a, b
end

local function compress(input)
	if type(input) ~= "string" then
		return nil, "string expected, got " .. type(input)
	end
	local len = #input
	if len <= 1 then
		return "u" .. input
	end

	local dict = {}
	local a, b = 0, 1

	local result = { "c" }
	local resultlen = 1
	local n = 2
	local word = ""
	for i = 1, len do
		local c = sub(input, i, i)
		local wc = word .. c
		if not (basedictcompress[wc] or dict[wc]) then
			local write = basedictcompress[word] or dict[word]
			if not write then
				return nil, "algorithm error, could not fetch word"
			end
			result[n] = write
			resultlen = resultlen + #write
			n = n + 1
			if len <= resultlen then
				return "u" .. input
			end
			dict, a, b = dictAddA(wc, dict, a, b)
			word = c
		else
			word = wc
		end
	end
	result[n] = basedictcompress[word] or dict[word]
	resultlen = resultlen + #result[n]
	n = n + 1
	if len <= resultlen then
		return "u" .. input
	end
	return tconcat(result)
end

local function dictAddB(str, dict, a, b)
	if a >= 256 then
		a, b = 0, b + 1
		if b >= 256 then
			dict = {}
			b = 1
		end
	end
	dict[char(a, b)] = str
	a = a + 1
	return dict, a, b
end

local function decompress(input)
	if type(input) ~= "string" then
		return nil, "string expected, got " .. type(input)
	end

	if #input < 1 then
		return nil, "invalid input - not a compressed string"
	end

	local control = sub(input, 1, 1)
	if control == "u" then
		return sub(input, 2)
	elseif control ~= "c" then
		return nil, "invalid input - not a compressed string"
	end
	input = sub(input, 2)
	local len = #input

	if len < 2 then
		return nil, "invalid input - not a compressed string"
	end

	local dict = {}
	local a, b = 0, 1

	local result = {}
	local n = 1
	local last = sub(input, 1, 2)
	result[n] = basedictdecompress[last] or dict[last]
	n = n + 1
	for i = 3, len, 2 do
		local code = sub(input, i, i + 1)
		local lastStr = basedictdecompress[last] or dict[last]
		if not lastStr then
			return nil, "could not find last from dict. Invalid input?"
		end
		local toAdd = basedictdecompress[code] or dict[code]
		if toAdd then
			result[n] = toAdd
			n = n + 1
			dict, a, b = dictAddB(lastStr .. sub(toAdd, 1, 1), dict, a, b)
		else
			local tmp = lastStr .. sub(lastStr, 1, 1)
			result[n] = tmp
			n = n + 1
			dict, a, b = dictAddB(tmp, dict, a, b)
		end
		last = code
	end
	return tconcat(result)
end

return {
	compress = compress,
	decompress = decompress,
}
