if not cjson then cjson = require "cjson" end

-- AGG define section for STRING_AGG
function STRING_AGG(...) 
    local self = {}
    local _stringarray = {}
    local _sep = rawget({...}, 2)  
    function self.state(gid, agg)  
        local cache = _stringarray[gid]
        if not cache then
            cache = {}
            _stringarray[gid] = cache
        end
        cache[#cache + 1] = agg
    end
    function self.final(gid)
        return table.concat(_stringarray[gid], _sep)
    end  
    return self
end

--  AGG define section for JSON_AGG
function JSON_AGG()
    local self = {}
    local _jsonarray={}
    function self.state(gid,value)
        local cache = _jsonarray[gid]
        if not cache then
            cache = {}
            _jsonarray[gid] = cache
        end
        cache[#cache + 1] = value
    end
    function self.final(gid)
        return cjson.encode_escape(_jsonarray[gid])
    end 
    return self
end

--  AGG define section for JSON_OBJECT_AGG
function JSON_OBJECT_AGG()
    local self = { }
    local _jsonarray = {}
    function self.state(gid,key,value)
        if not key then return end
        if not _jsonarray[gid] then _jsonarray[gid] = {} end
        _jsonarray[gid][key] = value
    end
    function self.final(gid)
        return cjson.encode_escape(_jsonarray[gid])
    end 
    return self
end

-- AGG define section for FIRST
function FIRST()
    local self = {}
    local _checked = {}
    local _first = {}
    function self.state(gid, agg)
        if not _checked[gid] then
            _checked[gid] = true
            _first[gid] = agg
        end
    end
    function self.final(gid)
        return _first[gid]
    end
    return self
end

-- AGG define section for LAST
function LAST()
    local self = {}
    local _last = {}
    function self.state(gid, agg)
        _last[gid] = agg
    end
    function self.final(gid)
        return _last[gid]
    end
    return self
end

-- AGG define section for STDDEV_POP
function STDDEV_POP()
    local self = {}
    local _num = {}
    local _sum = {}     -- sum
    local _sqsum = {}   -- square of sum
    local tb = {}
    function self.state(gid, value)
        if value == nil or type(value) ~= 'number' then return end
        tb[1] = _num[gid]
        if not tb[1] then
            _num[gid] = 1
            _sum[gid] = value
            _sqsum[gid] = value ^ 2
        else
            tb[2] = _sum[gid]
            tb[3] = _sqsum[gid]
            _num[gid] = tb[1] + 1
            _sum[gid] = tb[2] + value
            _sqsum[gid] = tb[3] + value ^ 2
            
        end
    end
    function self.final(gid)
        local num = _num[gid]
        if num and num > 0 then
            return math.sqrt(_sqsum[gid] / _num[gid] - ((_sum[gid] / _num[gid]) ^ 2))
        else
            return 0
        end
    end
    return self
end

-- AGG define section for STDDEV_SAMP
function STDDEV_SAMP()
    local self = {}
    local _sum = {}     -- sum
    local _sqsum = {}   -- square of sum
    local _num = {}
    local tb = {}
    function self.state(gid, value)
        if value == nil or type(value) ~= 'number' then return end
        tb[1] = _num[gid]
        if not tb[1] then
            _num[gid] = 1
            _sum[gid] = value
            _sqsum[gid] = value ^ 2
        else
            tb[2] = _sum[gid]
            tb[3] = _sqsum[gid]
            _num[gid] = tb[1] + 1
            _sum[gid] = tb[2] + value
            _sqsum[gid] = tb[3] + value ^ 2
        end
    end
    function self.final(gid)
        local num = _num[gid]
        if num and num > 0 then
            return math.sqrt((_sqsum[gid] - (_num[gid] * ((_sum[gid] / _num[gid]) ^ 2))) /
                             (_num[gid] - 1))
        else
            return 0
        end
    end
    return self
end

-- AGG define section for VAR_POP
function VAR_POP()
    local self = {}
    local _sum = {}     -- sum
    local _sqsum = {}   -- square of sum
    local _num = {}
    local tb = {}
    function self.state(gid, value)
        if value == nil or type(value) ~= 'number' then return end
        tb[1] = _num[gid]
        if not tb[1] then
            _num[gid] = 1
            _sum[gid] = value
            _sqsum[gid] = value ^ 2
        else
            tb[2] = _sum[gid]
            tb[3] = _sqsum[gid]
            _num[gid] = tb[1] + 1
            _sum[gid] = tb[2] + value
            _sqsum[gid] = tb[3] + value ^ 2
        end
    end
    function self.final(gid)
        local num = _num[gid]
        if num and num > 0 then
            return _sqsum[gid] / _num[gid] - ((_sum[gid] / _num[gid]) ^ 2)
        else
            return 0
        end
    end
    return self
end

-- AGG define section for VAR_SAMP
function VAR_SAMP()
    local self = {}
    local _sum = {}     -- sum
    local _sqsum = {}   -- square of sum
    local _num = {}
    local tb = {}
    function self.state(gid, value)
        if value == nil or type(value) ~= 'number' then return end
        tb[1] = _num[gid]
        if not tb[1] then
            _num[gid] = 1
            _sum[gid] = value
            _sqsum[gid] = value ^ 2
        else
            tb[2] = _sum[gid]
            tb[3] = _sqsum[gid]
            _num[gid] = tb[1] + 1
            _sum[gid] = tb[2] + value
            _sqsum[gid] = tb[3] + value ^ 2
        end
    end
    function self.final(gid)
        local num = _num[gid]
        if num and num > 0 then
            return (_sqsum[gid] - (_num[gid] * ((_sum[gid] / _num[gid]) ^ 2))) / (_num[gid] - 1)
        else
            return 0
        end
    end
    return self
end

-- AGG define section for BIT_AND
function BIT_AND()
    local self = {}
    local _bit_and = {}
    function self.state(gid, value)
        if value == nil or type(value) ~= 'number' then return end
        local ba = _bit_and[gid]
        if not ba then
            _bit_and[gid] = math.modf(value)
        else
            _bit_and[gid] = ba & math.modf(value)
        end
    end
    function self.final(gid)
        local val = _bit_and[gid]
        if val then
            return val
        else
            return 0
        end
    end
    return self
end

-- AGG define section for BIT_OR
function BIT_OR()
    local self = {}
    local _bit_or = {}
    function self.state(gid, value)
        if value == nil or type(value) ~= 'number' then return end
        local bo = _bit_or[gid]
        if not bo then
            _bit_or[gid] = math.modf(value)
        else
            _bit_or[gid] = bo | math.modf(value)
        end
    end
    function self.final(gid)
        local val = _bit_or[gid]
        if val then
            return val
        else
            return 0
        end
    end
    return self
end

-- AGG define section for BIT_XOR
function BIT_XOR()
    local self = {}
    local _bit_xor = {}
    function self.state(gid, value)
        if value == nil or type(value) ~= 'number' then return end
        local bx = _bit_xor[gid]
        if not bx then
            _bit_xor[gid] = math.modf(value)
        else
            _bit_xor[gid] = bx ~ math.modf(value)
        end
    end
    function self.final(gid)
        local val = _bit_xor[gid]
        if val then 
            return val
        else
            return 0
        end
    end
    return self
end
