--:Minify:--
local lib={}

local function check(a,b,ignoreb)
    if type(a)~="vector" then error("a must be vector") end
    if not ignoreb and b~=nil then
        if type(b)~="vector" then error("b must be vector") end
        if #a~=#b then error("vectors must be same size") end
    end
end

local vector={
    add = function(a,b)
        check(a,b)
        local ret={}
        for i=1, #a do
            ret[i]=a[i]+b[i]
        end
        return lib.convert(ret)
    end,

    sub = function(a,b)
        check(a,b)
        if type(b)~="vector" then error("b must be vector") end
        local ret={}
        for i=1, #a do
            ret[i]=a[i]-b[i]
        end
        return lib.convert(ret)
    end,

    unm = function(a)
        local ret={}
        for i=1, #a do
            ret[i]=-a[i]
        end
        return lib.convert(ret)
    end,

    mul = function(a,b)
        if type(a)=="number" and type(b)=="vector" then a,b=b,a end
        check(a,b,true)
        local ret={}
        if type(b)=="vector" then
            for i=1, #a do
                ret[i]=a[i]*b[i]
            end
        elseif type(b)=="number" then
            for i=1, #a do
                ret[i]=a[i]*b
            end
        else
            error("b must be vector or number")
        end
        return lib.convert(ret)
    end,

    div = function(a,b)
        if type(a)=="number" and type(b)=="vector" then a,b=b,a end
        check(a,b,true)
        local ret={}
        if type(b)=="vector" then
            for i=1, #a do
                ret[i]=a[i]/b[i]
            end
        elseif type(b)=="number" then
            for i=1, #a do
                ret[i]=a[i]/b
            end
        else
            error("b must be vector or number")
        end
        return lib.convert(ret)
    end,

    dot = function(a,b)
        check(a,b)
        local out=a[1]*b[1]
        for i=2, #a do
            out=out+a[i]*b[i]
        end
        return out
    end,

    tostring = function(a)
        check(a)
        local out = "{"
        for i=1, #a do
            out=out..tostring(a[i])..", "
        end
        return out:sub(1,#out-2).."}"
    end,

    equals = function(a,b)
        check(a,b)
        for i=1, #a do
            if a[i]~=b[i] then
                return false
            end
        end
        return true
    end,

    cross = function(a,b)
        check(a,b)
        if #a~=3 then error("Vectors must be of type 'vec3'") end
        if #b~=3 then error("Vectors must be of type 'vec3'") end
        return lib.convert({
            a.y*b.z-a.z*b.y,
            a.z*b.x-a.x*b.z,
            a.x*b.y-a.y*b.x
        })
    end,

    length = function(a)
        check(a)
        local out=a[1]*a[1]
        for i=2, #a do
            out=out+a[i]*a[i]
        end
        if type(out)=="vector" then
            return out:sqrt()
        else
            return math.sqrt(out)
        end
    end,

    len = function(a)
        return a:length()
    end,

    normalize = function(a)
        return a*(1/a:len())
    end,

    round = function(a, t)
        check(a)
        local ret={}
        for i=1, #a do
            ret[i]=math.floor((a[i]+t*0.5)/t)*t
        end
        return lib.convert(ret)
    end,

    exp = function(a, e)
        check(a)
        local ret={}
        for i=1, #a do
            ret[i]=a^e
        end
        return lib.convert(ret)
    end,

    sqrt = function(a)
        check(a)
        local ret={}
        for i=1, #a do
            if type(a[i])=="vector" then
                ret[i]=a[i]:sqrt()
            else
                ret[i]=math.sqrt(a[i])
            end
        end
        return lib.convert(ret)
    end
}

local idxtable={
    w=4,
    x=1,
    y=2,
    z=3,
}

local function index(self, k)
    if idxtable[k] then
        return rawget(self,idxtable[k])
    elseif vector[k] then
        return vector[k]
    else
        return rawget(self,k)
    end
end

local function newindex(self, k, v)
    if idxtable[k] then
        rawset(self, idxtable[k], v)
    else
        rawset(self, k, v)
    end
end

local meta={
    __name="vector",
    __type="vector",
    __usermeta=function() error("EACCES") end,
    __index=index,
    __newindex=newindex,
    __unm=vector.unm,
    __add=vector.add,
    __sub=vector.sub,
    __mul=vector.mul,
    __div=vector.div,
    __tostring=vector.tostring,
    __eq=vector.equals
}

function lib.new(...)
    local vars={...}
    return setmetatable(vars,meta)
end

function lib.convert(vars)
    return setmetatable(vars,meta)
end

return lib