local fn = {}

fn.shuffle = function(list)
	for i = #list, 2, -1 do
		local j = math.random(i)
		list[i], list[j] = list[j], list[i]
	end
	return list
end

fn.cshuf = function(list)
	return fn.shuffle(table.copy(list))
end

fn.urnd = function(min,max)
	local r = {}
	for i=min,max do r[1 + (i - min)] = i end
	fn.shuffle(r)
	return r
end

fn.uniq = function(lst)
	local hash = {}
	local new = {}
	for i,v in ipairs(lst) do
		if not hash[v] then
			hash[v] = true
			new[#new+1] = v
		end
	end
	return new
end

fn.scramble = function(list)
	local new = table.copy(list)
	fn.shuffle(new)
	return new
end

fn.copy = function(t)
	local new = {}
	for i,v in pairs(t) do new[i] = v end
	setmetatable(new,getmetatable(t))
	return new
end

fn.deepcopy = table.copy or function(t)
	new = {}
	for k,v in pairs(t) do
		if type(v) == 'table' then
			new[k] = fn.deepcopy(v)
		else
			new[k] = v
		end
	end
	return new
end

fn.merge = function(base,override)
	local new = fn.copy(base)
	for k,v in pairs(override) do
		new[k] = v
	end
	return new
end

fn.deepmerge = function(base,override,func)
	local new = {}
	local keys = fn.merge(fn.keys(base),fn.keys(override))
	for _,k in pairs(keys) do
		if type(base[k]) == 'table' and
		   type(override[k]) == 'table' then
			new[k] = fn.deepmerge(base[k], override[k], func)
		elseif func and override[k] and base[k] then
			new[k] = func(base[k],override[k], k)
		elseif override[k] then
			new[k] = override[k]
		else
			new[k] = base[k]
		end
	end
	return new
end

fn.append = function(r1, r2)
	local new = fn.copy(r1)
	for i=1,#r2 do
		new[#new + 1] = r2[i]
	end
	return new
end

fn.capitalize = function(str)
	return string.upper(string.sub(str, 1,1)) .. string.sub(str, 2)
end

fn.has = function(tbl,value,eqfn)
	for k,v in pairs(tbl) do
		if eqfn then
			if eqfn(v,value,tbl) then return true, k end
		else
			if value == v then return true, k end
		end
	end
	return false, nil
end

fn.keys = function(lst)
	local ks = {}
	for k,_ in pairs(lst) do
		ks[#ks + 1] = k
	end
	return ks
end

fn.pick = function(lst)
	local keys = fn.keys(lst)
	local k = keys[math.random(#keys)]
	return k, lst[k]
end

fn.unpack = function(tbl,i)
	i = i or 1
	if #tbl == i then return tbl[i] end
	return tbl[i], fn.unpack(tbl, i+1)
end

fn.split = function(...) return fn.unpack(sorcery.lib.str.explode(...)) end

fn.each = function(tbl,f)
	local r = {}
	for k,v in pairs(tbl) do
		local v, c = f(v,k)
		r[#r+1] = v
		if c == false then break end
	end
	return r
end

fn.each_o = function(tbl,f)
	local keys = fn.keys(tbl)
	table.sort(keys)
	return fn.each(keys, function(k,i)
		return f(tbl[k],k,i)
	end)
end

fn.iter = function(tbl,fn)
	for i=1,#tbl do
		fn(tbl[i], i)
	end
end

fn.map = function(tbl,fn)
	local new = {}
	for k,v in pairs(tbl) do
		local nv, nk = fn(v, k)
		new[nk or k] = nv
	end
	return new
end

fn.fold = function(tbl,fn,acc)
	if #tbl == 0 then
		fn.each_o(tbl, function(v)
			acc = fn(acc, v, k)
		end)
	else
		for i=0,#tbl do
			acc = fn(acc,tbl[i],i)
		end
	end
	return acc
end

fn.walk = function(tbl,path)
	if type(path) == 'table' then
		for _,p in pairs(path) do
			if tbl == nil or tbl[p] == nil then return nil end
			tbl = tbl[p]
		end
	else
		tbl = tbl[path]
	end
	return tbl
end

fn.proto = function(tbl,proto)
	local meta = getmetatable(tbl)
	local nm = {__index = proto or tbl}
	if meta ~= nil then
		nm = table.copy(meta)
		nm[__index] = proto
		nm[__metatable] = meta
	end
	return setmetatable(tbl or {},nm)
end

fn.case = function(e, c)
	if type(c[e]) == 'function'
		then return (c[e])(e)
		else return c[e]
	end
end

fn.cond = function(exp, c)
	for i, v in ipairs(c) do
		if c[1](exp) then return c[2](exp) end
	end
end

return fn
