class = require 'pl.class'
M = require 'pl.Map'
S = require 'pl.Set'
List = require 'pl.List'

asserteq = require 'pl.test' . asserteq
asserteq2 = require 'pl.test' . asserteq2
MultiMap = require 'pl.MultiMap'
OrderedMap = require 'pl.OrderedMap'

s1 = S{1,2}
s2 = S{1,2}
-- equality
asserteq(s1,s2)
-- union
asserteq(S{1,2} + S{2,3},  S{1,2,3})
asserteq(S{1,2} + 3,   S{1,2,3})
-- intersection
asserteq(S{1,2} * S{2,3},   S{2})
-- difference
fruit = S{'apple','banana','orange','apricots'}
tropical = S{'banana','orange'}

asserteq(fruit - tropical,  S{'apple','apricots'})
asserteq(tropical - 'orange', S{'banana'})

-- symmetric_difference
asserteq(S{1,2} ^ S{2,3}, S{1,3})
-- tostring - illustrative, because these assertions may or may not work,
-- due to no ordering in set elements
--asserteq(tostring(S{1,2}),'[1,2]')
--asserteq(tostring(S{1,S{2,3}}),'[1,[2,3]]')

s3 = S()
asserteq(S.isempty(s3),true)

s4 = S{1,2,3}

-- subsets/supersets
asserteq(s4 > s1,true)

S.set(s3,'one',true)
s3.two = true
asserteq(s3,S{'one','two'})

m = M{one=1,two=2}
asserteq(m,M{one=1,two=2})
m:update {three=3,four=4}
asserteq(m,M{one=1,two=2,three=3,four=4})

class.Animal()

function Animal:_init(name)
    self.name = name
end

function Animal:__tostring()
  return self.name..': '..self:speak()
end

class.Dog(Animal)

function Dog:speak()
  return 'bark'
end

class.Cat(Animal)

function Cat:_init(name,breed)
    self:super(name)  -- must init base!
    self.breed = breed
end

function Cat:speak()
  return 'meow'
end

Lion = class(Cat)

function Lion:speak()
  return 'roar'
end

fido = Dog('Fido')
felix = Cat('Felix','Tabby')
leo = Lion('Leo','African')

asserteq(tostring(fido),'Fido: bark')
asserteq(tostring(felix),'Felix: meow')
asserteq(tostring(leo),'Leo: roar')

assert(Dog:class_of(fido))
assert(fido:is_a(Dog))

assert(leo:is_a(Animal))

m = MultiMap()
m:set('john',1)
m:set('jane',3)
m:set('john',2)

ms = MultiMap{john={1,2},jane={3}}

asserteq(m,ms)

m = OrderedMap()
m:set('one',1)
m:set('two',2)
m:set('three',3)

asserteq(m:values(),List{1,2,3})

-- usually exercized like this:
--for k,v in m:iter() do print(k,v) end

fn = m:iter()
asserteq2 ('one',1,fn())
asserteq2 ('two',2,fn())
asserteq2 ('three',3,fn())

-- Keys overriding methods can be used.
m:set('set', 4)
asserteq(m:values(),List{1,2,3,4})

o1 = OrderedMap  {{z=2},{beta=1},{name='fred'}}
asserteq(tostring(o1),'{z=2,beta=1,name="fred"}')

-- order of keys is not preserved here!
o2 = OrderedMap   {z=4,beta=1.1,name='alice',extra='dolly'}

o1:update(o2)
asserteq(tostring(o1),'{z=4,beta=1.1,name="alice",extra="dolly"}')

o1:set('beta',nil)
asserteq(o1,OrderedMap{{z=4},{name='alice'},{extra='dolly'}})

o3 = OrderedMap()
o3:set('dog',10)
o3:set('cat',20)
o3:set('mouse',30)

asserteq(o3:keys(),{'dog','cat','mouse'})

o3:set('dog',nil)

asserteq(o3:keys(),{'cat','mouse'})

-- Vadim found a problem when clearing a key which did not exist already.
-- The keys list would then contain the key, although the map would not
o3:set('lizard',nil)

asserteq(o3:keys(),{'cat','mouse'})
asserteq(o3:values(), {20,30})
asserteq(tostring(o3),'{cat=20,mouse=30}')

-- copy constructor
o4 = OrderedMap(o3)

asserteq(o4,o3)

-- constructor throws an error if the argument is bad
-- (errors same as OrderedMap:update)
asserteq(false,pcall(function()
    m = OrderedMap('string')
end))

---- changing order of key/value pairs ----

o3 = OrderedMap{{cat=20},{mouse=30}}

o3:insert(1,'bird',5) -- adds key/value before specified position
o3:insert(1,'mouse') -- moves key keeping old value
asserteq(o3:keys(),{'mouse','bird','cat'})
asserteq(tostring(o3),'{mouse=30,bird=5,cat=20}')
o3:insert(2,'cat',21) -- moves key and sets new value
asserteq(tostring(o3),'{mouse=30,cat=21,bird=5}')
-- if you don't specify a value for an unknown key, nothing happens to the map
o3:insert(3,'alligator')
asserteq(tostring(o3),'{mouse=30,cat=21,bird=5}')

---- short-cut notation

o5 = OrderedMap()
o5.alpha = 1
o5.beta = 2
o5.gamma = 3

asserteq(o5,OrderedMap{{alpha=1},{beta=2},{gamma=3}})

o5.alpha = 10
o5.beta = 20
o5.gamma = 30
o5.delta = 40
o5.checked = false

asserteq(o5,OrderedMap{{alpha=10},{beta=20},{gamma=30},{delta=40},{checked=false}})





