local function O(O)local z={PositionX=-10.33,PositionY=20.33;PositionZ=.33,BodyRotationX=120.33,BodyRotationY=-120.33,BodyRotationZ=-140.33;LookRotationX=-100.33;LookRotationY=-200.33;LookRotationZ=-50.33,Ammo=27,Health=80;Armor=90;WeaponID=5,Nick="\074\111\104\110\068\111\101";UserId=7.6576725225576e+19,Alive=true}local b={"\080\111\115\105\116\105\111\110\088","\080\111\115\105\116\105\111\110\089","\080\111\115\105\116\105\111\110\090","\066\111\100\121\082\111\116\097\116\105\111\110\088";"\066\111\100\121\082\111\116\097\116\105\111\110\089","\066\111\100\121\082\111\116\097\116\105\111\110\090";"\076\111\111\107\082\111\116\097\116\105\111\110\088";"\076\111\111\107\082\111\116\097\116\105\111\110\089","\076\111\111\107\082\111\116\097\116\105\111\110\090","\065\109\109\111","\072\101\097\108\116\104";"\065\114\109\111\114";"\087\101\097\112\111\110\073\068","\065\108\105\118\101";"\078\105\099\107";"\085\115\101\114\073\100"}local i={}for O,z in ipairs(b)do i[z]=O end local function X(O,z)for b,i in pairs(O)do if O[b]~=z[b]then print("\118\097\108\117\101\061",O[b],"\044 \111\116\104\101\114\061",z[b])error("\073\110\118\097\108\105\100\058 \097\116\116\114\105\098\117\116\101 "..b)end end for b,i in pairs(z)do if O[b]~=z[b]then print("\118\097\108\117\101\061",O[b],"\044 \111\116\104\101\114\061",z[b])error("\073\110\118\097\108\105\100\058 \097\116\116\114\105\098\117\116\101 "..b)end end end local function y(O,z)for O,b in pairs(O)do if z[O]~=b then print("\118\097\108\117\101\061",b,"\044 \111\116\104\101\114\061",z[O])error("\073\110\118\097\108\105\100\058 \097\116\116\114\105\098\117\116\101 "..O)end end end local function q(O,z)if table.getn(z)~=table.getn(b)+2 then error("\069\110\099\111\100\101 \108\101\110\103\116\104 \105\110\118\097\108\105\100")end local X=0 for b,y in ipairs(b)do local q=i[y]-1 X=bit32.bor(X,bit32.lshift(1,q))if O[y]~=z[b+2]then print("\111\098\106\101\099\116\086\097\108\117\101\061",O[y],"\044 \101\110\099\111\100\101\100\086\097\108\117\101\061",z[b+2])error("\073\110\118\097\108\105\100\058 \097\116\116\114\105\098\117\116\101 "..y)end end if z[1]~=true then print("\101\120\112\101\099\116\101\100\061",true,"\044 \118\097\108\117\101\061",z[1])error("\073\110\118\097\108\105\100 \083\105\103\110\097\116\117\114\101")end if z[2]~=X then print("\101\120\112\101\099\116\101\100\077\097\115\107\061",X,"\044 \101\110\099\111\100\101\077\097\115\107\061",z[2])error("\073\110\118\097\108\105\100 \066\073\084\077\065\083\075")end end local function M(O,z,b,X)if table.getn(z)~=table.getn(X)+2 then error("\068\101\108\116\097 \108\101\110\103\116\104 \105\110\118\097\108\105\100")end local y=0 local q=0 for O,z in ipairs(b)do local b=i[z]-1 y=bit32.bor(y,bit32.lshift(1,b))end for b,X in ipairs(X)do local y=i[X]q=bit32.bor(q,bit32.lshift(1,y-1))if O[X]==z[b+3]then print("\111\098\106\101\099\116\086\097\108\117\101\061",O[X],"\044 \100\101\108\116\097\086\097\108\117\101\061",z[b+3])error("\073\110\118\097\108\105\100\044 \086\097\108\117\101 \109\117\115\116 \098\101 \100\105\102\102\101\114\101\110\116\058 \097\116\116\114\105\098\117\116\101 "..X)end end if z[1]~=y then print("\101\120\112\101\099\116\101\100\077\097\115\107\061",y,"\044 \100\101\108\116\097\077\097\115\107\061",z[2])error("\073\110\118\097\108\105\100 \066\073\084\077\065\083\075")end if z[2]~=q then print("\101\120\112\101\099\116\101\100\077\097\115\107\061",q,"\044 \100\101\108\116\097\077\097\115\107\061",z[3])error("\073\110\118\097\108\105\100 \066\073\084\077\065\083\075")end end local h=O.Encode(z)q(z,h)local o=O.Decode(h)y(z,o)local c={PositionX=736.34,PositionY=o.PositionY;PositionZ=o.PositionZ;BodyRotationY=o.BodyRotationY,BodyRotationZ=o.BodyRotationZ;LookRotationX=o.LookRotationX;LookRotationY=736.298362;Ammo=o.Ammo;Armor=o.Armor;WeaponID=o.WeaponID,UserId=o.UserId,Alive=false}local B=O.Diff(o,c)M(o,B,{"\066\111\100\121\082\111\116\097\116\105\111\110\088","\076\111\111\107\082\111\116\097\116\105\111\110\090";"\072\101\097\108\116\104","\078\105\099\107"},{"\080\111\115\105\116\105\111\110\088";"\076\111\111\107\082\111\116\097\116\105\111\110\089";"\065\108\105\118\101"})local e=O.Patch(o,B)X(c,e)local V={PositionX=o.PositionX;PositionY=e.PositionY,PositionZ=e.PositionZ;BodyRotationX=o.BodyRotationX,BodyRotationY=e.BodyRotationY;BodyRotationZ=e.BodyRotationZ,LookRotationX=e.LookRotationX,LookRotationY=o.LookRotationY,LookRotationZ=o.LookRotationZ,Ammo=e.Ammo;Health=o.Health,Armor=e.Armor,WeaponID=e.WeaponID;Nick=o.Nick;UserId=o.UserId,Alive=o.Alive}local l=O.Diff(e,V)M(e,l,{},{"\066\111\100\121\082\111\116\097\116\105\111\110\088","\076\111\111\107\082\111\116\097\116\105\111\110\090";"\072\101\097\108\116\104","\078\105\099\107","\080\111\115\105\116\105\111\110\088";"\076\111\111\107\082\111\116\097\116\105\111\110\089";"\065\108\105\118\101"})local j=O.Patch(e,l)X(j,V)X(j,z)X(V,z)print("\085\110\105\116 \116\101\115\116\115 \115\117\099\099\101\115\115\102\117\108\108\121 \099\111\109\112\108\101\116\101\100\033")end return O

local function RuntTests(Contract)
   local Seed = {
      PositionX      = -10.33,
      PositionY      = 20.33,
      PositionZ      = 0.33,
      BodyRotationX  = 120.33,
      BodyRotationY  = -120.33,
      BodyRotationZ  = -140.33,
      LookRotationX  = -100.33,
      LookRotationY  = -200.33,
      LookRotationZ  = -50.33,
      Ammo           = 27,
      Health         = 80,
      Armor          = 90,
      WeaponID       = 5,
      Nick           = 'JohnDoe',
      UserId         = 76576725225576266762,
      Alive          = true
   }

   local ATTRS = {
      'PositionX',
      'PositionY',
      'PositionZ',
      'BodyRotationX',
      'BodyRotationY',
      'BodyRotationZ',
      'LookRotationX',
      'LookRotationY',
      'LookRotationZ',
      'Ammo',
      'Health',
      'Armor',
      'WeaponID',
      'Alive',
      'Nick',
      'UserId'
   }

   local ATTRS_IDX = {}
   for index, key in ipairs(ATTRS) do
      ATTRS_IDX[key] = index
   end

   local function testEQL(a, b)
      for attr, value in pairs(a) do
         if a[attr] ~= b[attr] then
            print('value=', a[attr], ', other=', b[attr])
            error('Invalid: attribute '..attr)
         end
      end

      for attr, value in pairs(b) do
         if a[attr] ~= b[attr] then
            print('value=', a[attr], ', other=', b[attr])
            error('Invalid: attribute '..attr)
         end
      end
   end

   local function testDecoded(object, decoded)
      for attr, value in pairs(object) do
         if decoded[attr] ~= value then
            print('value=', value, ', other=', decoded[attr])
            error('Invalid: attribute '..attr)
         end
      end
   end

   --[[
      Verifying the generated encode is correct, in the format
      {TRUE, BITMASK, VALUE_A, VALUE_B, VALUE_N ...}
   ]]
   local function testEncoded(object, encoded)
      if table.getn(encoded) ~= table.getn(ATTRS) + 2 then
         error('Encode length invalid')
      end

      local encodeMask = 0x0

      for index, key in ipairs(ATTRS) do
         local attrIdx = ATTRS_IDX[key]-1
         encodeMask = bit32.bor(encodeMask, bit32.lshift(1, attrIdx))

         if object[key] ~= encoded[index+2] then
            print('objectValue=', object[key], ', encodedValue=', encoded[index+2])
            error('Invalid: attribute '..key)
         end
      end

      if encoded[1] ~= true then
         print('expected=', true, ', value=', encoded[1] )
         error('Invalid Signature')
      end

      if encoded[2] ~= encodeMask then
         print('expectedMask=', encodeMask, ', encodeMask=', encoded[2] )
         error('Invalid BITMASK')
      end
   end

   --[[
      Checks whether the generated delta is correct, in the format
      {BITMASK_DEL, BITMASK_MOD, VALUE_A, VALUE_B, VALUE_N...}
   ]]
   local function testDelta(object, delta, removedKeys, changedKeys)

      if table.getn(delta) ~= table.getn(changedKeys) + 2 then
         error('Delta length invalid')
      end

      local removedMask = 0x0
      local changedMask = 0x0
      for _, key in ipairs(removedKeys) do
         local attrIdx = ATTRS_IDX[key]-1
         removedMask = bit32.bor(removedMask, bit32.lshift(1, attrIdx))
      end

      for index, key in ipairs(changedKeys) do
         local attrIdx = ATTRS_IDX[key]
         changedMask = bit32.bor(changedMask, bit32.lshift(1, attrIdx -1))
         if object[key] == delta[index+3] then
            print('objectValue=', object[key], ', deltaValue=', delta[index+3])
            error('Invalid, Value must be different: attribute '..key)
         end
      end

      if delta[1] ~= removedMask then
         print('expectedMask=', removedMask, ', deltaMask=', delta[2] )
         error('Invalid BITMASK')
      end

      if delta[2] ~= changedMask then
         print('expectedMask=', changedMask, ', deltaMask=', delta[3] )
         error('Invalid BITMASK')
      end
   end

   -- Checks Encoded
   local Encoded = Contract.Encode(Seed)
   testEncoded(Seed, Encoded)

   -- Checks Decoded
   local Decoded = Contract.Decode(Encoded)
   testDecoded(Seed, Decoded)

   -- Checks the Diff
   local Changed = {
      PositionX      = 736.34,                     -- CHANGED
      PositionY      = Decoded.PositionY,
      PositionZ      = Decoded.PositionZ,
      -- BodyRotationX  = Decoded.BodyRotationX,   -- REMOVED
      BodyRotationY  = Decoded.BodyRotationY,
      BodyRotationZ  = Decoded.BodyRotationZ,
      LookRotationX  = Decoded.LookRotationX,
      LookRotationY  = 736.298362,                 -- CHANGED
      --LookRotationZ  = Decoded.LookRotationZ,    -- REMOVED
      Ammo           = Decoded.Ammo,
      --Health         = Decoded.Health,           -- REMOVED
      Armor          = Decoded.Armor,
      WeaponID       = Decoded.WeaponID,
      --Nick           = Decoded.Nick,             -- REMOVED
      UserId         = Decoded.UserId,
      Alive          = false                       -- CHANGED
   }

   local Delta = Contract.Diff(Decoded, Changed)
   testDelta(Decoded, Delta, {'BodyRotationX', 'LookRotationZ', 'Health', 'Nick'}, {'PositionX', 'LookRotationY', 'Alive'})

   -- Checks the Patch
   local PatchedUp = Contract.Patch(Decoded, Delta)
   testEQL(Changed, PatchedUp)

   -- Should revert to the original value
   local Recovered = {
      PositionX      = Decoded.PositionX,          -- Recovered
      PositionY      = PatchedUp.PositionY,
      PositionZ      = PatchedUp.PositionZ,
      BodyRotationX  = Decoded.BodyRotationX,      -- Recovered
      BodyRotationY  = PatchedUp.BodyRotationY,
      BodyRotationZ  = PatchedUp.BodyRotationZ,
      LookRotationX  = PatchedUp.LookRotationX,
      LookRotationY  = Decoded.LookRotationY,      -- Recovered
      LookRotationZ  = Decoded.LookRotationZ,      -- Recovered
      Ammo           = PatchedUp.Ammo,
      Health         = Decoded.Health,             -- Recovered
      Armor          = PatchedUp.Armor,
      WeaponID       = PatchedUp.WeaponID,
      Nick           = Decoded.Nick,               -- Recovered
      UserId         = Decoded.UserId,
      Alive          = Decoded.Alive               -- Recovered
   }

   local DeltaR = Contract.Diff(PatchedUp, Recovered)
   testDelta(PatchedUp, DeltaR, {}, {'BodyRotationX', 'LookRotationZ', 'Health', 'Nick', 'PositionX', 'LookRotationY', 'Alive'})

   local PatchedUpR = Contract.Patch(PatchedUp, DeltaR)

   -- in the end, all objects must be equal
   testEQL(PatchedUpR, Recovered)
   testEQL(PatchedUpR, Seed)
   testEQL(Recovered, Seed)

   print('Unit tests successfully completed!')
end

return RuntTests