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=A.stdmt local C=f.deepcompare local O=(require("\112\108\046\112\114\101\116\116\121")).write local b=Z.Map local I=Z.Set local h=require("\112\108\046\099\108\097\115\115")h(nil,nil,b)function b._init(A,f)local Z=getmetatable(f)if Z==I or Z==b then A:update(f)else return f end end local function L(f)return setmetatable(f,require("\112\108\046\076\105\115\116"))end b.keys=f.keys b.values=f.values function b.iter(f)return pairs(f)end function b.items(A)local Z=L(f.pairmap(function(f,A)return L({f;A})end,A))Z:sort(function(f,A)return f[1]<A[1]end)return Z end function b.setdefault(Z,f,A)local C=Z[f]if C~=nil then return C end Z:set(f,A)return A end b.len=f.size function b.set(Z,f,A)Z[f]=A end function b.get(A,f)return rawget(A,f)end local W=f.index_by function b.getvalues(A,f)return L(W(A,f))end b.update=f.update function b.__eq(A,f)return C(A,f,true)end function b.__tostring(f)return O(f,"")end return b
--- A Map class.
--
--    > Map = require 'pl.Map'
--    > m = Map{one=1,two=2}
--    > m:update {three=3,four=4,two=20}
--    > = m == M{one=1,two=20,three=3,four=4}
--    true
--
-- Dependencies: `pl.utils`, `pl.class`, `pl.tablex`, `pl.pretty`
-- @classmod pl.Map

local tablex = require 'pl.tablex'
local utils = require 'pl.utils'
local stdmt = utils.stdmt
local deepcompare = tablex.deepcompare

local pretty_write = require 'pl.pretty' . write
local Map = stdmt.Map
local Set = stdmt.Set

local class = require 'pl.class'

-- the Map class ---------------------
class(nil,nil,Map)

function Map:_init (t)
    local mt = getmetatable(t)
    if mt == Set or mt == Map then
        self:update(t)
    else
        return t -- otherwise assumed to be a map-like table
    end
end


local function makelist(t)
    return setmetatable(t, require('pl.List'))
end

--- list of keys.
Map.keys = tablex.keys

--- list of values.
Map.values = tablex.values

--- return an iterator over all key-value pairs.
function Map:iter ()
    return pairs(self)
end

--- return a List of all key-value pairs, sorted by the keys.
function Map:items()
    local ls = makelist(tablex.pairmap (function (k,v) return makelist {k,v} end, self))
    ls:sort(function(t1,t2) return t1[1] < t2[1] end)
    return ls
end

--- set a value in the map if it doesn't exist yet.
-- @param key the key
-- @param default value to set
-- @return the value stored in the map (existing value, or the new value)
function Map:setdefault(key, default)
    local val = self[key]
    if val ~= nil then
        return val
    end
    self:set(key,default)
   return default
end

--- size of map.
-- note: this is a relatively expensive operation!
-- @class function
-- @name Map:len
Map.len = tablex.size

--- put a value into the map.
-- This will remove the key if the value is `nil`
-- @param key the key
-- @param val the value
function Map:set (key,val)
    self[key] = val
end

--- get a value from the map.
-- @param key the key
-- @return the value, or nil if not found.
function Map:get (key)
    return rawget(self,key)
end

local index_by = tablex.index_by

--- get a list of values indexed by a list of keys.
-- @param keys a list-like table of keys
-- @return a new list
function Map:getvalues (keys)
    return makelist(index_by(self,keys))
end

--- update the map using key/value pairs from another table.
-- @tab table
-- @function Map:update
Map.update = tablex.update

--- equality between maps.
-- @within metamethods
-- @tparam Map m another map.
function Map:__eq (m)
    -- note we explicitly ask deepcompare _not_ to use __eq!
    return deepcompare(self,m,true)
end

--- string representation of a map.
-- @within metamethods
function Map:__tostring ()
    return pretty_write(self,'')
end

return Map
