local f=require("\112\108\046\116\097\098\108\101\120")local A=require("\112\108\046\117\116\105\108\115")local Z=require("\112\108\046\076\105\115\116")local C,O,b=f.index_by,table.sort,table.concat local I=require("\112\108\046\099\108\097\115\115")local h=require("\112\108\046\077\097\112")local L=I(h)L._name="\079\114\100\101\114\101\100\077\097\112"local W=rawset function L._init(A,f)W(A,"\095\107\101\121\115",Z())if f then local Z,C=A:update(f)if not Z then error(C,2)end end end local G,o=A.assert_arg,A.raise function L.update(A,f)G(1,f,"\116\097\098\108\101")if L:class_of(f)then for f,Z in f:iter()do A:set(f,Z)end elseif#f>0 then if type(f[1])=="\116\097\098\108\101"then for f,Z in ipairs(f)do local C,O=next(Z)if not C then return o("\101\109\112\116\121 \112\097\105\114 \105\110\105\116\105\097\108\105\122\097\116\105\111\110 \116\097\098\108\101")end A:set(C,O)end else return o("\099\097\110\110\111\116 \117\115\101 \097\110 \097\114\114\097\121 \116\111 \105\110\105\116\105\097\108\105\122\101 \097\110 \079\114\100\101\114\101\100\077\097\112")end else for f,Z in pairs(f)do A:set(f,Z)end end return A end function L.set(Z,f,A)if rawget(Z,f)==nil and A~=nil then Z._keys:append(f)W(Z,f,A)else if A==nil then Z._keys:remove_value(f)W(Z,f,nil)else Z[f]=A end end return Z end L.__newindex=L.set function L.insert(C,f,A,Z)local O=C[A]Z=Z or O if O then C._keys:remove_value(A)end if Z then C._keys:insert(f,A)W(C,A,Z)end return C end function L.keys(f)return f._keys end function L.values(f)return Z(C(f,f._keys))end function L.sort(A,f)O(A._keys,f)return A end function L.iter(f)local A=0 local Z=f._keys local C return function()A=A+1 if A>#Z then return nil end C=Z[A]return C,f[C]end end L.__pairs=L.iter function L.__tostring(f)local A={}for Z,C in ipairs(f._keys)do local O=f[C]local b=tostring(O)if type(O)~="\110\117\109\098\101\114"then b="\""..(b.."\"")end A[Z]=tostring(C)..("\061"..b)end return"\123"..(b(A,"\044").."\125")end return L
--- OrderedMap, a map which preserves ordering.
--
-- Derived from `pl.Map`.
--
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.class`, `pl.List`, `pl.Map`
-- @classmod pl.OrderedMap

local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local List = require 'pl.List'
local index_by,tsort,concat = tablex.index_by,table.sort,table.concat

local class = require 'pl.class'
local Map = require 'pl.Map'

local OrderedMap = class(Map)
OrderedMap._name = 'OrderedMap'

local rawset = rawset

--- construct an OrderedMap.
-- Will throw an error if the argument is bad.
-- @param t optional initialization table, same as for @{OrderedMap:update}
function OrderedMap:_init (t)
    rawset(self,'_keys',List())
    if t then
        local map,err = self:update(t)
        if not map then error(err,2) end
    end
end

local assert_arg,raise = utils.assert_arg,utils.raise

--- update an OrderedMap using a table.
-- If the table is itself an OrderedMap, then its entries will be appended.
-- if it s a table of the form `{{key1=val1},{key2=val2},...}` these will be appended.
--
-- Otherwise, it is assumed to be a map-like table, and order of extra entries is arbitrary.
-- @tab t a table.
-- @return the map, or nil in case of error
-- @return the error message
function OrderedMap:update (t)
    assert_arg(1,t,'table')
    if OrderedMap:class_of(t) then
       for k,v in t:iter() do
           self:set(k,v)
       end
    elseif #t > 0 then -- an array must contain {key=val} tables
       if type(t[1]) == 'table' then
           for _,pair in ipairs(t) do
               local key,value = next(pair)
               if not key then return raise 'empty pair initialization table' end
               self:set(key,value)
           end
       else
           return raise 'cannot use an array to initialize an OrderedMap'
       end
    else
       for k,v in pairs(t) do
           self:set(k,v)
       end
    end
   return self
end

--- set the key's value.   This key will be appended at the end of the map.
--
-- If the value is nil, then the key is removed.
-- @param key the key
-- @param val the value
-- @return the map
function OrderedMap:set (key,val)
    if rawget(self, key) == nil and val ~= nil then -- new key
        self._keys:append(key) -- we keep in order
        rawset(self,key,val)  -- don't want to provoke __newindex!
    else -- existing key-value pair
        if val == nil then
            self._keys:remove_value(key)
            rawset(self,key,nil)
        else
            self[key] = val
        end
    end
    return self
end

OrderedMap.__newindex = OrderedMap.set

--- insert a key/value pair before a given position.
-- Note: if the map already contains the key, then this effectively
-- moves the item to the new position by first removing at the old position.
-- Has no effect if the key does not exist and val is nil
-- @int pos a position starting at 1
-- @param key the key
-- @param val the value; if nil use the old value
function OrderedMap:insert (pos,key,val)
    local oldval = self[key]
    val = val or oldval
    if oldval then
        self._keys:remove_value(key)
    end
    if val then
        self._keys:insert(pos,key)
        rawset(self,key,val)
    end
    return self
end

--- return the keys in order.
-- (Not a copy!)
-- @return List
function OrderedMap:keys ()
    return self._keys
end

--- return the values in order.
-- this is relatively expensive.
-- @return List
function OrderedMap:values ()
    return List(index_by(self,self._keys))
end

--- sort the keys.
-- @func cmp a comparison function as for @{table.sort}
-- @return the map
function OrderedMap:sort (cmp)
    tsort(self._keys,cmp)
    return self
end

--- iterate over key-value pairs in order.
function OrderedMap:iter ()
    local i = 0
    local keys = self._keys
    local idx
    return function()
        i = i + 1
        if i > #keys then return nil end
        idx = keys[i]
        return idx,self[idx]
    end
end

--- iterate over an ordered map (5.2).
-- @within metamethods
-- @function OrderedMap:__pairs
OrderedMap.__pairs = OrderedMap.iter

--- string representation of an ordered map.
-- @within metamethods
function OrderedMap:__tostring ()
    local res = {}
    for i,v in ipairs(self._keys) do
        local val = self[v]
        local vs = tostring(val)
        if type(val) ~= 'number' then
            vs = '"'..vs..'"'
        end
        res[i] = tostring(v)..'='..vs
    end
    return '{'..concat(res,',')..'}'
end

return OrderedMap



