text:
--[[
  __  __           _            _                 _  __                         _____
 |  \/  |         | |          | |               | |/ /                        |  __ \
 | \  / | __ _  __| | ___      | |__  _   _      | ' / __ _ ___ _ __   ___ _ __| |__) |
 | |\/| |/ _` |/ _` |/ _ \     | '_ \| | | |     |  < / _` / __| '_ \ / _ \ '__|  _  /
 | |  | | (_| | (_| |  __/     | |_) | |_| |     | . \ (_| \__ \ |_) |  __/ |  | | \ \
 |_|  |_|\__,_|\__,_|\___|     |_.__/ \__, |     |_|\_\__,_|___/ .__/ \___|_|  |_|  \_\
                                       __/ |                   | |
                                      |___/                    |_|

  Author: Kasper Rasmussen
  Steam: https://steamcommunity.com/id/kasperrasmussen
]]
local Tunnel = module("vrp", "lib/Tunnel")
local Proxy = module("vrp", "lib/Proxy")

vRP = Proxy.getInterface("vRP")
vRPclient = Tunnel.getInterface("vRP", "kasperr_inventory")

RegisterServerEvent("kasperr_inventory:openGui")
AddEventHandler(
    "kasperr_inventory:openGui",
    function()
        local user_id = vRP.getUserId({source})
        local player = vRP.getUserSource({user_id})
        local data = vRP.getUserDataTable({user_id})
        if data and data.inventory then
            local inventory = {}
            for data_k, data_v in pairs(data.inventory) do
                for items_k, items_v in pairs(items) do
                    if data_k == items_k then
                        local item_name = vRP.getItemName({data_k})
                        if item_name then
                            table.insert(
                                inventory,
                                {
                                    name = item_name,
                                    amount = data_v.amount,
                                    idname = data_k,
                                    icon = items_v[3]
                                }
                            )
                        end
                    end
                end
            end
            local weight = vRP.getInventoryWeight({user_id})
            local maxWeight = vRP.getInventoryMaxWeight({user_id})
            TriggerClientEvent("kasperr_inventory:updateInventory", source, inventory, weight, maxWeight)
        end
    end
)

RegisterServerEvent("kasperr_inventory:useItem")
AddEventHandler(
    "kasperr_inventory:useItem",
    function(args)
        local data = args[1]
        local user_id = vRP.getUserId({source})
        local player = vRP.getUserSource({user_id})

        if data.idname then
            for k, v in pairs(items) do
                if data.idname == k then
                    useItem(user_id, player, k, v[1], v[2], data.amount)
                end
            end
        end
    end
)

RegisterServerEvent("kasperr_inventory:dropItem")
AddEventHandler(
    "kasperr_inventory:dropItem",
    function(data)
        local user_id = vRP.getUserId({source})
        local player = vRP.getUserSource({user_id})
        local amount = parseInt(data.amount)
        if vRP.tryGetInventoryItem({user_id, data.idname, amount, false}) then
            TriggerClientEvent("kasperr_inventory:closeGui", player)
            vRPclient.playAnim(player, {true, {{"pickup_object", "pickup_low", 1}}, false})
        else
            TriggerClientEvent(
                "kasperr_inventory:UINotification",
                player,
                "warning",
                Config.Language.WarningTitle,
                Config.Language.Error
            )
        end
    end
)

function split(str, sep)
    local array = {}
    local reg = string.format("([^%s]+)", sep)
    for mem in string.gmatch(str, reg) do
        table.insert(array, mem)
    end
    return array
end

function useItem(user_id, player, idname, type, variation, amount)
    if type == "drink" or type == "food" or type == "heal" or type == "weapon" or type == "ammo" then
        if type == "drink" then
            if vRP.tryGetInventoryItem({user_id, idname, tonumber(amount), false}) then
                TriggerClientEvent("kasperr_inventory:objectForAnimation", player, "prop_ld_flow_bottle")
                play_drink(player)
                for i = 1, tonumber(amount) do
                    vRP.varyThirst({user_id, variation})
                end
            end
        end
        if type == "food" then
            if vRP.tryGetInventoryItem({user_id, idname, tonumber(amount), false}) then
                TriggerClientEvent("kasperr_inventory:objectForAnimation", player, "prop_cs_burger_01")
                play_eat(player)
                for i = 1, tonumber(amount) do
                    vRP.varyHunger({user_id, variation})
                end
            end
        end
        if type == "heal" then
            if vRP.tryGetInventoryItem({user_id, idname, tonumber(amount), false}) then
                for i = 1, tonumber(amount) do
                    vRPclient.varyHealth(player, {25})
                end
            end
        end
        if type == "weapon" then
            if vRP.tryGetInventoryItem({user_id, idname, tonumber(amount), false}) then
                local fullidname = split(idname, "|")
                vRPclient.giveWeapons(
                    player,
                    {
                        {
                            [fullidname[2]] = {ammo = 0}
                        },
                        false
                    }
                )
            end
        end
        if type == "ammo" then
            local fullidname = split(idname, "|")
            local exists = false
            vRPclient.getWeapons(
                player,
                {},
                function(weapons)
                    for k, v in pairs(weapons) do
                        if k == fullidname[2] then
                            exists = true
                        end
                    end
                    if exists == true then
                        if vRP.tryGetInventoryItem({user_id, idname, tonumber(amount), false}) then
                            vRPclient.giveWeapons(
                                player,
                                {
                                    {
                                        [fullidname[2]] = {ammo = tonumber(amount)}
                                    },
                                    false
                                }
                            )
                        end
                    else
                        TriggerClientEvent(
                            "kasperr_inventory:UINotification",
                            player,
                            "warning",
                            Config.Language.WarningTitle,
                            Config.Language.WeaponNotEquipped
                        )
                    end
                end
            )
        end
    end
    if type == "none" then
        TriggerClientEvent(
            "kasperr_inventory:UINotification",
            player,
            "warning",
            Config.Language.WarningTitle,
            Config.Language.CannotBeUsed
        )
    end
end

RegisterServerEvent("kasperr_inventory:giveItem")
AddEventHandler(
    "kasperr_inventory:giveItem",
    function(data)
        local user_id = vRP.getUserId({source})
        local player = vRP.getUserSource({user_id})
        if user_id ~= nil then
            vRPclient.getNearestPlayer(
                player,
                {10},
                function(nplayer)
                    if nplayer ~= nil then
                        local nuser_id = vRP.getUserId({nplayer})
                        if nuser_id ~= nil then
                            local amount = parseInt(data.amount)
                            local new_weight =
                                vRP.getInventoryWeight({nuser_id}) + vRP.getItemWeight({data.idname}) * amount
                            if new_weight <= vRP.getInventoryMaxWeight({nuser_id}) then
                                if vRP.tryGetInventoryItem({user_id, data.idname, amount, false}) then
                                    vRP.giveInventoryItem({nuser_id, data.idname, amount, true})

                                    vRPclient.playAnim(player, {true, {{"mp_common", "givetake1_a", 1}}, false})
                                    vRPclient.playAnim(nplayer, {true, {{"mp_common", "givetake2_a", 1}}, false})
                                else
                                    vRPclient.notify(player, {Config.Language.Error})
                                end
                            else
                                vRPclient.notify(player, {Config.Language.NotEnoughtSpace})
                            end
                        else
                            vRPclient.notify(player, {Config.Language.NoNearby})
                        end
                    else
                        vRPclient.notify(player, {Config.Language.NoNearby})
                    end
                end
            )
        end
    end
)

function play_eat(player)
    local seq = {
        {"mp_player_inteat@burger", "mp_player_int_eat_burger_enter",1},
        {"mp_player_inteat@burger", "mp_player_int_eat_burger",1},
        {"mp_player_inteat@burger", "mp_player_int_eat_burger_fp",1},
        {"mp_player_inteat@burger", "mp_player_int_eat_exit_burger",1}
    }

    vRPclient.playAnim(player,{true,seq,false})
end

function play_drink(player)
    local seq = {
        {"mp_player_intdrink","intro_bottle",1},
        {"mp_player_intdrink","loop_bottle",1},
        {"mp_player_intdrink","outro_bottle",1}
    }

    vRPclient.playAnim(player,{true,seq,false})
end


text:
require "networking.NetworkedEntity"
require "entities.Actor"
require "Color"

-- Message fields.
local F_X = "x"
local F_Y = "y"
local F_RED = "r"
local F_GREEN = "g"
local F_BLUE = "b"
local F_DIR = "d"
local F_SUCCESS = "s"
local F_UPDATE_TYPE = "i" -- incremental update type

-- Incremental update types
local T_STEP = 1
local T_SPIN = 2

--
-- Class for tying an actor entity to corresponding actors on other instances.
--
NetworkedActor = buildClass(NetworkedEntity)
local Class = NetworkedActor

--
-- Instantiates an Actor and performs necessary setup.
--
function Class.createNewInstanceWithParams(manager, id, entityType, params)
  return Class(manager, id, entityType, params, Actor())
end

--
-- Instantiates an Actor and performs necessary setup.
--
function Class.createNewInstance(manager, id, entityType, ...)
  local x, y, r, g, b = ...
  return Class.createNewInstanceWithParams(manager, id, entityType, {
      [F_X] = x,
      [F_Y] = y,
      [F_RED] = r,
      [F_GREEN] = g,
      [F_BLUE] = b,
  })
end

--
-- Registers this class to be instantiated by the network.
--
Class.registerEntityType(NetworkedEntityType.ACTOR, Class)

function Class:_init(manager, networkedId, entityType, params, actor)
  Class.superclass._init(self, manager, networkedId, entityType, params, actor)
  assertClass(actor, Actor, "actor")
  self:setActorState(params)
end

function Class:startBroadcastingUpdates()
  if self:isBroadcastingUpdates() then return end
  Class.superclass.startBroadcastingUpdates(self)

  -- Register for listener callbacks
  local actor = self:getLocalEntity()
  actor:registerMoveListener(self, self.onActorStep)
  actor:registerSpinListener(self, self.onActorSpin)
end

function Class:stopBroadcastingUpdates()
  if not self:isBroadcastingUpdates() then return end
  local actor = self:getLocalEntity()
  actor:unregisterMoveListener(self, self.onActorStep)
  actor:unregisterSpinListener(self, self.onActorSpin)

  Class.superclass.stopBroadcastingUpdates(self)
end

function Class:getInstantiationParams(params)
  params = Class.superclass.getInstantiationParams(self, params)
  return self:writeActorState(params, "new")
end

function Class:setSynchronizedState(state)
  Class.superclass.setSynchronizedState(self, state)
  self:setActorState(state)
end

--
-- Sets the state of the actor given a params/state object.
--
function Class:setActorState(state)
  self:lock()
  local actor = self:getLocalEntity()
  local x, y = state[F_X], state[F_Y]
  local r, g, b = state[F_RED], state[F_GREEN], state[F_BLUE]
  if x and y then
    actor:setPosition(x, y)
  end
  if r and g and b then
    actor:setColor(Color(r, g, b))
  end
  self:unlock()
end

function Class:getSynchronizedState(state)
  state = Class.superclass.getSynchronizedState(self, state)
  return self:writeActorState(state, "sync")
end

--
-- Outputs the state of the actor to the provided table.
-- Available modes:
-- - 'new': for new Actors
-- - 'sync': for sync updates
--
function Class:writeActorState(state, mode)
  local actor = self:getLocalEntity()
  local x, y = actor:getPosition()
  state[F_X] = x
  state[F_Y] = y

  if mode == "new" then
    local r, g, b = actor:getColor():getRGBA()
    state[F_RED] = r
    state[F_GREEN] = g
    state[F_BLUE] = b
  end

  return state
end

function Class:performIncrementalUpdate(update)
  self:lock()
  local inSync = Class.superclass.performIncrementalUpdate(self, update)

  if inSync then
    local updateType = update[F_UPDATE_TYPE]
    if updateType == T_STEP then
      inSync = self:performStepUpdate(update)
    elseif updateType == T_SPIN then
      inSync = self:performSpinUpdate(update)
    end
  end
  self:unlock()

  return inSync
end

--
-- Handles actors being moved.
--
function Class:performStepUpdate(update)
  self:lock()
  local inSync = true
  local actor = self:getLocalEntity()
  local dir = update[F_DIR]
  local didMove = update[F_SUCCESS]

  -- Make sure actor is located where it's supposed to be.
  local x, y = actor:getPosition()
  if x ~= update[F_X] or y ~= update[F_Y] then
    inSync = false
  end

  -- If the update says the actor has moved, then force movement.
  -- Move the actor
  if inSync then
    inSync = actor:move(dir) == didMove
  end
  self:unlock()

  return inSync
end

--
-- Handles actors emoting a spin
--
function Class:performSpinUpdate(update)
  self:lock()
  local actor = self:getLocalEntity()
  local inSync = actor:spin()
  self:unlock()
  return inSync
end

--
-- Builds incremental update message for an actor stepping.
--
function Class:buildStepUpdate(x, y, dir, success)
  local msg = {}
  msg[F_UPDATE_TYPE] = T_STEP
  msg[F_X] = x
  msg[F_Y] = y
  msg[F_DIR] = dir
  msg[F_SUCCESS] = success
  return msg
end

--
-- Builds incremental update message for an actor spinning.
--
function Class:buildSpinUpdate()
  local msg = {}
  msg[F_UPDATE_TYPE] = T_SPIN
  return msg
end

--
-- Handles actor stepping.
--
function Class:onActorStep(actor, x, y, dir, success)
  if self:isLocked() or self:getLocalEntity() ~= actor then return end
  self:sendIncrementalUpdate(self:buildStepUpdate(x, y, dir, success))
end

--
-- Handles actors spinning.
--
function Class:onActorSpin(actor)
  if self:isLocked() or self:getLocalEntity() ~= actor then return end
  self:sendIncrementalUpdate(self:buildSpinUpdate())
end

domain: lua


text:
-- Natural Selection 2 Competitive Mod
-- Source located at - https://github.com/xToken/CompMod
-- lua\CompMod\Structures\Alien\Veil\client.lua
-- - Dragon

local originalVeilOnUpdate
originalVeilOnUpdate = Class_ReplaceMethod("Veil", "OnUpdate",
	function(self, deltaTime)
		originalVeilOnUpdate(self, deltaTime)
		if self.isTeleporting ~= self.lastisTeleporting then
			-- This isnt good coding, but these are all over the place in vanilla
			if not self.isTeleporting and self.lastisTeleporting then
				-- We are not teleporting, trigger clear, then infest start.
				self:CleanupInfestation()
			end
			self.lastisTeleporting = self.isTeleporting
		end
	end
)


text:
--------------------------------------------------------------------------------
-- ItsyScape/GameDB/Commands/ResourceType.lua
--
-- This file is a part of ItsyScape.
--
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.
--------------------------------------------------------------------------------
local Class = require "ItsyScape.Common.Class"
local Game = require "ItsyScape.GameDB.Commands.Game"
local Pokeable = require "ItsyScape.GameDB.Commands.Pokeable"

-- Constructs a new Mapp.ResourceType in the Brochure.
--
-- Adds a new category to Game.Resource corresponding to whatever name is given
-- to the constructor.
local ResourceType = Class(Pokeable)

function ResourceType:new(name)
	self.name = name

	local game = Game.getGame()
	game:addResourceType(self)
	game.Resource:add(name)
end

function ResourceType:getName()
	return self.name
end

function ResourceType:instantiate(brochure)
	if not self.instance then
		self.instance = brochure:createResourceType(self.name)
	end

	return self.instance
end

return ResourceType


text:
--------------------------------------------------------------------------------
-- map.lua - Defines a map which manages the construction of the hex grid and the path within
--------------------------------------------------------------------------------

require "source/pathfinder"
require "source/utilities/vector"

Map = flower.class()

function Map:init(t)

    -- Copy all data members of the table as member variables
    for k, d in pairs(t) do
        self[k] = d
    end

    -- Try to load the map
    if not self:load() then
        if self.file ~= nil then
            print("Cannot Load Map: " .. self.file)
        else
            print("Cannot Load Map: nil")
        end
    end
end

function Map:load(file)
    self.file = self.file or file
    if not (self.file and io.fileExists(self.file)) then
        return false
    end

    self.map = dofile(self.file)

    self.width = self.map.width or self.width
    self.height = self.map.height or self.height

    self.grid = flower.MapImage(self.texture, self.width,
                                self.height, self.tileWidth,
                                self.tileHeight, self.radius)

    self.grid:setShape(MOAIGridSpace.HEX_SHAPE)
    self.grid:setLayer(self.layer)

    self.selectedImage = flower.SheetImage(self.texture)
    self.selectedImage:setTileSize(self.tileWidth, self.tileHeight)
    self.selectedImage:setLayer(self.layer)

    -- TODO: Fix these numbers
    self.selectedImage:setScl(self.height / self.tileHeight / 1.3, self.height / self.tileHeight / 1.3)
    self.selectedImage:setPiv(self.radius / 2, self.radius / 2)
    self.selectedImage:setColor(0.2, 0.2, 0.2, 1)
    self.selectedImage:setVisible(false)

    if type(self.map.tiles) == "table" then
        for i = 1,self.width do
            for j = 1,self.height do
                self.grid.grid:setTile(i, j, self.map.default_tile)
            end
        end

        for k, data in pairs(self.map.tiles) do
            for j, pos in ipairs(data) do
                self.grid.grid:setTile(pos[1], pos[2], k)
            end
        end
    elseif type(self.map.tiles) == "string" then

        -- Load file from stream
        local fileStream = MOAIFileStream.new()
        if not fileStream:open(self.map.tiles, MOAIFileStream.READ) then
            return false
        end

        self.grid.grid:streamTilesIn(fileStream)
        fileStream:close()

        self.spawnTiles = {}
        self.targetPosition = {}
        for i = 1,self.width do
            for j = 1,self.height do
                local tile = self.grid.grid:getTile(i, j)
                if tile == TILE_TYPES.TARGET then
                    self.targetPosition[1], self.targetPosition[2] = i, j
                elseif tile == TILE_TYPES.SPAWN then
                    table.insert(self.spawnTiles, {i, j})
                end
            end
        end
    else
        return false
    end

    -- TODO: make this a bit more dynamic
    local function validTileCallback(tile)
        return tile == TILE_TYPES.ENEMY or tile == TILE_TYPES.TARGET or tile == TILE_TYPES.SPAWN
    end

    -- Find path in the map
    if self:isPathDynamic() then
        self.path = findPath(self:getMOAIGrid(), vector{self.targetPosition[1], self.targetPosition[2]}, validTileCallback)
    else
        self.path = self.map.paths[1]
        self.targetPosition = self.path[#self.path]
    end

    return true
end

function Map:isTileSelected()
    return self.tileSelected
end

-- TODO: need to center the selected tile
function Map:selectTile(pos)

    local worldPos = self:gridToScreenSpace(pos, MOAIGridSpace.TILE_LEFT_TOP)

    self.selectedImage:setVisible(true)
    self.selectedImage:setIndex(self.grid:getTile(pos[1], pos[2]))
    self.selectedImage:setPos(worldPos[1], worldPos[2])

    self.tileSelected = true
end

function Map:unselectTile()
    self.selectedImage:setVisible(false)
    self.tileSelected = false
end

function Map:randomStartingPosition()
    local startPosition = not self:isPathDynamic() and self.path[1]
    if not startPosition then
        local randomIndex = math.random(1, #self.spawnTiles)
        startPosition = self.spawnTiles[randomIndex]
    end

    return self:gridToScreenSpace(startPosition)
end

function Map:screenToGridSpace(x, y, layer)
    local prop = self:getGrid()

    x, y = layer:wndToWorld(x, y)
    x, y = prop:worldToModel(x, y)

    return vector{self:getMOAIGrid():locToCoord(x, y)}
end

function Map:gridToScreenSpace(pos, alignment)
    alignment = alignment or MOAIGridSpace.TILE_CENTER
    if type(pos) == "table" then
        return vector{self:getMOAIGrid():getTileLoc(pos[1], pos[2], alignment)}
    elseif type(pos) == "number" then
        return self:getMOAIGrid():getTileLoc(pos, 0, alignment)
    end
end

-- Returns true if the path was found using a pathfinder
function Map:isPathDynamic()
    if self.map.paths then
        return false
    end

    return true
end

function Map:clearTile(pos)
    self:setTile(pos, TILE_TYPES.EMPTY)
end

function Map:setTile(pos, index)
    self:getGrid():setTile(pos[1], pos[2], index)
end

function Map:getTile(pos)
    return self:getGrid():getTile(pos[1], pos[2])
end

function Map:getWaveLimit()
    return self.map.waveLimit and self.map.waveLimit or math.huge
end

function Map:getPath()
    return self.path
end

function Map:getWaves()
    return self.map.waves
end

-- TODO: remove these methods to return the grid
function Map:getGrid()
    return self.grid
end

function Map:getMOAIGrid()
    return self:getGrid().grid
end

function Map:setLayer(layer)
    self.layer = layer
    self.grid:setLayer(self.layer)
    self.selectedImage:setLayer(self.layer)
end

function Map:resetTowers(towers)
    for i = 1,self.width do
        for j = 1,self.height do
            local pos = vector({i, j})
            local tile = self:getTile(pos)
            local key = Tower.serialize_pos(pos)
            if towers[key] == nil then
                if tile ~= TILE_TYPES.EMPTY and tile ~= TILE_TYPES.ENEMY and tile ~= TILE_TYPES.VOID then
                    self:setTile(pos, TILE_TYPES.EMPTY)
                end
            else
                self:setTile(pos, towers[key].type.id)
            end
        end
    end
end


text:
--------------------------------
-- @module RelativeBox
-- @extend Layout
-- @parent_module ccui

--------------------------------
-- @overload self, size_table
-- @overload self
-- @function [parent=#RelativeBox] create
-- @param self
-- @param #size_table size
-- @return RelativeBox#RelativeBox ret (return value: ccui.RelativeBox)

--------------------------------
-- Default constructor
-- @function [parent=#RelativeBox] RelativeBox
-- @param self
-- @return RelativeBox#RelativeBox self (return value: ccui.RelativeBox)

return nil


text:
--[[ Copyright (c) 2009 Edvin "Lego3" Linge

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. --]]

--! Hospital policy fullscreen window (set staff tiredness and patient cure thresholds, etc.).
class "UIPolicy" (UIFullscreen)

function UIPolicy:UIPolicy(ui, disease_selection)
  self:UIFullscreen(ui)
  local gfx = ui.app.gfx
  if not pcall(function()
    self.background = gfx:loadRaw("Pol01V", 640, 480)
    local palette = gfx:loadPalette("QData", "Pol01V.pal")
    palette:setEntry(255, 0xFF, 0x00, 0xFF) -- Make index 255 transparent
    self.panel_sprites = gfx:loadSpriteTable("QData", "Pol02V", true, palette)
    self.label_font = gfx:loadFont("QData", "Font74V", false, palette)
    self.text_font = gfx:loadFont("QData", "Font105V", false, palette)
  end) then
    ui:addWindow(UIInformation(ui, {_S.errors.dialog_missing_graphics}))
    self:close()
    return
  end

  local hosp = ui.hospital
  self.hospital = hosp

  local --[[persistable:hospital_policy_allow_staff]] function allowStaff(name, state, btn)
    if name == "Allow" then
      if self.prohibit_button.toggled then -- Changing setting from prohibit to allow
        hosp.policies["staff_allowed_to_move"] = true
        self.prohibit_button:toggle()
      else -- Already allowed, toggle again
        self.allow_button:toggle()
      end
    else -- Clicking the prohibit button
      if self.allow_button.toggled then -- Changing setting from allow to prohibit
        hosp.policies["staff_allowed_to_move"] = false
        self.allow_button:toggle()
      else -- Already prohibited, toggle again
        self.prohibit_button:toggle()
      end
    end
  end

  -- Buttons
  self:addPanel(0, 607, 447):makeButton(0, 0, 26, 26, 6, self.close):setTooltip(_S.tooltip.policy.close)
  self.allow_button = self:addPanel(0, 348, 379):makeToggleButton(0, 0, 48, 17, 4, allowStaff, "Allow"):setTooltip(_S.tooltip.policy.staff_leave) -- Allow staff to move
  self.prohibit_button = self:addPanel(0, 395, 379):makeToggleButton(0, 0, 48, 17, 5, allowStaff, "Prohibit"):setTooltip(_S.tooltip.policy.staff_stay) -- Prohibit staff to move

  if self.hospital.policies["staff_allowed_to_move"] then
    self.allow_button:toggle()
  else
    self.prohibit_button:toggle()
  end

  -- Slider positions
  local guess = 129 + hosp.policies["guess_cure"]*299
  local home = 129 + hosp.policies["send_home"]*299
  local stop = 124 + (hosp.policies["stop_procedure"] - 1)*299
  local staffroom = 149 + hosp.policies["goto_staffroom"]*250

  -- Sliders
  self.sliders = {}
  self.sliders["guess_cure"] = self:addPanel(2, guess, 119, 82, 44)
  self.sliders["send_home"] = self:addPanel(1, home, 135, 82, 28)
  self.sliders["stop_procedure"] = self:addPanel(3, stop, 210, 92, 28)
  self.sliders["goto_staffroom"] = self:addPanel(3, staffroom, 285, 92, 28)
  self.sliders["guess_cure"].min_x = home
  self.sliders["guess_cure"].total_min_x = 129 -- Needed to get the correct value set when
  -- windows is closed.
  self.sliders["guess_cure"].max_x = 428
  self.sliders["send_home"].min_x = 129
  self.sliders["send_home"].max_x = guess
  self.sliders["send_home"].total_max_x = 428
  self.sliders["stop_procedure"].min_x = 124
  self.sliders["stop_procedure"].max_x = 423
  self.sliders["stop_procedure"].addition = true -- This value goes from 1 to 2.
  self.sliders["goto_staffroom"].min_x = 149
  self.sliders["goto_staffroom"].max_x = 399

  -- Tooltips for slider bars
  self:makeTooltip(_S.tooltip.policy.diag_procedure,   161, 119, 479, 174)
  self:makeTooltip(_S.tooltip.policy.diag_termination, 161, 210, 479, 249)
  self:makeTooltip(_S.tooltip.policy.staff_rest,       161, 285, 479, 324)
end

function UIPolicy:draw(canvas, x, y)
  self.background:draw(canvas, self.x + x, self.y + y)
  UIFullscreen.draw(self, canvas, x, y)
  x, y = self.x + x, self.y + y

  local text = self.text_font
  local label = self.label_font

  -- Labels on the panels
  local added_x, added_y = self.sliders["send_home"].x, self.sliders["send_home"].y
  label:draw(canvas, _S.policy.sliders.send_home, x + added_x, y + added_y + 2, 82, 0)
  added_x, added_y = self.sliders["guess_cure"].x, self.sliders["guess_cure"].y
  label:draw(canvas, _S.policy.sliders.guess, x + added_x, y + added_y + 2, 82, 0)
  added_x, added_y = self.sliders["stop_procedure"].x, self.sliders["stop_procedure"].y
  label:draw(canvas, _S.policy.sliders.stop, x + added_x, y + added_y + 2, 92, 0)
  added_x, added_y = self.sliders["goto_staffroom"].x, self.sliders["goto_staffroom"].y
  label:draw(canvas, _S.policy.sliders.staff_room, x + added_x, y + added_y + 2, 92, 0)

  -- All other text
  text:draw(canvas, _S.policy.header,            x + 160, y + 78, 300, 0)
  text:draw(canvas, _S.policy.diag_procedure,    x + 161, y + 100)
  text:draw(canvas, _S.policy.diag_termination,  x + 161, y + 181)
  text:draw(canvas, _S.policy.staff_rest,        x + 161, y + 262)
  text:draw(canvas, _S.policy.staff_leave_rooms, x + 161, y + 374)

end

function UIPolicy:onMouseMove(x, y, dx, dy)
  local repaint = UIFullscreen.onMouseMove(self, x, y, dx, dy)
  if self.moving_panel then -- A slider is being moved.
    local p = self.moving_panel
    self.moved_x = self.moved_x + dx
    local new_x = self.moved_x + self.down_x - self.moving_panel.w / 2 - self.offset
    if new_x > p.min_x then
      if new_x < p.max_x then
        self.moving_panel.x = new_x
        self.position_x = new_x
      else
        self.moving_panel.x = self.moving_panel.max_x
        self.position_x = self.moving_panel.max_x
      end
    else
      self.moving_panel.x = self.moving_panel.min_x
      self.position_x = self.moving_panel.min_x
    end
    repaint = true
  end
  return repaint
end

function UIPolicy:onMouseDown(code, x, y)
  if code == "left" then
    self.moving_panel = self:panelHit(x, y)
    if self.moving_panel then
      self.down_x = x
      self.offset = x - (self.moving_panel.x + self.moving_panel.w / 2)
      self.moved_x = 0
      self.position_x = self.moving_panel.x
      return true
    end
  end
  return UIFullscreen.onMouseDown(self, code, x, y)
end

function UIPolicy:onMouseUp(code, x, y)
  if self.moving_panel then
    if self.moving_panel == self.sliders["guess_cure"] then
      self.sliders["send_home"].max_x = self.position_x
    elseif self.moving_panel == self.sliders["send_home"] then
      self.sliders["guess_cure"].min_x = self.position_x
    end
  end
  self.moving_panel = nil
  return UIFullscreen.onMouseUp(self, code, x, y)
end

function UIPolicy:panelHit(x, y)
  for name, panel in pairs(self.sliders) do
    if x > panel.x and y > panel.y and x < panel.x + panel.w and y < panel.y + panel.h then
      return panel
    end
  end
end

function UIPolicy:close()
  for key, s in pairs(self.sliders or {}) do
    local divider = (s.total_max_x or s.max_x) - (s.total_min_x or s.min_x)
    local number = (s.addition and 1 or 0)
    self.hospital.policies[key] = number + (s.x - (s.total_min_x or s.min_x))/divider
  end
  UIFullscreen.close(self)
  self.ui:getWindow(UIBottomPanel):updateButtonStates()
end


text:
---
--- math_types.lua
---
--- Copyright (C) 2018-2019 Xrysnow. All rights reserved.
---

--
local M = {}

local ffi = ffi or require('ffi')
ffi.defines = ffi.defines or {}

for _, v in ipairs({
                       [[union u_32ToU32{int32_t x;uint32_t i;}]],
                       [[union u_64ToU64{int64_t x;uint64_t i;}]],
                       [[union u_FloatToU32{float x;uint32_t i;}]],
                       [[union u_DoubleToU64{double x;uint64_t i;}]] }) do
    if not ffi.defines[v] then
        ffi.cdef(v)
        ffi.defines[v] = true
    end
end

local int32_t = ffi.typeof('int32_t')
local uint32_t = ffi.typeof('uint32_t')
local int64_t = ffi.typeof('int64_t')
local uint64_t = ffi.typeof('uint64_t')
local float = ffi.typeof('float')
local double = ffi.typeof('double')

local unionI32 = ffi.typeof('union u_32ToU32')
local unionI64 = ffi.typeof('union u_64ToU64')
local unionF = ffi.typeof('union u_FloatToU32')
local unionD = ffi.typeof('union u_DoubleToU64')

local i32 = 2 ^ 32
local i64 = 2 ^ 64
local err_hex = "Can't convert it."
local err_len = "Param is too long."

local function trim(hex)
    if string.sub(hex, 1, 2) == '0x' then
        return string.sub(hex, 3)
    end
    return hex
end

local function hex2uint(hex)
    local len = string.len(hex)
    local i = len
    if len > 16 then
        error(err_len)
    end
    if len <= 8 then
        local ret = uint64_t(0)
        while i > 0 do
            local c = string.byte(hex, -i)
            local off
            if c >= 48 and c <= 57 then
                off = 48
            elseif c >= 97 and c <= 102 then
                off = 87
            elseif c >= 65 and c <= 70 then
                off = 55
            else
                error("Wrong letter in hex.")
            end
            ret = ret + (c - off) * 16 ^ (i - 1)
            i = i - 1
        end
        return ret
    else
        return hex2uint(string.sub(hex, 1, -9)) * i32 + hex2uint(string.sub(hex, -8))
    end
end

local function uint2chars(x, n)
    local ret = ''
    local ut = uint32_t
    if n > 4 then
        ut = uint64_t
    end
    local d = ut(256) ^ (n - 1)
    for i = 1, n do
        ret = ret .. string.char(tonumber(x / d))
        x = x % d
        d = d / 256
    end
    --to little-endian
    return ret:reverse()
end

local function chars2uint(c)
    assert(#c <= 8, err_len)
    local ut = uint32_t
    if #c > 4 then
        ut = uint64_t
    end
    local ret = ut(0)
    --little-endian
    for i = 0, #c - 1 do
        ret = ret + ut(256) ^ i * string.byte(c, i + 1)
    end
    return ret
end

---
---@class math.Int
local Int = {}
M.Int = Int

Int.max = 2 ^ 31 - 1
Int.min = -2 ^ 31

function Int.tobytes(x)
    if x <= Int.max and x >= Int.min then
        return uint2chars(uint32_t(x), 4)
    end
    error(err_hex)
end

function Int.frombytes(b)
    assert(#b <= 4, err_len)
    local n = unionI32()
    n.i = chars2uint(b)
    return tonumber(n.x)
end

function Int.tohex(x)
    if x <= Int.max and x >= Int.min then
        return bit.tohex(tonumber(int32_t(x)))
    end
    error(err_hex)
end

function Int.fromhex(h)
    assert(string.len(trim(h)) <= 4, err_len)
    local n = unionI32()
    n.i = hex2uint(trim(h))
    return tonumber(n.x)
end

---
---## Better use with ctype.
---@class math.Int64
local Int64 = {}
M.Int64 = Int64

Int64.max = 2 ^ 63 - 1
Int64.min = -2 ^ 63
Int64.cmin = int64_t(-2 ^ 31) ^ 2 * 2
Int64.cmax = Int64.cmin - 1

function Int64.tobytes(x)
    if x <= Int64.cmax and x >= Int64.cmin then
        return uint2chars(uint64_t(x), 8)
    end
    error(err_hex)
end

---## Return ctype instead of number.
function Int64.frombytes(b)
    local n = unionI64()
    n.i = chars2uint(b)
    return n.x
end

function Int64.tohex(x)
    if x <= Int64.cmax and x >= Int64.cmin then
        local n = int64_t(x)
        return bit.tohex(tonumber(n.i / i32)) .. bit.tohex(tonumber(n.i % i32))
    end
    error(err_hex)
end

---## Return ctype instead of number.
function Int64.fromhex(h)
    local n = unionI64()
    n.i = hex2uint(trim(h))
    return n.x
end

---
---@class math.UInt
local UInt = {}
M.UInt = UInt

UInt.max = 2 ^ 32 - 1
UInt.min = 0

function UInt.tobytes(x)
    if x <= UInt.max and x >= UInt.min then
        return uint2chars(uint32_t(x), 4)
    end
    error(err_hex)
end

function UInt.frombytes(b)
    assert(#b <= 4, err_len)
    return tonumber(chars2uint(b))
end

function UInt.tohex(x)
    if x <= UInt.max and x >= UInt.min then
        return bit.tohex(tonumber(uint32_t(x)))
    end
    error(err_hex)
end

function UInt.fromhex(h)
    assert(string.len(trim(h)) <= 4, err_len)
    return tonumber(hex2uint(trim(h)))
end

---
---## Better use with ctype.
---@class math.UInt64
local UInt64 = {}
M.UInt64 = UInt64

UInt64.max = 2 ^ 64 - 1
UInt64.min = 0
UInt64.cmax = uint64_t(-1)
UInt64.cmin = uint64_t(0)

function UInt64.tobytes(x)
    if x <= UInt64.cmax and x >= UInt64.cmin then
        return uint2chars(uint64_t(x), 8)
    end
    error(err_hex)
end

---## Return ctype instead of number.
function UInt64.frombytes(b)
    return chars2uint(b)
end

function UInt64.tohex(x)
    if x <= UInt64.cmax and x >= UInt64.cmin then
        local n = uint64_t(x)
        return bit.tohex(tonumber(n.i / i32)) .. bit.tohex(tonumber(n.i % i32))
    end
    error(err_hex)
end

---## Return ctype instead of number.
function UInt64.fromhex(h)
    return hex2uint(trim(h))
end

---
---## Possible loss of precision, be careful.
---@class math.Float
local Float = {}
M.Float = Float

local f1 = unionF({ 1e39 })
local f2 = unionF({ -1e39 })
f1.i = f1.i - 1
f2.i = f2.i - 1
Float.max = tonumber(f1.x)
Float.min = tonumber(f2.x)
Float.cmax = f1.x
Float.cmin = f2.x
Float.eps = 1.19209290E-7

function Float.tobytes(x)
    if x <= Float.max and x >= Float.min then
        local n = unionF()
        n.x = x
        return uint2chars(n.i, 4)
    end
    error(err_hex)
end

function Float.frombytes(b)
    assert(#b <= 4, err_len)
    local n = unionF()
    n.i = chars2uint(b)
    return tonumber(n.x)
end

function Float.tohex(x)
    if x < Float.max and x > Float.min then
        local n = unionF(x)
        return bit.tohex(tonumber(n.i))
    end
    error(err_hex)
end

function Float.fromhex(h)
    assert(string.len(trim(h)) <= 4, err_len)
    local n = unionF()
    n.i = hex2uint(trim(h))
    return tonumber(n.x)
end

---
---@class math.Double
local Double = {}
M.Double = Double

local d1 = unionD({ 1e309 })
local d2 = unionD({ -1e309 })
d1.i = d1.i - 1
d2.i = d2.i - 1

Double.max = tonumber(d1.x)
Double.min = tonumber(d2.x)
Double.cmax = d1.x
Double.cmin = d2.x
Double.eps = 1.084202172485504E-19

function Double.tobytes(x)
    local n = unionD()
    n.x = x
    return uint2chars(n.i, 8)
end

function Double.frombytes(b)
    local n = unionD()
    n.i = chars2uint(b)
    return tonumber(n.x)
end

function Double.tohex(x)
    local n = unionD(x)
    return bit.tohex(tonumber(n.i / i32)) .. bit.tohex(tonumber(n.i % i32))
end

function Double.fromhex(h)
    local n = unionD()
    n.i = hex2uint(trim(h))
    return tonumber(n.x)
end

---

M.int32_t = ffi.typeof('int32_t')     ---## ctype for int32_t from ffi
M.uint32_t = ffi.typeof('uint32_t')    ---## ctype for uint32_t from ffi
M.int64_t = ffi.typeof('int64_t')     ---## ctype for int64_t from ffi
M.uint64_t = ffi.typeof('uint64_t')    ---## ctype for uint64_t from ffi
M.float = ffi.typeof('float')       ---## ctype for float from ffi
M.double = ffi.typeof('double')      ---## ctype for double from ffi

return M


text:
#!/usr/bin/env tarantool
local test = require("sqltester")
test:plan(50)

test:do_execsql_test(
    "alter-1.1",
    [[
        CREATE TABLE t1(id  INT PRIMARY KEY, a INT , b INT );
        INSERT INTO t1 VALUES(1, 1, 2);
        CREATE TABLE "t1x1"(c  INT UNIQUE, b  INT PRIMARY KEY);
        INSERT INTO "t1x1" VALUES(3, 4);
        CREATE INDEX t1i1 ON T1(B);
        CREATE INDEX t1i2 ON t1(a, b);
        CREATE INDEX i3 ON "t1x1"(b, c);
        CREATE TABLE "Space_Table"(id  INT PRIMARY KEY, e INT , f INT , g  INT UNIQUE);
        INSERT INTO "Space_Table" VALUES(1, 5, 6, 7);
        SELECT 't1', * FROM t1;
        SELECT 't1x1', * FROM "t1x1";
        SELECT 'Space_Table', * FROM "Space_Table";
    ]], {
        -- <alter-1.1>
        "Space_Table", 1, 5, 6, 7
        -- </alter-1.1>
    })

test:do_execsql_test(
    "alter-1.2",
    [[
        SELECT count(*) FROM "_space" WHERE "name" IN ('T1', 't1x1', 'Space_Table')
    ]], {
        -- <alter-1.2>
        3
        -- </alter-1.2>
    })

test:do_execsql_test(
    "alter-1.3",
    [[
        ALTER TABLE T1 RENAME to "t1";
        ALTER TABLE "t1x1" RENAME TO T2;
        ALTER TABLE "Space_Table" RENAME to SPACE_TABLE;
    ]], {
        -- <alter-1.3>
        -- </alter-1.3>
    })

test:do_execsql_test(
    "alter-1.4",
    [[
        SELECT 't1', * FROM "t1";
        SELECT 't2', * FROM t2;
        SELECT 'space_table', * FROM SPACE_TABLE;
    ]], {
        -- <alter-1.4>
        "space_table", 1, 5, 6, 7
        -- </alter-1.4>
    })

test:do_execsql_test(
    "alter-1.5",
    [[
        SELECT count(*) FROM "_space" WHERE "name" IN ('T1', 't1x1', 'Space_Table', 'SPACE_TABLE', 't1', 'T2');
    ]], {
        -- <alter-1.5>
        3
        -- </alter-1.5>
    })

test:do_catchsql_test(
    "alter-2.1",
    [[
        ALTER TABLE none RENAME TO hi;
    ]], {
        -- <alter-2.1>
        1, "Space 'NONE' does not exist"
        -- </alter-2.1>
    })

test:do_catchsql_test(
    "alter-2.2",
    [[
        CREATE TABLE t3(id  INT PRIMARY KEY, p INT , q INT , r INT );
        ALTER TABLE t2 RENAME TO t3;
    ]], {
        -- <alter-2.2>
        1, "Space 'T3' already exists"
        -- </alter-2.2>
    })

test:do_test(
    "alter-2.3.prepare",
    function()
        local format = {}
        format[1] = { name = 'id', type = 'integer'}
        format[2] = { name = 'f2', type = 'number'}
        local s = box.schema.create_space('t', {format = format})
        s:create_index('i', {parts= {1, 'integer'}})

        s:replace{1, 4}
        s:replace{2, 2}
        s:replace{3, 3}
        s:replace{4, 3}
    end,
    {})

test:do_catchsql_test(
    "alter-2.3",
    [[
        ALTER TABLE "t" RENAME TO "new_lua_space";
    ]], {
        -- <alter-2.3>
        0
        -- </alter-2.3>
    })

test:do_execsql_test(
    "alter-2.4",
    [[
        SELECT "f2" from "new_lua_space" WHERE "f2" >= 3 ORDER BY "id";
    ]], {
    -- <alter-2.4>
        4, 3, 3
    -- </alter-2.4>
})

test:do_execsql_test(
    "alter-3.1",
    [[
        CREATE TABLE t6(id  INT PRIMARY KEY, a INT , b INT , c INT );
        CREATE TABLE tab(id  INT PRIMARY KEY);
        CREATE TRIGGER trig1 AFTER INSERT ON T6 FOR EACH ROW BEGIN INSERT INTO tab VALUES(new.id); END;
        INSERT INTO t6 VALUES(1, 1, 2, 3);
        SELECT * FROM tab;

    ]], {
        -- <alter-3.1>
        1
        -- </alter-3.1>
    })

test:do_execsql_test(
    "alter-3.2",
    [[
        ALTER TABLE t6 RENAME TO t7;
        INSERT INTO t7 VALUES(2, 4, 5, 6);
        SELECT * FROM tab;

    ]], {
        -- <alter-3.2>
        1, 2
        -- </alter-3.2>
    })

test:do_execsql_test(
    "alter-3.3",
    [[
            DROP TRIGGER trig1;
            SELECT * FROM "_trigger";
    ]], {
        -- <alter-3.3>
        -- </alter-3.3>
    })

test:do_execsql_test(
    "alter-3.4",
    [[
        CREATE TRIGGER trig2 AFTER INSERT ON t7 FOR EACH ROW BEGIN INSERT INTO tab VALUES(new.id); END;
        INSERT INTO t7 VALUES(3, 1, 2, 3);
        SELECT * FROM tab;
    ]], {
        -- <alter-3.4>
        1, 2, 3
        -- </alter-3.4>
    })

test:do_execsql_test(
    "alter-3.5",
    [[
        ALTER TABLE t7 RENAME TO "t8";
        INSERT INTO "t8" VALUES(4, 4, 5, 6);
        SELECT * FROM tab;
    ]], {
        -- <alter-3.5>
        1, 2, 3, 4
        -- </alter-3.5>
    })

test:do_execsql_test(
    "alter-3.6",
    [[
        DROP TRIGGER trig2;
    ]], {
        -- <alter-3.6>
        -- </alter-3.6>
    })

test:do_execsql_test(
    "alter-3.7",
    [[
        CREATE TRIGGER trig3 AFTER INSERT ON "t8" FOR EACH ROW BEGIN INSERT INTO tab VALUES(new.id); END;
        INSERT INTO "t8" VALUES(5, 1, 2, 3);
        SELECT * FROM tab;
    ]], {
        -- <alter-3.7>
        1, 2, 3, 4, 5
        -- </alter-3.7>
    })

test:do_execsql_test(
    "alter-3.8",
    [[
        ALTER TABLE "t8" RENAME TO t9;
        INSERT INTO t9 VALUES(6, 4, 5, 6);
        SELECT * FROM tab;
    ]], {
        -- <alter-3.8>
        1, 2, 3, 4, 5, 6
        -- </alter-3.8>
    })

test:do_execsql_test(
    "alter-4.1",
    [[
        CREATE TABLE tbl1(a INTEGER PRIMARY KEY AUTOINCREMENT);
        INSERT INTO tbl1 VALUES(10);
        INSERT INTO tbl1 VALUES(NULL);
        SELECT a FROM tbl1;
    ]], {
        -- <alter-4.1>
        10, 11
        -- </alter-4.1>
    })

test:do_execsql_test(
    "alter-4.2",
    [[
        ALTER TABLE tbl1 RENAME TO tbl2;
        DELETE FROM tbl2;
        INSERT INTO tbl2 VALUES(NULL);
        SELECT a FROM tbl2;
    ]], {
        -- <alter-4.2>
        12
        -- </alter-4.2>
    })

test:do_execsql_test(
    "alter-4.3",
    [[
        DROP TABLE tbl2;
    ]], {
        -- <alter-4.3>
        -- </alter-4.3>
    })

test:do_execsql_test(
    "alter-5.1",
    [[
        CREATE TABLE xyz(x  INT PRIMARY KEY);
        ALTER TABLE xyz RENAME TO "xyz1234abc";
        SELECT "name" FROM "_space" WHERE "name" = 'xyz1234abc';
    ]], {
        -- <alter-5.1>
        "xyz1234abc"
        -- </alter-5.1>
    })

test:do_execsql_test(
    "alter-5.2",
    [[
        ALTER TABLE "xyz1234abc" RENAME TO xyzabc;
        SELECT "name" FROM "_space" WHERE "name" = 'XYZABC';
    ]], {
        -- <alter-5.2>
        "XYZABC"
        -- </alter-5.2>
    })

test:do_execsql_test(
    "alter-6.1",
    [[
        DROP TABLE IF EXISTS t1;
        DROP TABLE IF EXISTS t2;
        DROP TRIGGER trig3;
        CREATE TABLE t1(id  INT PRIMARY KEY, b INT , c INT );
        INSERT INTO t1 VALUES(1,2,3), (3,2,1);
        CREATE TABLE t2(id  INT PRIMARY KEY);
        CREATE TRIGGER on_t1 AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(new.id + 100); END;
        CREATE TRIGGER on_t2 AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(new.id + 101); END;
        CREATE TRIGGER on_t3 AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 values(new.id + 102); END;
        ALTER TABLE t1 RENAME TO "a";
        INSERT INTO "a" VALUES(8, 5, 9);
        SELECT * FROM t2;
    ]], {
        -- <alter-6.1>
        108, 109, 110
        -- </alter-6.1>
    })

test:do_execsql_test(
    "alter-6.2",
    [[
        DROP TRIGGER on_t2;
        SELECT "name" FROM "_trigger";
    ]], {
        -- <alter-6.2>
        "ON_T1", "ON_T3"
        -- </alter-6.2>
    })

test:do_execsql_test(
    "alter-6.3",
    [[
        DELETE FROM t2;
        INSERT INTO "a" VALUES(100,5,9);
        SELECT * FROM t2;
    ]], {
        -- <alter-6.3>
        200, 202
        -- </alter-6.3>
    })

test:do_execsql_test(
    "alter-6.4",
    [[
        DROP TABLE "a";
        SELECT * FROM "_trigger";
    ]], {
        -- <alter-6.4>
        -- </alter-6.4>
    })

-- Make sure that foreign keys remain after renaming
-- as for parent table, as for child table.
--
test:do_execsql_test(
    "alter-7.1",
    [[
        DROP TABLE IF EXISTS t1;
        DROP TABLE IF EXISTS t2;
        DROP TABLE IF EXISTS t3;
        CREATE TABLE t2(id  INT PRIMARY KEY);
        CREATE TABLE t3(id  INT PRIMARY KEY);
        CREATE TABLE t1(a  INT PRIMARY KEY, b INT , c INT , FOREIGN KEY(b) REFERENCES t2(id), FOREIGN KEY(c) REFERENCES t3(id));
        INSERT INTO t2 VALUES(1);
        INSERT INTO t3 VALUES(2);
        INSERT INTO t1 VALUES(1, 1, 2);
        ALTER TABLE t1 RENAME TO t5;
        SELECT * FROM t5;
    ]], {
        -- <alter-7.1>
        1, 1, 2
        -- </alter-7.1>
    })

test:do_catchsql_test(
    "alter-7.2",
    [[
        INSERT INTO t5 VALUES(2, 1, 3);
    ]], {
        -- <alter-7.2>
        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
        -- </alter-7.2>
    })

test:do_catchsql_test(
    "alter-7.3",
    [[
        INSERT INTO t5 VALUES(2, 2, 2);
    ]], {
        -- <alter-7.3>
        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
        -- </alter-7.3>
    })

test:do_execsql_test(
    "alter-7.4",
    [[
        INSERT INTO t5 VALUES(2, 1, 2);
        SELECT * FROM t5;
    ]], {
        -- <alter-7.4>
        1, 1, 2, 2, 1, 2
        -- </alter-7.4>
    })

test:do_execsql_test(
    "alter-7.5",
    [[
        INSERT INTO t2 VALUES(2);
        INSERT INTO t3 VALUES(3);
        INSERT INTO t5 VALUES(3, 2, 3);
        SELECT * FROM t5;
    ]], {
        -- <alter-7.5>
        1, 1, 2, 2, 1, 2, 3, 2, 3
        -- </alter-7.5>
    })

test:do_catchsql_test(
    "alter-7.6",
    [[
        SELECT * FROM t1;
    ]], {
        -- <alter-7.6>
        1, "Space 'T1' does not exist"
        -- </alter-7.6>
    })

test:do_execsql_test(
    "alter-7.7",
    [[
        ALTER TABLE t2 RENAME TO t4;
        SELECT * FROM t4;
    ]], {
        -- <alter-7.7>
        1, 2
        -- </alter-7.7>
    })

test:do_execsql_test(
    "alter-7.8",
    [[
        SELECT * FROM t5;
    ]], {
        -- <alter-7.8>
        1, 1, 2, 2, 1, 2, 3, 2, 3
        -- </alter-7.8>
    })

test:do_catchsql_test(
    "alter-7.9",
    [[
        INSERT INTO t5 VALUES(4, 5, 3);
    ]], {
        -- <alter-7.9>
        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
        -- </alter-7.9>
    })

test:do_execsql_test(
    "alter-7.10",
    [[
        INSERT INTO t5 VALUES(4, 1, 2);
        SELECT * FROM t5;
    ]], {
        -- <alter-7.10>
        1, 1, 2, 2, 1, 2, 3, 2, 3, 4, 1, 2
        -- </alter-7.10>
    })

test:do_catchsql_test(
    "alter-7.11",
    [[
        INSERT INTO t4 VALUES(5);
        INSERT INTO t3 VALUES(4);
        INSERT INTO t5 VALUES(5, 5, 4);
    ]], {
        -- <alter-7.11>
        0
        -- </alter-7.11>
    })

test:do_execsql_test(
    "alter-7.12",
    [[
        ALTER TABLE t3 RENAME TO t6;
        SELECT * FROM t6;

    ]], {
        -- <alter-7.12>
        2, 3, 4
        -- </alter-7.12>
    })

test:do_execsql_test(
    "alter-7.13",
    [[
        SELECT * FROM t5;
    ]], {
        -- <alter-7.13>
        1, 1, 2, 2, 1, 2, 3, 2, 3, 4, 1, 2, 5, 5, 4
        -- </alter-7.13>
    })

test:do_catchsql_test(
    "alter-7.14",
    [[
        INSERT INTO t5 VALUES(6, 5, 10);
    ]], {
        -- <alter-7.14>
        1, "Failed to execute SQL statement: FOREIGN KEY constraint failed"
        -- </alter-7.14>
    })

test:do_execsql_test(
    "alter-7.15",
    [[
        INSERT INTO t5 VALUES(6, 1, 2);
        SELECT * FROM t5;
    ]], {
        -- <alter-7.15>
        1, 1, 2, 2, 1, 2, 3, 2, 3, 4, 1, 2, 5, 5, 4, 6, 1, 2
        -- </alter-7.15>
    })

test:do_catchsql_test(
    "alter-7.16",
    [[
        INSERT INTO t4 VALUES(6);
        INSERT INTO t6 VALUES(10);
        INSERT INTO t5 VALUES(7, 6, 10);
    ]], {
        -- <alter-7.11>
        0
        -- </alter-7.11>
    })

test:do_test(
    "alter-8.1.0",
    function()
        local format = {}
        format[1] = { name = 'id', type = 'scalar'}
        format[2] = { name = 'f2', type = 'scalar'}
        box.schema.create_space('T', {format = format})
    end,
    {})

test:do_catchsql_test(
    "alter-8.1.1",
    [[
        ALTER TABLE t ADD CONSTRAINT pk PRIMARY KEY("id");
    ]], {
        0
    })

test:do_test(
    "alter-8.1.2",
    function()
        return box.space.T.index[0].id
    end, 0)

test:do_catchsql_test(
    "alter-8.2",
    [[
        ALTER TABLE t ADD CONSTRAINT pk1 PRIMARY KEY("f2");
    ]], {
        1, "Duplicate key exists in unique index 'primary' in space '_index'"
    })

test:do_catchsql_test(
    "alter-8.3.1",
    [[
        ALTER TABLE t ADD CONSTRAINT i1 UNIQUE("f2");
    ]], {
        0
    })

test:do_test(
    "alter-8.3.2",
    function()
        local i = box.space.T.index[1]
        return i.id
    end, 1)

test:do_catchsql_test(
    "alter-8.4",
    [[
        DROP INDEX i1 ON t;
        DROP INDEX pk ON t;
    ]], {
    0
})

-- Commented due to #2953
--
-- test:do_catchsql_test(
--     "alter-7.11",
--     [[
--         DROP TABLE t4;
--     ]], {
--         -- <alter-7.11>
--         1, "FOREIGN KEY constraint failed"
--         -- </alter-7.11>
--     })

test:finish_test()


text:
--BOW embedding without deep network
require 'nn'
--require 'cunn'
-- IMP if args is not passed, it takes from global 'args'
return function(args)

    function create_network(args)
        local n_hid = args.hist_len*args.ncols*args.state_dim
        local mlp = nn.Sequential()
        mlp:add(nn.Reshape(args.hist_len*args.ncols*args.state_dim))
        -- mlp:add(nn.Linear(args.hist_len*args.ncols*args.state_dim, n_hid))
        -- mlp:add(nn.Rectifier())
        -- mlp:add(nn.Linear(n_hid, n_hid))
        -- mlp:add(nn.Rectifier())

        local mlp_out = nn.ConcatTable()
        mlp_out:add(nn.Linear(n_hid, args.n_actions))
        mlp_out:add(nn.Linear(n_hid, args.n_objects))

        mlp:add(mlp_out)

        if args.gpu >=0 then
            mlp:cuda()
        end
        return mlp
    end

    return create_network(args)
end

-- for action-object linking
   -- local mlp_out = nn.ConcatTable()
   --      mlp_out:add(nn.Identity())
   --      local action_object_nn = nn.Sequential()
   --      action_object_nn:add(nn.Linear(args.n_actions, n_hid))
   --      action_object_nn:add(nn.Linear(n_hid, args.n_objects))
   --      mlp_out:add(action_object_nn)


text:
fx = {
style = "STYLE_TRAIL",
properties = {
property_09 = {
name = "Duration",
type = "VARTYPE_FLOAT",
value = 0.50000,
},
property_13 = {
name = "LineDistance",
type = "VARTYPE_FLOAT",
value = -1,
},
property_06 = {
name = "Billboard",
type = "VARTYPE_BOOL",
value = 1,
},
property_05 = {
name = "ColourTail",
type = "VARTYPE_COLOUR",
value = {
1,
1,
1,
1,
},
},
property_08 = {
name = "FadeOutTime",
type = "VARTYPE_FLOAT",
value = 1,
},
property_07 = {
name = "DeathRate",
type = "VARTYPE_FLOAT",
value = 1,
},
property_02 = {
name = "Blending",
type = "VARTYPE_INT",
value = 2,
},
property_01 = {
name = "Width",
type = "VARTYPE_FLOAT",
value = 4,
},
property_04 = {
name = "ColourHead",
type = "VARTYPE_COLOUR",
value = {
1,
1,
1,
1,
},
},
property_03 = {
name = "Texture",
type = "VARTYPE_STRING",
value = "DATA:ART/FX/ALOS_THOR_HAMMER/THOR_HAMMER_SPARK_TRAIL.TGA",
},
property_12 = {
name = "WidthEpsilon",
type = "VARTYPE_FLOAT",
value = 0,
},
property_11 = {
name = "DurationEpsilon",
type = "VARTYPE_FLOAT",
value = 0,
},
property_00 = {
name = "Length",
type = "VARTYPE_FLOAT",
value = 0.50000,
},
property_10 = {
name = "LengthEpsilon",
type = "VARTYPE_FLOAT",
value = 0.50000,
},
},
}


text:
return function()
	local assertDeepEqual = require(script.Parent.Parent.Parent.assertDeepEqual)
	local createSpy = require(script.Parent.Parent.createSpy)
	local createElement = require(script.Parent.Parent.createElement)
	local createFragment = require(script.Parent.Parent.createFragment)
	local createReconciler = require(script.Parent.Parent.createReconciler)
	local NoopRenderer = require(script.Parent.Parent.NoopRenderer)

	local Component = require(script.Parent.Parent.Component)

	local noopReconciler = createReconciler(NoopRenderer)

	it("should be invoked on initial mount", function()
		local getDerivedSpy = createSpy()
		local WithDerivedState = Component:extend("WithDerivedState")

		WithDerivedState.getDerivedStateFromProps = getDerivedSpy.value

		function WithDerivedState:render()
			return nil
		end

		local element = createElement(WithDerivedState, {
			someProp = 1,
		})
		local hostParent = nil
		local hostKey = "WithDerivedState"

		noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		expect(getDerivedSpy.callCount).to.equal(1)

		local values = getDerivedSpy:captureValues("props", "state")

		assertDeepEqual(values.props, { someProp = 1 })
		assertDeepEqual(values.state, {})
	end)

	it("should be invoked when updated via props", function()
		local getDerivedSpy = createSpy()
		local WithDerivedState = Component:extend("WithDerivedState")

		WithDerivedState.getDerivedStateFromProps = getDerivedSpy.value

		function WithDerivedState:render()
			return nil
		end

		local hostParent = nil
		local hostKey = "WithDerivedState"

		local node = noopReconciler.mountVirtualNode(
			createElement(WithDerivedState, {
				someProp = 1,
			}),
			hostParent,
			hostKey
		)

		noopReconciler.updateVirtualNode(
			node,
			createElement(WithDerivedState, {
				someProp = 2,
			})
		)

		expect(getDerivedSpy.callCount).to.equal(2)

		local values = getDerivedSpy:captureValues("props", "state")

		assertDeepEqual(values.props, { someProp = 2 })
		assertDeepEqual(values.state, {})
	end)

	it("should be invoked when updated via state", function()
		local getDerivedSpy = createSpy()
		local WithDerivedState = Component:extend("WithDerivedState")

		WithDerivedState.getDerivedStateFromProps = getDerivedSpy.value

		function WithDerivedState:init()
			self:setState({
				someState = 1,
			})
		end

		function WithDerivedState:render()
			return nil
		end

		local element = createElement(WithDerivedState)
		local hostParent = nil
		local hostKey = "WithDerivedState"

		local node = noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		noopReconciler.updateVirtualNode(node, element, {
			someState = 2,
		})

		-- getDerivedStateFromProps will be called:
		-- * Once on empty props
		-- * Once during the self:setState in init
		-- * Once more, defensively, on the resulting state AFTER init
		-- * On updating with new state via updateVirtualNode
		expect(getDerivedSpy.callCount).to.equal(4)

		local values = getDerivedSpy:captureValues("props", "state")

		assertDeepEqual(values.props, {})
		assertDeepEqual(values.state, { someState = 2 })
	end)

	it("should be invoked when updating via state in init (which skips reconciliation)", function()
		local getDerivedSpy = createSpy()
		local WithDerivedState = Component:extend("WithDerivedState")

		WithDerivedState.getDerivedStateFromProps = getDerivedSpy.value

		function WithDerivedState:init()
			self:setState({
				stateFromInit = 1,
			})
		end

		function WithDerivedState:render()
			return nil
		end

		local element = createElement(WithDerivedState, {
			someProp = 1,
		})
		local hostParent = nil
		local hostKey = "WithDerivedState"

		noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		-- getDerivedStateFromProps will be called:
		-- * Once on empty props
		-- * Once during the self:setState in init
		-- * Once more, defensively, on the resulting state AFTER init
		expect(getDerivedSpy.callCount).to.equal(3)

		local values = getDerivedSpy:captureValues("props", "state")

		assertDeepEqual(values.props, {
			someProp = 1,
		})
		assertDeepEqual(values.state, {
			stateFromInit = 1,
		})
	end)

	it("should receive defaultProps", function()
		local getDerivedSpy = createSpy()
		local WithDerivedState = Component:extend("WithDerivedState")

		WithDerivedState.defaultProps = {
			someDefaultProp = "foo",
		}

		WithDerivedState.getDerivedStateFromProps = getDerivedSpy.value

		function WithDerivedState:render()
			return nil
		end

		local element = createElement(WithDerivedState, {
			someProp = 1,
		})
		local hostParent = nil
		local hostKey = "WithDerivedState"

		local node = noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		expect(getDerivedSpy.callCount).to.equal(1)

		local values = getDerivedSpy:captureValues("props", "state")

		assertDeepEqual(values.props, {
			someDefaultProp = "foo",
			someProp = 1,
		})

		-- Update via props, confirm that defaultProp is still present
		element = createElement(WithDerivedState, {
			someProp = 2,
		})

		noopReconciler.updateVirtualNode(node, element)

		expect(getDerivedSpy.callCount).to.equal(2)

		values = getDerivedSpy:captureValues("props", "state")

		assertDeepEqual(values.props, {
			someDefaultProp = "foo",
			someProp = 2,
		})
	end)

	it("should derive state for all setState updates, even when deferred", function()
		local Child = Component:extend("Child")
		local stateUpdaterSpy = createSpy(function()
			return {}
		end)
		local stateDerivedSpy = createSpy()

		function Child:render()
			return nil
		end

		function Child:didMount()
			self.props.callback()
		end

		local Parent = Component:extend("Parent")

		Parent.getDerivedStateFromProps = stateDerivedSpy.value

		function Parent:render()
			local callback = function()
				self:setState(stateUpdaterSpy.value)
			end

			return createFragment({
				ChildA = createElement(Child, {
					callback = callback,
				}),
				ChildB = createElement(Child, {
					callback = callback,
				}),
			})
		end

		local element = createElement(Parent)
		local hostParent = nil
		local key = "Test"

		noopReconciler.mountVirtualNode(element, hostParent, key)

		expect(stateUpdaterSpy.callCount).to.equal(2)

		-- getDerivedStateFromProps is always called on initial state
		expect(stateDerivedSpy.callCount).to.equal(3)
	end)

	it("should have derived state after assigning to state in init", function()
		local getStateCallback
		local getDerivedSpy = createSpy(function()
			return {
				derived = true,
			}
		end)
		local WithDerivedState = Component:extend("WithDerivedState")

		WithDerivedState.getDerivedStateFromProps = getDerivedSpy.value

		function WithDerivedState:init()
			self.state = {
				init = true,
			}

			getStateCallback = function()
				return self.state
			end
		end

		function WithDerivedState:render()
			return nil
		end

		local hostParent = nil
		local hostKey = "WithDerivedState"
		local element = createElement(WithDerivedState)

		noopReconciler.mountVirtualNode(element, hostParent, hostKey)

		expect(getDerivedSpy.callCount).to.equal(2)

		assertDeepEqual(getStateCallback(), {
			init = true,
			derived = true,
		})
	end)
end


text:
--- Utility functions that callback with (maid, value) and guarantee cleanup of maid returning maid
-- @module BoundLinkConnectionUtils

local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Nevermore"))

local Maid = require("Maid")
local observeProperty = require("observeProperty")
local CharacterUtils = require("CharacterUtils")

local BoundLinkConnectionUtils = {}

-- TODO: Move this somewhere else? (ConnectionUtils?)
function BoundLinkConnectionUtils.connectToParent(object, callback)
	assert(typeof(object) == "Instance", "Bad 'object' instance")
	assert(type(callback) == "function", "Bad 'callback' function")

	return observeProperty(object, "Parent", callback)
end

function BoundLinkConnectionUtils.connectToParentLinksBoundClass(object, linkName, binder, callback)
	assert(typeof(object) == "Instance", "Bad 'object' instance")
	assert(type(linkName) == "string", "Bad 'linkName' string")
	assert(binder, "Bad 'binder' binder")
	assert(type(callback) == "function", "Bad 'callback' function")

	return BoundLinkConnectionUtils.connectToParent(object, function(maid, parent)
		maid:GiveTask(BoundLinkConnectionUtils.connectToLinksValueBoundClass(parent, linkName, binder, callback))
	end)
end

function BoundLinkConnectionUtils.connectToGetPlayerFromCharacter(object, callback)
	assert(typeof(object) == "Instance", "Bad 'object' instance")
	assert(type(callback) == "function", "Bad 'callback' function")

	local maid = Maid.new()

	-- Assume character doesn't change after being assigned (i.e. ancestry is aligned with setting)

	local handleChanged = BoundLinkConnectionUtils._makeChangedHandlerWithNoCheck(maid, callback)
	maid:GiveTask(object.AncestryChanged:Connect(function()
		handleChanged(CharacterUtils.getPlayerFromCharacter(object))
	end))
	handleChanged(CharacterUtils.getPlayerFromCharacter(object))

	return maid
end

function BoundLinkConnectionUtils.connectToChildren(parent, callback)
	assert(typeof(parent) == "Instance", "Bad 'parent' instance")
	assert(typeof(callback) == "function", "Bad 'callback' function")

	local topMaid = Maid.new()

	local function handleChildAdded(child)
		if topMaid[child] then
			return -- shouldn't happen, but we'll be paranoid
		end

		local maid = Maid.new()
		topMaid[child] = maid
		callback(topMaid, child)
	end

	topMaid:GiveTask(parent.ChildAdded:Connect(handleChildAdded))
	topMaid:GiveTask(parent.ChildRemoved:Connect(function(child)
		topMaid[child] = nil
	end))

	for _, child in pairs(parent:GetChildren()) do
		handleChildAdded(child)
	end

	return topMaid
end

function BoundLinkConnectionUtils.connectToBoundChildren(parent, binder, callback)
	assert(typeof(parent) == "Instance", "Bad 'parent' instance")
	assert(binder, "Bad 'binder' binder")
	assert(type(callback) == "function", "Bad 'callback' instance")

	return BoundLinkConnectionUtils.connectToChildren(parent, function(maid, child)
		maid:GiveTask(BoundLinkConnectionUtils.connectToBoundClass(binder, child, callback))
	end)
end

function BoundLinkConnectionUtils.connectToLinksValueBoundClass(parent, linkName, binder, callback)
	assert(typeof(parent) == "Instance", "Bad 'parent' instance")
	assert(type(linkName) == "string", "Bad 'linkName' string")
	assert(binder, "Bad 'binder' binder")
	assert(type(callback) == "function", "Bad 'callback' instance")

	return BoundLinkConnectionUtils.connectToLinksValue(parent, linkName, function(maid, linkValue)
		maid:GiveTask(BoundLinkConnectionUtils.connectToBoundClass(binder, linkValue, callback))
	end)
end

function BoundLinkConnectionUtils.connectToParentLinks(object, linkName, callback)
	assert(typeof(object) == "Instance", "Bad 'object' instance")
	assert(type(linkName) == "string", "Bad 'linkName' name")
	assert(type(callback) == "function", "Bad 'callback' function")

	return BoundLinkConnectionUtils.connectToParent(object, function(maid, parent)
		maid:GiveTask(BoundLinkConnectionUtils.connectToLinksValue(parent, linkName, callback))
	end)
end

-- @param callback callback(maid, linkValue, link)
function BoundLinkConnectionUtils.connectToLinksValue(parent, linkName, callback)
	assert(typeof(parent) == "Instance", "Bad 'parent' instance")
	assert(type(linkName) == "string", "Bad 'linkName' instance")
	assert(typeof(callback) == "function", "Bad 'callback' function")

	local maid = Maid.new()

	-- So we do assume child name doesn't change
	local function isLink(child)
		return child.Name == linkName and child:IsA("ObjectValue")
	end

	local function handleChildAdded(child)
		if isLink(child) then
			maid[child] = BoundLinkConnectionUtils.connectToLinkValue(child, callback)
		end
	end

	maid:GiveTask(parent.ChildAdded:Connect(handleChildAdded))
	maid:GiveTask(parent.ChildRemoved:Connect(function(child)
		maid[child] = nil
	end))

	for _, child in pairs(parent:GetChildren()) do
		handleChildAdded(child)
	end

	return maid
end

function BoundLinkConnectionUtils.connectToLinkValue(link, callback)
	assert(typeof(link) == "Instance", "Bad 'link' instance")
	assert(typeof(callback) == "function", "Bad 'callback' function")

	local maid = Maid.new()

	local handleLinkChanged = BoundLinkConnectionUtils._makeChangedHandlerWith(maid, callback)
	maid:GiveTask(link:GetPropertyChangedSignal("Value"):Connect(function()
		handleLinkChanged(link.Value, link)
	end))
	handleLinkChanged(link.Value, link)

	return maid
end

function BoundLinkConnectionUtils.connectToBoundClass(binder, instance, callback)
	assert(binder, "Bad 'binder' binder")
	assert(typeof(instance) == "Instance", "Bad 'instance' instance")
	assert(typeof(callback) == "function", "Bad 'callback' function")

	local maid = Maid.new()

	local handleClassChanged = BoundLinkConnectionUtils._makeChangedHandlerWith(maid, callback)
	maid:GiveTask(binder:ObserveInstance(instance, handleClassChanged))
	handleClassChanged(binder:Get(instance))

	return maid
end

function BoundLinkConnectionUtils.connectToBoundClasses(bindersList, instance, callback)
	assert(bindersList, "Bad 'bindersList' list")
	assert(typeof(instance) == "Instance", "Bad 'instance' instance")
	assert(typeof(callback) == "function", "Bad 'callback' function")

	local maid = Maid.new()

	for _, binder in pairs(bindersList) do
		maid:GiveTask(BoundLinkConnectionUtils.connectToBoundClass(binder, instance, callback))
	end

	return maid
end

function BoundLinkConnectionUtils._makeChangedHandlerWith(topMaid, callback)
	assert(topMaid, "Bad 'topMaid' maid")
	assert(type(callback) == "function", "Bad 'callback' function")

	return function(value, ...)
		if value ~= nil then
			local maid = Maid.new()
			topMaid._callbackMaid = maid
			callback(maid, value, ...)
		else
			topMaid._callbackMaid = nil
			-- TODO: Call callback
		end
	end
end

function BoundLinkConnectionUtils._makeChangedHandlerWithNoCheck(topMaid, callback)
	assert(topMaid, "Bad 'topMaid' maid")
	assert(type(callback) == "function", "Bad 'callback' function")

	return function(value, ...)
		local maid = Maid.new()
		topMaid._callbackMaid = maid
		callback(maid, value, ...)
	end
end

return BoundLinkConnectionUtils


text:
test_run = require('test_run')
inspector = test_run.new()
engine = inspector:get_cfg('engine')
inspector:cmd("push filter '"..engine.."' to 'engine'")

-- iterator (str)
space = box.schema.space.create('test', { engine = engine })
index = space:create_index('primary', { type = 'tree', parts = {1, 'string'} })
for key = 1, 100 do space:replace({tostring(key)}) end
t = {} for state, v in index:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(tostring(44), {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(tostring(44), {iterator = 'GT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(tostring(77), {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(tostring(77), {iterator = 'LT'}) do table.insert(t, v) end
t
space:drop()

-- iterator (num)
space = box.schema.space.create('test', { engine = engine })
index = space:create_index('primary', { type = 'tree', parts = {1, 'unsigned'} })
for key = 1, 100 do space:replace({key}) end
t = {} for state, v in index:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(44, {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(44, {iterator = 'GT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(77, {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(77, {iterator = 'LT'}) do table.insert(t, v) end
t
space:drop()

-- iterator (decimal)
decimal = require('decimal')
space = box.schema.space.create('test', { engine = engine })
index = space:create_index('primary', { type = 'tree', parts = {1, 'decimal'} })
for key = 1, 100 do space:replace{decimal.new((key-50)/10)} end
t = {} for state, v in index:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(decimal.new(-0.6), {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(decimal.new(-0.6), {iterator = 'GT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(decimal.new(2.7), {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(decimal.new(2.7), {iterator = 'LT'}) do table.insert(t, v) end
t
space:drop()

-- iterator multi-part (num, num)
space = box.schema.space.create('test', { engine = engine })
index = space:create_index('primary', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
for key = 1, 100 do space:replace({key, key}) end
t = {} for state, v in index:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({44, 44}, {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({44, 44}, {iterator = 'GT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({77, 77}, {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs({77, 77}, {iterator = 'LT'}) do table.insert(t, v) end
t
space:drop()

-- iterator with tuple.new
space = box.schema.space.create('test', { engine = engine })
index = space:create_index('primary', { type = 'tree', parts = {1, 'string'} })
for key = 1, 100 do space:replace({tostring(key)}) end
t = {} for state, v in index:pairs(box.tuple.new{}, {iterator = 'ALL'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(box.tuple.new{}, {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(box.tuple.new(tostring(44)), {iterator = 'GE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(box.tuple.new(tostring(44)), {iterator = 'GT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(box.tuple.new{}, {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(box.tuple.new(tostring(77)), {iterator = 'LE'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(box.tuple.new{}, {iterator = 'LT'}) do table.insert(t, v) end
t
t = {} for state, v in index:pairs(box.tuple.new(tostring(77)), {iterator = 'LT'}) do table.insert(t, v) end
t
space:drop()

iterate = dofile('utils.lua').iterate

inspector:cmd("push filter '(error: .builtin/.*[.]lua):[0-9]+' to '\\1'")
# Tree single-part unique

space = box.schema.space.create('tweedledum', { engine = engine })
idx1 = space:create_index('primary', { type = 'tree', parts = {1, 'string'}, unique = true})
-- Tree single-part non-unique
idx2 = space:create_index('i1', { type = 'tree', parts = {2, 'string'}, unique = false})
-- Tree multi-part unique
idx3 = space:create_index('i2', { type = 'tree', parts = {2, 'string', 3, 'string'}, unique = true})
-- Tree multi-part non-unique
idx4 = space:create_index('i3', { type = 'tree', parts = {3, 'string', 4, 'string'}, unique = false })

space:insert{'pid_001', 'sid_001', 'tid_998', 'a'}
space:insert{'pid_002', 'sid_001', 'tid_997', 'a'}
space:insert{'pid_003', 'sid_002', 'tid_997', 'b'}
space:insert{'pid_005', 'sid_002', 'tid_996', 'b'}
space:insert{'pid_007', 'sid_003', 'tid_996', 'a'}
space:insert{'pid_011', 'sid_004', 'tid_996', 'c'}
space:insert{'pid_013', 'sid_005', 'tid_996', 'b'}
space:insert{'pid_017', 'sid_006', 'tid_996', 'a'}
space:insert{'pid_019', 'sid_005', 'tid_995', 'a'}
space:insert{'pid_023', 'sid_005', 'tid_994', 'a'}

-------------------------------------------------------------------------------
-- Iterator: tree single-part unique
-------------------------------------------------------------------------------

iterate('tweedledum', 'primary', 0, 1)
iterate('tweedledum', 'primary', 0, 1, box.index.ALL)
iterate('tweedledum', 'primary', 0, 1, box.index.EQ)
iterate('tweedledum', 'primary', 0, 1, box.index.REQ)
iterate('tweedledum', 'primary', 0, 1, box.index.GE)
iterate('tweedledum', 'primary', 0, 1, box.index.GT)
iterate('tweedledum', 'primary', 0, 1, box.index.LE)
iterate('tweedledum', 'primary', 0, 1, box.index.LT)
iterate('tweedledum', 'primary', 0, 1, box.index.EQ, 'pid_003')
iterate('tweedledum', 'primary', 0, 1, box.index.REQ, 'pid_003')
iterate('tweedledum', 'primary', 0, 1, box.index.EQ, 'pid_666')
iterate('tweedledum', 'primary', 0, 1, box.index.REQ, 'pid_666')
iterate('tweedledum', 'primary', 0, 1, box.index.GE, 'pid_001')
iterate('tweedledum', 'primary', 0, 1, box.index.GT, 'pid_001')
iterate('tweedledum', 'primary', 0, 1, box.index.GE, 'pid_999')
iterate('tweedledum', 'primary', 0, 1, box.index.GT, 'pid_999')
iterate('tweedledum', 'primary', 0, 1, box.index.LE, 'pid_002')
iterate('tweedledum', 'primary', 0, 1, box.index.LT, 'pid_002')
iterate('tweedledum', 'primary', 0, 1, box.index.LE, 'pid_000')
iterate('tweedledum', 'primary', 0, 1, box.index.LT, 'pid_000')

-------------------------------------------------------------------------------
-- Iterator: tree single-part non-unique
-------------------------------------------------------------------------------

iterate('tweedledum', 'i1', 1, 2, box.index.ALL)
iterate('tweedledum', 'i1', 1, 2, box.index.EQ)
iterate('tweedledum', 'i1', 1, 2, box.index.REQ)
iterate('tweedledum', 'i1', 1, 2, box.index.GE)
iterate('tweedledum', 'i1', 1, 2, box.index.GT)
iterate('tweedledum', 'i1', 1, 2, box.index.LE)
iterate('tweedledum', 'i1', 1, 2, box.index.LT)
iterate('tweedledum', 'i1', 1, 2, box.index.EQ, 'sid_005')
iterate('tweedledum', 'i1', 1, 2, box.index.REQ, 'sid_005')
iterate('tweedledum', 'i1', 1, 2, box.index.GE, 'sid_005')
iterate('tweedledum', 'i1', 1, 2, box.index.GT, 'sid_005')
iterate('tweedledum', 'i1', 1, 2, box.index.GE, 'sid_999')
iterate('tweedledum', 'i1', 1, 2, box.index.GT, 'sid_999')
iterate('tweedledum', 'i1', 1, 2, box.index.LE, 'sid_005')
iterate('tweedledum', 'i1', 1, 2, box.index.LT, 'sid_005')
iterate('tweedledum', 'i1', 1, 2, box.index.LE, 'sid_000')
iterate('tweedledum', 'i1', 1, 2, box.index.LT, 'sid_000')

-------------------------------------------------------------------------------
-- Iterator: tree multi-part unique
-------------------------------------------------------------------------------

iterate('tweedledum', 'i2', 1, 3, box.index.ALL)
iterate('tweedledum', 'i2', 1, 3, box.index.EQ)
iterate('tweedledum', 'i2', 1, 3, box.index.REQ)
iterate('tweedledum', 'i2', 1, 3, box.index.GE)
iterate('tweedledum', 'i2', 1, 3, box.index.GT)
iterate('tweedledum', 'i2', 1, 3, box.index.LE)
iterate('tweedledum', 'i2', 1, 3, box.index.LT)
iterate('tweedledum', 'i2', 1, 3, box.index.EQ, 'sid_005')
iterate('tweedledum', 'i2', 1, 3, box.index.EQ, 'sid_005', 'tid_995')
iterate('tweedledum', 'i2', 1, 3, box.index.EQ, 'sid_005', 'tid_999')
iterate('tweedledum', 'i2', 1, 3, box.index.REQ, 'sid_005')
iterate('tweedledum', 'i2', 1, 3, box.index.REQ, 'sid_005', 'tid_995')
iterate('tweedledum', 'i2', 1, 3, box.index.REQ, 'sid_005', 'tid_999')
iterate('tweedledum', 'i2', 1, 3, box.index.GE, 'sid_005')
iterate('tweedledum', 'i2', 1, 3, box.index.GT, 'sid_005')
iterate('tweedledum', 'i2', 1, 3, box.index.GE, 'sid_005', 'tid_995')
iterate('tweedledum', 'i2', 1, 3, box.index.GT, 'sid_005', 'tid_995')
iterate('tweedledum', 'i2', 1, 3, box.index.GE, 'sid_005', 'tid_999')
iterate('tweedledum', 'i2', 1, 3, box.index.GT, 'sid_005', 'tid_999')
iterate('tweedledum', 'i2', 1, 3, box.index.GE, 'sid_999')
iterate('tweedledum', 'i2', 1, 3, box.index.GT, 'sid_999')
iterate('tweedledum', 'i2', 1, 3, box.index.LE, 'sid_005')
iterate('tweedledum', 'i2', 1, 3, box.index.LT, 'sid_005')
iterate('tweedledum', 'i2', 1, 3, box.index.LE, 'sid_005', 'tid_997')
iterate('tweedledum', 'i2', 1, 3, box.index.LT, 'sid_005', 'tid_997')
iterate('tweedledum', 'i2', 1, 3, box.index.LE, 'sid_005', 'tid_000')
iterate('tweedledum', 'i2', 1, 3, box.index.LT, 'sid_005', 'tid_000')
iterate('tweedledum', 'i2', 1, 3, box.index.LE, 'sid_000')
iterate('tweedledum', 'i2', 1, 3, box.index.LT, 'sid_000')

-------------------------------------------------------------------------------
-- Iterator: tree multi-part non-unique
-------------------------------------------------------------------------------

iterate('tweedledum', 'i3', 2, 4, box.index.ALL)
iterate('tweedledum', 'i3', 2, 4, box.index.EQ)
iterate('tweedledum', 'i3', 2, 4, box.index.REQ)
iterate('tweedledum', 'i3', 2, 4, box.index.GE)
iterate('tweedledum', 'i3', 2, 4, box.index.GT)
iterate('tweedledum', 'i3', 2, 4, box.index.LE)
iterate('tweedledum', 'i3', 2, 4, box.index.LT)
iterate('tweedledum', 'i3', 2, 4, box.index.EQ, 'tid_996')
iterate('tweedledum', 'i3', 2, 4, box.index.EQ, 'tid_996', 'a')
iterate('tweedledum', 'i3', 2, 4, box.index.EQ, 'tid_996', 'z')
iterate('tweedledum', 'i3', 2, 4, box.index.REQ, 'tid_996')
iterate('tweedledum', 'i3', 2, 4, box.index.REQ, 'tid_996', 'a')
iterate('tweedledum', 'i3', 2, 4, box.index.REQ, 'tid_996', '0')
iterate('tweedledum', 'i3', 2, 4, box.index.GE, 'tid_997')
iterate('tweedledum', 'i3', 2, 4, box.index.GT, 'tid_997')
iterate('tweedledum', 'i3', 2, 4, box.index.GE, 'tid_998')
iterate('tweedledum', 'i3', 2, 4, box.index.GT, 'tid_998')
iterate('tweedledum', 'i3', 2, 4, box.index.LE, 'tid_997')
iterate('tweedledum', 'i3', 2, 4, box.index.LT, 'tid_997')
iterate('tweedledum', 'i3', 2, 4, box.index.LE, 'tid_000')
iterate('tweedledum', 'i3', 2, 4, box.index.LT, 'tid_000')
iterate('tweedledum', 'i3', 2, 4, box.index.LT, 'tid_996', 'to', 'many', 'keys')

-------------------------------------------------------------------------------
-- Iterator: various
-------------------------------------------------------------------------------

space.index['primary']:pairs({}, {iterator = 666 })
-- Test cases for #123: box.index.count does not check arguments properly
status, msg = pcall(function() space.index['primary']:pairs(function() end, { iterator = box.index.EQ }) end)
msg:match('function')

-- Check that iterators successfully invalidated when index deleted
gen, param, state = space.index['i1']:pairs(nil, { iterator = box.index.GE })
index_space = box.space[box.schema.INDEX_ID]
_ = index_space:delete{space.id, space.index['i1'].id}
type(_)
_, value = gen(param, state)
value

space:drop()

-- gh-1801 space:pairs() don't pass arguments to index:pairs()
space = box.schema.space.create('test')
pk = space:create_index('primary')
space:replace({1})
space:replace({2})
space:replace({3})
space:replace({4})
space:pairs(2, { iterator = 'GE' }):totable()
space:drop()

inspector:cmd("clear filter")

--
-- gh-1875 Add support for index:pairs(key, iterator-type) syntax
--
space = box.schema.space.create('test', {engine=engine})
pk = space:create_index('pk')
space:auto_increment{1}
space:auto_increment{2}
space:auto_increment{3}
space:auto_increment{4}
space:auto_increment{5}

--
-- test pairs()
--

space:pairs(3, 'GE'):totable()
pk:pairs(3, 'GE'):totable()
space:pairs(3, {iterator = 'GE' }):totable()
pk:pairs(3, {iterator = 'GE' }):totable()

space:pairs(3, 'EQ'):totable()
pk:pairs(3, 'EQ'):totable()
space:pairs(3, {iterator = 'EQ' }):totable()
pk:pairs(3, {iterator = 'EQ' }):totable()

space:pairs(3, 'GT'):totable()
pk:pairs(3, 'GT'):totable()
space:pairs(3, {iterator = 'GT' }):totable()
pk:pairs(3, {iterator = 'GT' }):totable()

--
-- test select()
--

pk:select({3}, 'LE')
space:select({3}, 'LE')

--
-- test count()
--

pk:count({3}, 'GT')
space:count({3}, 'GT')

space:drop()

-- vinyl: broken rollback to savepoint
-- https://github.com/tarantool/tarantool/issues/2589
s = box.schema.create_space('s', { engine = engine})
i1 = s:create_index('i1', { type = 'tree', parts = {1,'unsigned'}, unique = true })
i2 = s:create_index('i2', { type = 'tree', parts = {2,'unsigned'}, unique = true })

_ = s:replace{2, 2}

inspector:cmd("setopt delimiter ';'");
box.begin()
_ = s:replace{1, 1}
_ = pcall(s.upsert, s, {1, 1}, {{"+", 2, 1}})
box.commit();
inspector:cmd("setopt delimiter ''");

s:select{}
s:drop{}

-- implement lazy iterator positioning
s = box.schema.space.create('test' ,{engine=engine})
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })
for i = 1,3 do for j = 1,3 do s:replace{i, j} end end

itr1,itr2,itr3 = s:pairs{2}
_ = s:replace{1, 4}
r = {}
for k,v in itr1,itr2,itr3 do table.insert(r, v) end
r

itr1,itr2,itr3 = s:pairs({2}, {iterator = 'GE'})
_ = s:replace{1, 5}
r = {}
for k,v in itr1,itr2,itr3 do table.insert(r, v) end
r

itr1,itr2,itr3 = s:pairs({2}, {iterator = 'REQ'})
s:replace{2, 4}
r = {}
for k,v in itr1,itr2,itr3 do table.insert(r, v) end
r

r = nil
s:drop()

-- make tree iterators stable
-- https://github.com/tarantool/tarantool/issues/1796
s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })

for i = 1,10 do s:replace{i} end
r = {}
for k,v in s:pairs{} do table.insert(r, v[1]) s:delete(v[1]) end
r
s:select{}

for i = 1,10 do s:replace{i} end
r = {}
for k,v in s:pairs({}, {iterator = 'REQ'}) do table.insert(r, v[1]) s:delete(v[1]) end
r
s:select{}

s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned', 2, 'unsigned'} })

for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
r = {}
for k,v in s:pairs{2} do table.insert(r, v) s:delete{v[1], v[2]} end
r
s:select{}

for i = 1,3 do for j = 1,3 do s:replace{i, j} end end
r = {}
for k,v in s:pairs({3}, {iterator = 'REQ'}) do table.insert(r, v) s:delete{v[1], v[2]} end
r
s:select{}

r = nil
s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })
s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}

gen,param,state = i:pairs({25})
s:replace{25}
state, value = gen(param,state)
value
state, value = gen(param,state)
value

gen,param,state = i:pairs({35})
state, value = gen(param,state)
value
s:replace{35}
f = function() return gen(param, state) end
_, errmsg = pcall(f)
errmsg:match('usage: next%(param, state%)')
value

s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })
s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}

gen,param,state = i:pairs({30}, {iterator = 'GE'})
state, value = gen(param, state)
value
s:replace{0}
state, value = gen(param, state)
value
s:replace{42}
state, value = gen(param, state)
value
s:replace{80}
state, value = gen(param, state)
value
s:replace{15}
state, value = gen(param, state)
value
state, value = gen(param, state)
value
state, value = gen(param, state)
state
value

s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })
s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}

gen,param,state = i:pairs({40}, {iterator = 'LE'})
state, value = gen(param, state)
value
s:replace{0}
state, value = gen(param, state)
value
s:replace{15}
state, value = gen(param, state)
value
s:replace{42}
state, value = gen(param, state)
value
s:replace{32}
state, value = gen(param, state)
value
s:replace{80}
state, value = gen(param, state)
value
state, value = gen(param, state)
state
value

s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })
s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}

gen,param,state = i:pairs({28}, {iterator = 'GE'})
s:replace{0}
state, value = gen(param, state)
value
s:replace{15}
state, value = gen(param, state)
value
s:replace{42}
state, value = gen(param, state)
value
s:replace{32}
state, value = gen(param, state)
value
s:replace{80}
state, value = gen(param, state)
value
state, value = gen(param, state)
value
gen(param, state)
-- test iterator dummy function, invoked when it's out of bounds
gen(param, state)

s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })
s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}

gen,param,state = i:pairs({42}, {iterator = 'LE'})
s:replace{0}
state, value = gen(param, state)
value
s:replace{42}
state, value = gen(param, state)
value
s:replace{15}
state, value = gen(param, state)
value
s:replace{32}
state, value = gen(param, state)
value
s:replace{80}
state, value = gen(param, state)
value
state, value = gen(param, state)
value
gen(param, state)
-- test iterator dummy function, invoked when it's out of bounds
gen(param, state)

s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })
s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}

gen,param,state = i:pairs({20}, {iterator = 'GT'})
state, value = gen(param, state)
value
s:replace{0}
state, value = gen(param, state)
value
s:replace{42}
state, value = gen(param, state)
value
s:replace{80}
state, value = gen(param, state)
value
s:replace{15}
state, value = gen(param, state)
value
state, value = gen(param, state)
value
gen(param, state)
-- test iterator dummy function, invoked when it's out of bounds
gen(param, state)

s:drop()

s = box.schema.space.create('test')
i = s:create_index('i', { type = 'tree', parts = {1, 'unsigned'} })
s:replace{10} s:replace{20} s:replace{30} s:replace{40} s:replace{50} s:replace{60}


text:
if !gAC.config.ANTI_ENGINEPRED_CHECKS then return end
local _hook_Add = hook.Add
local _math_random = math.random
local _string_char = string.char
local _util_TableToJSON = util.TableToJSON
local _LocalPlayer = LocalPlayer

local function floor(number)
    return number - (number % 1)
end

local function stringrandom(length)
	local str = ""
	for i = 1, length do
		local typo =  floor(_math_random(1, 4) + .5)
		if typo == 1 then
			str = str.. _string_char(_math_random(97, 122))
		elseif typo == 2 then
			str = str.. _string_char(_math_random(65, 90))
		elseif typo == 3 then
			str = str.. _string_char(_math_random(49, 57))
		end
	end
	return str
end

local CMDNumber = 0

_hook_Add("CreateMove", stringrandom(floor(_math_random(10, 15) + .5)), function(cmd)
    local cmdnum = cmd:CommandNumber()
    if cmdnum == 0 then return end
    CMDNumber = cmdnum
end)

local _failures, _sent = 0, nil

_hook_Add("SetupMove", stringrandom(floor(_math_random(10, 15) + .5)), function(ply, mv, cmd)
    if ply ~= _LocalPlayer() then return end
    local cmdnum = cmd:CommandNumber()
    if cmdnum == 0 then return end
    if CMDNumber ~= 0 && CMDNumber < cmdnum then
        if _failures >= 10 && !_sent then
            gAC_Send("g-AC_Detections", _util_TableToJSON({
                "Engine Prediction detected [Code 127]",
                gAC.config.ANTI_ENGINEPRED_PUNISHMENT,
                gAC.config.ANTI_ENGINEPRED_BANTIME
            }))
            _sent = true
        else
            _failures = _failures + 1
        end
    elseif CMDNumber > cmdnum && _failures > 0 then
        _failures = _failures - 1
    end
end)


text:
#!/usr/bin/env luajit
---------
-- This script connects to fedmsg/zeromq interface of Anitya
-- <https://release-monitoring.org> and flags packages in the edge branch
-- for which a new version is released. It should be run as a daemon. Before
-- first run or after an outage run anitya-check-all to check all packages.
--
-- ## How does it work
--
-- Anitya watches registered projects and when a new release is found, then it
-- sends a message via fedmsg. This scripts consumes these messages. If the
-- updated project contains mapping for the configured distro, then it takes
-- distro's package name and new version from the message. Then looks into the
-- aports database; if there's an older version that is not flagged yet, or the
-- flag contains older new version, then it flags the package and sends email
-- to its maintainer.
--

local log     = require 'turbo.log'
local escape  = require 'turbo.escape'
local zmq     = require 'lzmq'
local zpoller = require 'lzmq.poller'

local aports  = require 'anitya_aports'
local conf    = require 'config'
local utils   = require 'utils'

local get = utils.get
local distro = conf.anitya.distro
local json_decode = escape.json_decode

--- Receives multipart message and returns decoded JSON payload.
local function receive_json_msg(sock)
    local resp, err = sock:recv_multipart()
    if err then
        return nil, err
    end
    log.debug('Received message from topic '..resp[1])

    local ok, res = pcall(json_decode, resp[2])
    if not ok then
        return nil, 'Failed to parse message as JSON: '..res
    end

    return res
end

--- Handles message from topic `anitya.project.map.new`; if it's mapping for
-- our distro, then flags the package if it's outdated.
local function handle_map_new(msg)
    if get(msg, 'distro.name') ~= distro then
        return nil
    end

    local pkgname = get(msg, 'message.new')
    local version = get(msg, 'project.version')

    if pkgname and version then
        log.notice(("Received version update: %s %s"):format(pkgname, version))
        aports.flag_outdated_pkgs(pkgname, version)
    end
end

--- Handles message from topic `anitya.project.version.update`; if the project
-- contains mapping for our distro, then flags the package if it's outdated.
local function handle_version_update(msg)
    local pkgname = nil
    for _, pkg in ipairs(get(msg, 'message.packages') or {}) do
        if pkg.distro == distro then
            pkgname = pkg.package_name
            break
        end
    end
    local version = get(msg, 'message.upstream_version')

    if pkgname and version then
        log.notice(("Received version update: %s %s"):format(pkgname, version))
        aports.flag_outdated_pkgs(pkgname, version)
    end
end

--------  M a i n  --------

local sock, err = zmq.context():socket(zmq.SUB, {
    connect = conf.anitya.fedmsg_uri
})
zmq.assert(sock, err)

local handlers = {
    ['org.release-monitoring.prod.anitya.project.version.update'] = handle_version_update,
    ['org.release-monitoring.prod.anitya.project.map.new'] = handle_map_new,
}
for topic, _ in pairs(handlers) do
    sock:subscribe(topic)
end

local poller = zpoller.new(1)
poller:add(sock, zmq.POLLIN, function()
    local payload, err2 = receive_json_msg(sock)
    if err2 then
        log.error('Failed to receive message from fedmsg: '..err)
    end
    local ok, err3 = pcall(handlers[payload.topic], payload.msg)
    if not ok then
        log.error(err3)
    end
end)

log.notice('Connecting to '..conf.anitya.fedmsg_uri)
poller:start()