import time

space = "    "
resource_dic = {"Stone": 0, "Wood": 0, "Thatch": 0, "Fungal Wood": 0, "CP": 0, "Fiber": 0}
item_dic = {
#Thatch
"Sloped Thatch Roofs": 0, #add
"Sloped Thatch Walls Right": 0, #add
"Sloped Thatch Walls Left": 0, #add
"Thatch Doors": 0, #add
"Thatch Roofs": 0, #add
"Thatch Door Frames": 0, #add
"Thatch Walls": 0, #add
"Thatch Foundations": 0, #add
#Wood
"Wood Staircases": 0, #add
"Wood Railings": 0, #add
"Wood Fence Foundations": 0, #add
"Wood Ramps": 0, #add
"Sloped Wood Roofs": 0, #add
"Sloped Wood Walls Left": 0, #add
"Sloped Wood Walls Right": 0, #add
"Dinosaur Gates": 0, #add
"Dinosaur Gateways": 0, #add
"Wood Catwalks": 0, #add
"Wood Pillars": 0, #add
"Wood Ladders": 0, #add
"Wood Trapdoors": 0, #add
"Wood Hatch Frames": 0, #add
"Wood Windows": 0, #add
"Wood Window Frames": 0, #add
"Wood Doors": 0, #add
"Wood Ceilings": 0, #add
"Wood Door Frames": 0, #add
"Wood Walls": 0, #add
"Wood Foundations": 0, #add
#Stone
"Stone Staircases": 0, #add
"Stone Railings": 0, #add
"Stone Fence Foundations": 0, #add
"Behemoth Reinforced Dinosaur Gates": 0, #add
"Behemoth Stone Dinosaur Gateways": 0, #add
"Sloped Stone Roofs": 0, #add
"Sloped Stone Walls Left": 0, #add
"Sloped Stone Walls Right": 0, #add
"Reinforced Dinosaur Gates": 0, #add
"Stone Dinosaur Gateways": 0,
"Stone Pillars": 0, #add
"Reinforced Trapdoors": 0, #add
"Stone Hatch Frames": 0, #add
"Stone Window Frames": 0, #add
"Reinforced Windows": 0, #add
"Reinforced Wooden Doors": 0, #add
"Stone Ceilings": 0,
"Stone Door Frames": 0,
"Stone Walls": 0,
"Stone Foundations": 0,
#Green House
"Sloped Greenhouse Walls Right": 0, #add
"Sloped Greenhouse Walls Left": 0, #add
"Sloped Greenhouse Roofs": 0, #add
"Greenhouse Windows": 0, #add
"Greenhouse Doors": 0, #add
"Greenhouse Ceilings": 0, #add
"Greenhouse Door Frames": 0, #add
"Greenhouse Walls": 0, #add
#Metal
"Metal Staircases": 0, #add
"Metal Railings": 0, #add
"Metal Fence Foundations": 0, #add
"Behemoth Gates": 0, #add
"Behemoth Gateways": 0, #add
"Metal Ramps": 0, #add
"Sloped Metal Roofs": 0, #add
"Sloped Metal Walls Left": 0, #add
"Sloped Metal Walls Right": 0, #add
"Metal Dinosaur Gates": 0, #add
"Metal Dinosaur Gateways": 0, #add
"Metal Catwalks": 0, #add
"Metal Pillars": 0, #add
"Metal Ladders": 0, #add
"Metal Trapdoors": 0, #add
"Metal Hatch Frames": 0, #add
"Metal Window Frames": 0, #add
"Metal Windows": 0, #add
"Metal Doors": 0, #add
"Metal Ceilings": 0, #add
"Metal Door Frames": 0, #add
"Metal Walls": 0, #add
"Metal Foundations": 0, #add
#Tek
"Vacuum Compartment Moonpool": 0, #add
"Vacuum Compartment": 0, #add
"Tek Staircases": 0, #add
"Tek Railings": 0, #add
"Tek Fence Foundations": 0, #add
"Behemoth Tek Gates": 0, #add
"Behemoth Tek Gateways": 0, #add
"Tek Ramps": 0, #add
"Sloped Tek Roofs": 0, #add
"Sloped Tek Walls Left": 0, #add
"Sloped Tek Walls Right": 0, #add
"Tek Dinosaur Gates": 0, #add
"Tek Dinosaur Gateways": 0, #add
"Tek Catwalks": 0, #add
"Tek Pillars": 0, #add
"Tek Ladders": 0, #add
"Tek Trapdoors": 0, #add
"Tek Hatch Frames": 0, #add
"Tek Window Frames": 0, #add
"Tek Windows": 0, #add
"Tek Doors": 0, #add
"Tek Ceilings": 0, #add
"Tek Door Frames": 0, #add
"Tek Walls": 0, #add
"Tek Foundations": 0, #add
#Storage
"Storage Boxes": 0, #add
"Large Storage Boxes": 0, #add
"Water Tanks": 0, #add
"Metal Water Reservoirs": 0, #add
"Feeding Trough": 0, #add
"Bookshelfs": 0, #add
"Vaults": 0, #add
"Preserving Bins": 0, #add
"Refrigerators": 0, #add
"Vessels": 0, #add
"Gas Collectors": 0, #add
#Crafting
"Campfires": 0, #add
"Mortar And Pestles": 0, #add
"Cooking Pots": 0, #add
"Refining Forges": 0, #add
"Smithys": 0, #add
"Beer Barrels": 0, #add
"Fabricators": 0, #add
"Industrial Grills": 0, #add
"Industrial Grinders": 0, #add
"Industrial Forges": 0, #add
"Chemistry Benchs": 0, #add
"Industrial Cookers": 0, #add
"Tek Replicators": 0, #add
#Farming
"Small Crop Plots": 0, #add
"Medium Crop Plots": 0, #add
"Large Crop Plots": 0, #add
"Compost Bins": 0, #add
"Stone Irrigation Pipes - Inclined": 0, #add
"Stone Irrigation Pipes - Intersection": 0, #add
"Stone Irrigation Pipes - Straight": 0, #add
"Stone Irrigation Pipes - Tap": 0, #add
"Stone Irrigation Pipes - Vertical": 0 #add
}

#Fill out Classes with accurate resource costs for all of the above dictionary entries.

def help():
    print("Hello this is an application made by Milkman65. If you would like to contribute any ideas or code to this project please check out my github(https://github.com/Milkman65).")
    print(" ")
    print("Basic use of this application requires the user to type the assigned value(right) of a topic(left) as seen on the UI.")
    print("As an example to access the Stone Structure Menu if the UI looks like this 'Stone: 1' you would need to enter the input '1'.")
    print(" ")
    input("To get back to the main menu type anything and press enter: ")
    mainMenu()

def quickIn(param): #makes coding in inputs less space consuming
    if param ==  "select":
        amount = input("Select a option: ")
        return amount
    else:
        amount = input("How many do you want?: ")
        return amount

def totalResources(): #prints total amount of resources needed
    print("|" * 70)
    print("Total Resources and Items")
    print(" ")
    for i in resource_dic: #prints a key and its value if it isn't equal to 0
        if resource_dic[i] <= 0:
            continue
        else:
            print(i, resource_dic[i])

def totalStructures():
    print("Here is a list of the total amount of structures you've calculated")
    print()
    for i in item_dic: #prints a key and its value if it isn't equal to 0
        if item_dic[i] <= 0:
            continue
        else:
            print(i, item_dic[i])
    print()
    input("Enter anything to continue: ")

def mainMenu():
    print("x" * 70)
    print("{}MAIN MENU{}".format(space * 4, space * 4))
    print()
    print("Thatch: 1{}Wood: 2{}Stone: 3{}Greenhouse: 4".format(space,space,space))
    print()
    print("Metal: 5{}Tek: 6{}Storage: 7{}Crafting: 8".format(space,space,space))
    print()
    print("Farming: 9")
    print()
    print("Exit: 0{} Calculated: info{} Help: help".format(space,space))
    print()
    totalResources() #prints total amount of resources
    choice = quickIn("select")
    #error checking
    if choice.upper() == "INFO":
        totalStructures()
        mainMenu()
    elif choice.upper() == "HELP":
        help()
    elif choice.isdigit() == False:
        print("You must choose a number, Reloading")
        time.sleep(3)
        mainMenu()
    elif int(choice) > 9 or int(choice) < 0:
        print("Input not in valid range, Reloading")
        time.sleep(3)
        mainMenu()
    #menu system
    elif int(choice) == 0:
        print("Thanks for using me :)")
        time.sleep(3)
        exit()
    elif int(choice) == 1: #Thatch
        thatchMenu()
    elif int(choice) == 2: #Wood
        woodMenu()
    elif int(choice) == 3: #Stone
        stoneMenu()
    elif int(choice) == 4: #Greenhouse
        greenhouseMenu()
    elif int(choice) == 5: #Metal
        metalMenu()
    elif int(choice) == 6: #Tek
        tekMenu()
    elif int(choice) == 7: #Storage
        storageMenu()
    elif int(choice) == 8: #Crafting
        craftingMenu()
    elif int(choice) == 9: #Farming
        farmingMenu()
    else:
        print("All 'if' options exhausted, Reloading")
        time.sleep(3)
        mainMenu()

def thatchMenu():
    global resource_dic
    print("This is thatch")

def woodMenu():
    global resource_dic
    print("This is wood")

def stoneMenu():
    global resource_dic
    print("x" * 70)
    print("{}STONE{}".format(space * 4, space * 4))
    print()
    print("Foundation: 1{}Wall: 2{}Ceiling: 3{}Door Frame: 4".format(space,space,space))
    print()
    print("Dino Gateway: 5{}Reinforced Door: 6".format(space))
    print()
    print("Back: 0{}Calculated: info".format(space))
    print()
    totalResources() #prints total amount of resources
    choice = quickIn("select")
    #error checking
    if choice.upper() == "INFO":
        totalStructures()
        stoneMenu()
    elif choice.isdigit() == False:
        print("You must choose a valid option, Reloading")
        time.sleep(3)
        stoneMenu()
    elif int(choice) > 6 or int(choice) < 0:
        print("Input is not recognized, Reloading")
        time.sleep(3)
        stoneMenu()
    #menu system
    elif int(choice) == 0: #back
        mainMenu()
    elif int(choice) == 1: #Stone Foundation
        amount = int(quickIn(None))
        StoneStruct.stoneFoundation(amount)
        stoneMenu()
    elif int(choice) == 2: #Stone Wall
        amount = int(quickIn(None))
        StoneStruct.stoneWall(amount)
        stoneMenu()
    elif int(choice) == 3: #Stone Ceiling
        amount = int(quickIn(None))
        StoneStruct.stoneCeiling(amount)
        stoneMenu()
    elif int(choice) == 4: #Stone Door Frame
        amount = int(quickIn(None))
        StoneStruct.stoneDoorframe(amount)
        stoneMenu()
    elif int(choice) == 5: #DinoGateway
        amount = int(quickIn(None))
        StoneStruct.stoneDinogateway(amount)
        stoneMenu()
    elif int(choice) == 6: #ReinforcedDoor
        amount = int(quickIn(None))
    else:
        print("All 'if' options exhausted, Reloading")
        time.sleep(3)
        stoneMenu()

def greenhouseMenu():
    global resource_dic
    print("This is greenhouse")

def tekMenu():
    global resource_dic
    print("This is tek")

def storageMenu():
    global resource_dic
    print("This is storage")

def craftingMenu():
    global resource_dic
    print("This is crafting")

def farmingMenu():
    global resource_dic
    print("This is farming")

class ThatchStruct(object): #Class that calculates all the values of thatch structs
    def __init__(self, amount):
        self.amount = amount

class WoodStruct(object): #Class that calculates all the values of wood structs
    def __init__(self, amount):
        self.amount = amount

class StoneStruct(object): #Class that calculates all the values of stone structs
    def __init__(self, amount):
        self.amount = amount

    def stoneFoundation(amount):
        global resource_dic
        resource_dic["Stone"] += 80 * amount
        resource_dic["Wood"] += 40 * amount
        resource_dic["Thatch"] += 30 * amount
        item_dic["Stone Foundations"] += 1 * amount

    def stoneWall(amount):
        global resource_dic
        resource_dic["Stone"] += 40 * amount
        resource_dic["Wood"] += 20 * amount
        resource_dic["Thatch"] += 15 * amount
        item_dic["Stone Walls"] += 1 * amount

    def stoneCeiling(amount):
        global resource_dic
        resource_dic["Stone"] += 60 * amount
        resource_dic["Wood"] += 30 * amount
        resource_dic["Thatch"] += 20 * amount
        item_dic["Stone Ceilings"] += 1 * amount

    def stoneDoorframe(amount):
        global resource_dic
        resource_dic["Stone"] += 30 * amount
        resource_dic["Wood"] += 16 * amount
        resource_dic["Thatch"] += 12 * amount
        item_dic["Stone Door Frames"] += 1 * amount

    def stoneDinogateway(amount):
        global resource_dic
        resource_dic["Stone"] += 280 * amount
        resource_dic["Wood"] += 140 * amount
        resource_dic["Thatch"] += 100 * amount

    def stoneStaircase(amount):
        global resource_dic
        resource_dic["Stone"] += 200 * amount
        resource_dic["Wood"] += 100 * amount
        resource_dic["Thatch"] += 75 * amount

class GreenhouseStruct(object): #Class that calculates all the values of greenhouse structs
    def __init__(self, amount):
        self.amount = amount

class MetalStruct(object): #Class that calculates all the values of metal structs
    def __init__(self, amount):
        self.amount = amount

class TekStruct(object): #Class that calculates all the values of tek structs
    def __init__(self, amount):
        self.amount = amount

class StorageStruct(object): #Class that calculates all the values of storage structs
    def __init__(self, amount):
        self.amount = amount

class CraftingStruct(object): #Class that calculates all the values of crafting structs
    def __init__(self, amount):
        self.amount = amount

class FarmingStruct(object): #Class that calculates all the values of farming structs
    def __init__(self, amount):
        self.amount = amount

mainMenu()


# -*-coding:Utf-8 -*

# Copyright (c) 2012 LE GOFF Vincent
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the copyright holder nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Fichier contenant la classe ScriptPNJ détaillée plus bas."""

from primaires.scripting.script import Script

class ScriptPNJ(Script):

    """Script et évènements propre aux PNJ.

    C'est dans cette classe que sont construits les évènements du scripting
    des PNJ. Il est ainsi plus facile à modifier si vous souhaitez
    rajouter un évènement.

    """

    def init(self):
        """Initialisation du script"""
        # Evénement entre
        evt_entre = self.creer_evenement("entre")
        evt_entre.aide_courte = "le PNJ entre quelque part"
        evt_entre.aide_longue = \
            "Cet évènement est appelé quand le PNJ entre dans une salle, " \
            "quelque soit sa salle de provenance et " \
            "de destination."

        # Configuration des variables de l'évènement entre
        var_depuis = evt_entre.ajouter_variable("depuis", "str")
        var_depuis.aide = "la direction d'où vient le PNJ"
        var_salle = evt_entre.ajouter_variable("salle", "Salle")
        var_salle.aide = "la salle actuelle du PNJ"

        # Evénement sort
        evt_sort = self.creer_evenement("sort")
        evt_sort.aide_courte = "le PNJ quitte une salle"
        evt_sort.aide_longue = \
            "Cet évènement est appelé quand le PNJ quitte une " \
            "salle via un déplacement standard (en entrant un nom de " \
            "sortie). Le déplacement par |cmd|goto|ff| n'appelle " \
            "pas cet évènement. Cependant, un déplacement par script " \
            "l'appelle (faire attention aux boucles infinies potentielles)."

        # Configuration des variables de l'évènement sort
        var_vers = evt_sort.ajouter_variable("vers", "str")
        var_vers.aide = "la direction empruntée par le PNJ"
        var_salle = evt_sort.ajouter_variable("salle", "Salle")
        var_salle.aide = "la salle quittée par le PNJ"
        var_destination = evt_sort.ajouter_variable("destination", "Salle")
        var_destination.aide = "la salle où se rend le PNJ"

        # Evénement arrive
        evt_arrive = self.creer_evenement("arrive")
        evt_arrive.aide_courte = "un personnage arrive dans la salle du PNJ"
        evt_arrive.aide_longue = \
            "Cet évènement est appelé quand un personnage arrive dans la " \
            "salle où se trouve le PNJ."

        # Configuration des variables de l'évènement arrive
        var_depuis = evt_arrive.ajouter_variable("depuis", "str")
        var_depuis.aide = "la direction d'où vient le personnage"
        var_personnage = evt_arrive.ajouter_variable("personnage",
                "Personnage")
        var_personnage.aide = "le personnage qui arrive"
        var_salle = evt_arrive.ajouter_variable("salle", "Salle")
        var_salle.aide = "la salle d'où vient le personnage"

        # Evénement part
        evt_part = self.creer_evenement("part")
        evt_part.aide_courte = "un personnage part de la salle du PNJ"
        evt_part.aide_longue = \
            "Cet évènement est appelé quand un personnage quitte la salle " \
            "où se trouve le PNJ."

        # Configuration des variables de l'évènement part
        var_vers = evt_part.ajouter_variable("vers", "str")
        var_vers.aide = "la direction empruntée par le personnage"
        var_destination = evt_part.ajouter_variable("destination", "Salle")
        var_destination.aide = "la salle où se rend le personnage"
        var_personnage = evt_part.ajouter_variable("personnage", "Personnage")
        var_personnage.aide = "le personnage qui arrive"

        # Evénement regarde
        evt_regarde = self.creer_evenement("regarde")
        evt_reg_avant = evt_regarde.creer_evenement("avant")
        evt_reg_apres = evt_regarde.creer_evenement("après")
        evt_regarde.aide_courte = "un personnage regarde le PNJ"
        evt_reg_avant.aide_courte = "avant la description du PNJ"
        evt_reg_apres.aide_courte = "après la description du PNJ"
        evt_regarde.aide_longue = \
            "Cet évènement est appelé quand un personnage regarde le PNJ."
        evt_reg_avant.aide_longue = \
            "Cet évènement est appelé avant que la description du PNJ " \
            "ne soit envoyée au personnage le regardant."
        evt_reg_apres.aide_longue = \
            "Cet évènement est appelé après que la description du PNJ " \
            "ait été envoyée au personnage le regardant."

        # Configuration des variables de l'évènement regarde
        var_perso = evt_regarde.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage regardant le PNJ"

        # Evénement discute
        evt_discute = self.creer_evenement("discute")
        evt_discute.aide_courte = "un personnage engage une discussion avec " \
                "le PNJ"
        evt_discute.aide_longue = \
            "Cet évènement est appelé quand un personnage engage une " \
            "discussion avec le PNJ à propos d'un sujet quelconque."

        # Configuration des variables de l'évènement discute
        var_sujet = evt_discute.ajouter_variable("sujet", "str")
        var_sujet.aide = "le sujet de la discussion"
        var_perso = evt_discute.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage regardant le PNJ"

        # Evénement attaque
        evt_attaque = self.creer_evenement("attaqué")
        evt_attaque.aide_courte = "le PNJ est attaqué par un personnage"
        evt_attaque.aide_longue = \
            "Cet évènement est appelé quand le PNJ est attaqué en " \
            "corps-à-corps. Le personnage l'attaquant est stocké dans " \
            "la variable |ent|personnage|ff|."

        # Configuration des variables de l'évènement attaque
        var_perso = evt_attaque.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage attaquant le PNJ"

        # Evénement donne
        evt_donne = self.creer_evenement("donne")
        evt_donne.aide_courte = "un personnage donne un objet au PNJ"
        evt_donne.aide_longue = \
            "Cet évènement est appelé quand un personnage donne un ou " \
            "plusieurs objet(s) au PNJ, à condition que le PNJ puisse " \
            "le(s) prendre."

        # Configuration des variables de l'évènement donne
        var_objet = evt_donne.ajouter_variable("objet", "Objet")
        var_objet.aide = "l'objet donné"
        var_qtt = evt_donne.ajouter_variable("quantite", "int")
        var_qtt.aide = "la quantité remise"
        var_perso = evt_donne.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le donneur"

        # Evénement tick
        evt_tick = self.creer_evenement("tick")
        evt_tick.aide_courte = "le tick du PNJ se déclenche"
        evt_tick.aide_longue = \
            "Cet évènement est appelé quand le tick du PNJ se déclenche " \
            "(toutes les minutes)."

        # Evénement repop
        evt_repop = self.creer_evenement("repop")
        evt_repop.aide_courte = "le PNJ se repop"
        evt_repop.aide_longue = \
            "Cet évènement est appelé quand le PNJ repop, dans deux " \
            "cas : soit quand le MUD se lance (tous les PNJ sont " \
            "repop automatiquement, bien qu'ils gardent la plupart " \
            "de leurs informations) ou quand ils apparaissent dans une " \
            "salle qui a configuré le PNJ en repop."

        # Evénement magasin
        evt_marchand = self.creer_evenement("marchand")
        evt_marchand_ouvre = evt_marchand.creer_evenement("ouvre")
        evt_marchand_ferme = evt_marchand.creer_evenement("ferme")
        evt_marchand.aide_courte = "le vendeur du magasin agit"
        evt_marchand_ouvre.aide_courte = "le magasin ouvre ses portes"
        evt_marchand_ferme.aide_courte = "la magasin ferme ses portes"
        evt_marchand.aide_longue = \
            "Cet évènement est appelé si le PNJ est un vendeur de magasin, " \
            "dans différentes situations qui sont classées en sous-évènements."
        evt_marchand_ouvre.aide_longue = \
            "Cet évènement est appelé quand le magasin ouvre ses portes. Si " \
            "le vendeur n'est pas trouvé dans la salle du magasin, alors " \
            "le premier PNJ modelé sur le prototype indiqué dans le " \
            "magasin est choisi (qu'il soit là où non)."
        evt_marchand_ferme.aide_longue = \
            "Cet évènement est appelé quand le magasin ferme ses portes."

        # Evénement meurt
        evt_meurt = self.creer_evenement("meurt")
        evt_meurt.nom_acteur = "adversaire"
        evt_meurt_avant = evt_meurt.creer_evenement("avant")
        evt_meurt_avant.nom_acteur = "adversaire"
        evt_meurt_apres = evt_meurt.creer_evenement("après")
        evt_meurt_apres.nom_acteur = "adversaire"
        evt_meurt.aide_courte = "le PNJ meurt"
        evt_meurt_avant.aide_courte = "avant la mort du PNJ"
        evt_meurt_apres.aide_courte = "après la mort du PNJ"
        evt_meurt.aide_longue = \
            "Cet évènement est appelé quand le PNJ meurt d'une façon ou " \
            "d'une autre (lors d'un combat, tué par un script, tué à " \
            "proximité ou à distance)."
        evt_meurt_avant.aide_longue = \
            "Cet évènement est appelé avant la mort du PNJ. Le PNJ n'est " \
            "pas encore réellement mort, cependant le message a déjà été " \
            "envoyé aux autres joueurs présents dans la salle."
        evt_meurt_apres.aide_longue = \
            "Cet évènement est appelé quand le PNJ est déjà mort et que " \
            "son cadavre a été créé sur le sol."

        # Configuration des variables de l'évènement meurt et
        # sous-évènements
        var_salle = evt_meurt.ajouter_variable("salle", "Salle")
        var_salle.aide = "la salle où meurt le PNJ"
        var_adv = evt_meurt.ajouter_variable("adversaire", "Personnage")
        var_adv.aide = "l'adversaire du PNJ (si meurt en combat)"

        # Evénement tue
        evt_tue = self.creer_evenement("tue")
        evt_tue.aide_courte = "le PNJ tue un personnage"
        evt_tue.aide_longue = \
            "Cet évènement est appelé quand le PNJ tue (en combat rapproché " \
            "ou non) un autre personnage."

        # Configuration des variables de l'évènement attaque
        var_perso = evt_tue.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage tué par le PNJ"

        # Événement dépece
        evt_depece = self.creer_evenement("dépece")
        evt_depece.aide_courte = "le PNJ est dépecé"
        evt_depece.aide_longue = \
            "Cet évènement est appelé quand le cadavre du PNJ est " \
            "dépecé. Le personnage dépeçant le PNJ se trouve dans " \
            "la variable 'personnage'. Cet évènement est appelé " \
            "après l'envoie du message au personnage."

        # Configuration des variables de l'évènement dépece
        var_perso = evt_depece.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage dépeçant le PNJ"
        var_salle = evt_depece.ajouter_variable("salle", "Salle")
        var_salle.aide = "la salle où est dépecé le PNJ"

        # Événement gagne_niveau
        evt_niveau = self.creer_evenement("gagne_niveau")
        evt_niveau.aide_courte = "le PNJ gagne un niveau"
        evt_niveau.aide_longue = \
            "Cet évènement est appelé quand le PNJ gagne un niveau, " \
            "principal ou secondaire. Pour obtenir le niveau actuel " \
            "du PNJ, vous pouvez utiliser la fonction du même nom."

        # Événement dit
        evt_dit = self.creer_evenement("dit")
        evt_dit.aide_courte = "un personnage dit quelque chose dans la " \
                "salle du PNJ"
        evt_dit.aide_longue = \
            "Cet évènement est appelé quand un personnage dit quelque " \
            "chose dans la salle. Il est appelé après la diffusion " \
            "du message à tous les personnages présents. Notez que, " \
            "si le PNJ parle via commande, son propre script n'est " \
            "pas appelé."

        # Configuration des variables de l'évènement dit
        var_message = evt_dit.ajouter_variable("message", "str")
        var_message.aide = "ce qui est dit par le personnage"
        var_perso = evt_dit.ajouter_variable("personnage", "Personnage")
        var_perso.aide = "le personnage disant quelque chose"
        var_salle = evt_dit.ajouter_variable("salle", "Salle")
        var_salle.aide = "la salle dans laquelle le personnage parle"

        # On ajoute à tous les évènements la variable 'pnj'
        for evt in self.evenements.values():
            if evt.nom != "dépece":
                var_pnj = evt.ajouter_variable("pnj", "PNJ")
                var_pnj.aide = "le PNJ scripté"


import sys
f = open(sys.argv[1],"r")
def binary(n):
    if n > 0:binary(n//2);print(n % 2,end = '')
while True:
    text = f.readline()
    if "\n" == text:continue
    if len(text) == 0:break
    if int(text) == 0:print(0);continue
    binary(int(text));print("")


# -*- coding: utf-8 -*-
"""

Script Name: MainMenu.py
Author: Do Trinh/Jimmy - 3D artist.

Description:

"""
# -------------------------------------------------------------------------------------------------------------
""" Import """

# Python
import sys

# PyQt5
from PyQt5.QtWidgets                import QApplication

# Plm
from appData                        import (__plmWiki__, mainConfig)
from ui.uikits.MenuBar              import MenuBar
from utils                          import data_handler

class MainMenuBar(MenuBar):

    key                     = 'MainMenuBar'
    appInfo = data_handler(filePath=mainConfig)

    def __init__(self, actionManager, parent=None):
        super(MainMenuBar, self).__init__(parent)

        self._parent = parent
        self.actionManger = actionManager
        self.url = __plmWiki__
        self.buildMenu()

    def buildMenu(self):

        self.appMenu = self.build_appMenu()
        self.goMenu = self.build_goMenu()
        self.officeMenu = self.build_officceMenu()
        self.toolMenu = self.build_toolMenu()
        self.devMenu = self.build_devMenu()
        self.libMenu = self.build_livMenu()
        self.helpMenu = self.build_helpMenu()

        for menu in [self.appMenu, self.goMenu, self.officeMenu, self.toolMenu, self.devMenu, self.libMenu,
                     self.helpMenu, self.organisationMenu, self.teamMenu, self.projectMenu]:
            menu.key = '{0}_Menu_{1}'.format(self.key, menu.title())
            menu._name = '{0} Menu {1}'.format(self.key, menu.title())
            self.menus.add(menu.key, menu)

        self.mns = [mn for mn in self.menus.values()]

    def build_helpMenu(self):
        menu = self.addMenu("&Help")
        actions = self.actionManger.helpMenuActions(self.parent)
        self.add_actions(menu, actions[0:2])
        menu.addSeparator()
        self.add_actions(menu, actions[2:5])
        menu.addSeparator()
        self.add_actions(menu, actions[5:7])
        menu.addSeparator()
        self.add_actions(menu, actions[7:])
        return menu

    def build_livMenu(self):
        menu = self.addMenu("&Lib")
        actions = self.actionManger.libMenuActions(self.parent)
        self.add_actions(menu, actions)
        return menu

    def build_devMenu(self):
        menu = self.addMenu("&Dev")
        actions = self.actionManger.devMenuActions(self.parent)
        self.add_actions(menu, actions)
        return menu

    def build_toolMenu(self):
        menu = self.addMenu("&Tools")
        actions = self.actionManger.toolsMenuActions(self.parent)
        self.add_actions(menu, actions[0:7])
        menu.addSeparator()
        self.add_actions(menu, actions[7:])
        return menu

    def build_officceMenu(self):
        menu = self.addMenu("&Office")
        action = self.actionManger.officeMenuActions(self.parent)
        self.add_actions(menu, action)
        return menu

    def build_goMenu(self):
        menu = self.addMenu('&Go')
        actions = self.actionManger.goMenuActions(self.parent)
        self.add_actions(menu, actions)
        return menu

    def build_appMenu(self):
        menu = self.addMenu("&App")
        actions = self.actionManger.appMenuActions(self.parent)
        self.add_actions(menu, actions[0:3])

        menu.addSeparator()
        self.organisationMenu = menu.addMenu("&Organisation")
        orgActions = self.actionManger.orgMenuActions(self.parent)
        self.add_actions(self.organisationMenu, orgActions)

        self.teamMenu = menu.addMenu('&Team')
        teamActions = self.actionManger.teamMenuActions(self.parent)
        self.add_actions(self.teamMenu, teamActions)

        self.projectMenu = menu.addMenu('&Project')
        prjActions = self.actionManger.projectMenuActions(self.parent)
        self.add_actions(self.projectMenu, prjActions)

        menu.addSeparator()
        self.add_actions(menu, actions[3:])
        return menu

    def add_actions(self, menu, actions):
        for action in actions:
            menu.addAction(action)

    def showMenu(self, bool):
        cb = self.sender()
        key = cb.key.replace('CheckBox_', '').replace('Preferences', self.key)
        for k, menu in self.menus.items():
            if key == k:
                menu.setEnabled(bool)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    from ui.uikits.GroupBox import GroupBox
    subMenu = MainMenuBar()
    layout = GroupBox("Main Menu", subMenu, "qmainLayout")
    layout.show()
    app.exec_()
# -------------------------------------------------------------------------------------------------------------
# Created by panda on 11/07/2018 - 12:59 AM
# © 2017 - 2018 DAMGteam. All rights reserved


from django.shortcuts import render
# Create your views here.

from django.http import HttpResponse
from django.template import loader
from .models import Message
import time
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse

def index(request):
    if 'submit' in request.POST:
        # if 'submit' in request.POST:
        user_message = request.POST['user_message']
        user_name = request.POST['user_name']
        if user_message.strip() != '':
            if user_name == '':
                user_name = 'NO_ONE'
            now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            Message.objects.create(message_text=user_message, message_name=user_name, support=0, pub_date=now)
        return HttpResponseRedirect('/board/')

    elif 'msg_id' in request.GET:
        msg_id = request.GET.get('msg_id')
        count = Message.objects.get(id=msg_id).support
        msg = Message.objects.get(id=msg_id)
        msg.support = count + 1
        msg.save()
        return HttpResponseRedirect('/board/')
    else:
        message_list = Message.objects.order_by('-pub_date')
        template = loader.get_template('board/index.html')
        context = {
            'message_list': message_list,
        }
        return HttpResponse(template.render(context, request))


import os
import uuid

import pytest

from src.lightning_app.testing.testing import run_cli

@pytest.mark.cloud
@pytest.mark.skipif(
    os.environ.get("LIGHTNING_BYOC_ROLE_ARN") is None, reason="missing LIGHTNING_BYOC_ROLE_ARN environment variable"
)
@pytest.mark.skipif(
    os.environ.get("LIGHTNING_BYOC_EXTERNAL_ID") is None,
    reason="missing LIGHTNING_BYOC_EXTERNAL_ID environment variable",
)
def test_cluster_lifecycle() -> None:
    role_arn = os.environ.get("LIGHTNING_BYOC_ROLE_ARN", None)
    external_id = os.environ.get("LIGHTNING_BYOC_EXTERNAL_ID", None)
    region = "us-west-2"
    instance_types = "t2.small,t3.small"
    cluster_name = "byoc-%s" % (uuid.uuid4())
    with run_cli(
        [
            "create",
            "cluster",
            cluster_name,
            "--provider",
            "aws",
            "--role-arn",
            role_arn,
            "--external-id",
            external_id,
            "--region",
            region,
            "--instance-types",
            instance_types,
            "--wait",
        ]
    ) as (stdout, stderr):
        assert "success" in stdout, f"stdout: {stdout}\nstderr: {stderr}"

    with run_cli(["list", "clusters"]) as (stdout, stderr):
        assert cluster_name in stdout, f"stdout: {stdout}\nstderr: {stderr}"

    with run_cli(["delete", "cluster", "--force", cluster_name]) as (stdout, stderr):
        assert "success" in stdout, f"stdout: {stdout}\nstderr: {stderr}"

@pytest.mark.cloud
def test_cluster_list() -> None:
    with run_cli(["list", "clusters"]) as (stdout, stderr):
        assert "lightning-cloud" in stdout, f"stdout: {stdout}\nstderr: {stderr}"


import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import math

x0 = 4
N = 741

t = np.arange(0, 30, 0.1)

def k(t):
    g = 0.000035
    return g

def p(t):
    v = 0.98
    return v

def f(x, t):
    xd = (k(t) + p(t)*x)*(N - x)
    return xd

x = odeint(f, x0, t)

plt.plot(t, x)
plt.grid('axis = "both"')


from cv2 import cv2
import numpy as np
import matplotlib.pyplot as plt
import os

def intersection(line1, line2):
    a_x1, a_y1,a_x2,a_y2 = line1
    b_x1, b_y1,b_x2,b_y2 = line2
    A1 = a_y2 - a_y1
    B1 = a_x1 - a_x2
    C1 = a_y1 * (a_x2 -a_x1)  - a_x1 * (a_y2 - a_y1)
    C1 *= -1
    A2 = b_y2 - b_y1
    B2 = b_x1 - b_x2
    C2 = b_y1 * (b_x2 -b_x1)  - b_x1 * (b_y2 - b_y1)
    C2 *= -1
    determ = A1*B2 - A2*B1

    return [(C1*B2 - C2*B1) // determ, ( C2*A1-C1*A2 ) // determ]

def get_lines(edges, thresh=220, gap=550):
    lines = cv2.HoughLinesP(edges,1,np.pi/180,thresh,maxLineGap=gap)
    length = len(lines)
    # print("number of lines :", length)
    format_line = np.zeros([length,4],dtype='int32')
    for i in range(length):
        format_line[i] =lines[i][0]
    return format_line

def vertical_and_horizontal(lines):
    ver, hor = [], []
    for line in lines:
        x1, y1, x2, y2 = line
        if abs(x2 - x1) > abs(y2 - y1):
            hor.append(line)
        else:
            ver.append(line)

    ver = np.array(sorted(ver, key=lambda l: min(l[0], l[2])))
    hor = np.array(sorted(hor, key=lambda l: min(l[1], l[3])))
    return ver, hor

def line_filter(lines):
    """:returns top, bottom, left, right"""
    ver, hor = vertical_and_horizontal(lines)

    return hor[0], hor[-1], ver[0], ver[-1]

def get_matrix(lines):
    top, bottom, left, right = line_filter(lines)

    inter = np.float32([
        intersection(top, left),
        intersection(top, right),
        intersection(bottom, left),
        intersection(bottom, right)
    ])

    width = points_distance(inter[0], inter[1])
    height = points_distance(inter[0], inter[2])
    size = np.float32([[0,0], [width, 0], [0,height], [width,height]])

    return cv2.getPerspectiveTransform(inter, size), (width, height)

def points_distance(p1, p2):
    return int(np.linalg.norm([p1[0]-p2[0], p1[1]-p2[1]]))

def before(image):
    gray = image
    # gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    kernel = np.ones((5, 5), np.uint8)
    gray_after_kernel = cv2.erode(gray, kernel,iterations=3)
    edges = cv2.Canny(gray_after_kernel,75,150)
    return edges

def after(image):
    gray = image
    # gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    gray = cv2.blur(gray,(5,5))
    filter = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
    gray_after_filter = cv2.filter2D(gray,-1,filter)
    edges = cv2.Canny(gray_after_filter,75,150)

    #make lines on edgers smooth
    blur = cv2.GaussianBlur( edges, (5,5), 0)
    smooth = cv2.addWeighted( blur, 1.5, edges, -0.5, 0)
    return smooth

def img_read(path):
    image = cv2.imread(path)
    return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

def draw_lines(image, lines):
    lined_img = image.copy()

    for line in lines:
        x1,y1,x2,y2 = line
        cv2.line(lined_img,(x1,y1),(x2,y2),(0,255,255),5)
    return lined_img

def medium_in_histogram(image_gray) -> int:
    return int(image_gray.mean())

def make_threshold(image_gray):
    shape = np.max(image_gray.shape)//80
    shape += not shape % 2

    blur = cv2.GaussianBlur(image_gray, (shape,shape), shape//3)

    medium = medium_in_histogram(image_gray)
    _, threshold = cv2.threshold(blur, medium, 255, cv2.THRESH_BINARY)
    kernal = np.ones((15, 15), np.uint8)
    threshold = cv2.erode(threshold, kernal)

    return after(threshold)

def biggest_contour(image_threshold, with_convex=False):
    contours, _ = cv2.findContours(image_threshold,
                                   cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

    contour_sizes = ((cv2.contourArea(contour), contour) for contour in contours)
    cnt = max(contour_sizes, key=lambda _: _[0])[1]

    if with_convex:
        cnt = cv2.convexHull(cnt)

    return cnt

def rotate_to_horizontal(image):
    height, width = image.shape[:2]
    if width < height:
        image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)
    return image.copy()

def is_gray(image):
    return len(image.shape) < 3 or image.shape[2] == 1

def concatenate_images(*images):
    height, width = images[0].shape[:2]

    method = cv2.hconcat if width < height else cv2.vconcat

    images = list(images)
    for i, image in enumerate(images):
        if is_gray(image):
            images[i] = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB, dst=image)

    return method(images)

def is_rect_contour(contour, max_error_rate=0.09):
    rect = np.int0(cv2.boxPoints(cv2.minAreaRect(contour)))
    x = cv2.contourArea(rect)
    x = (x - cv2.contourArea(contour))/x

    return x < max_error_rate

class Cropper:
    def __init__(self, image):
        self.img = image

        try:
            self.gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        except cv2.error:
            self.gray = image

    def crop(self, show_result=False, show_histogram=False, show_lines=False):
        imgs_to_show = []

        th = make_threshold(self.gray)

        show_cnt = self.img.copy() if show_lines else None

        cnt_img = np.zeros(th.shape[:2], np.uint8)
        # cnt_img = biggest_contour(th) # todo: hull or not hull
        cnt = biggest_contour(th, with_convex=True)
        if not is_rect_contour(cnt):
            return None

        cv2.drawContours(cnt_img, [cnt], 0, 255, 5)

        lines = get_lines(cnt_img)
        try:
            matrix, size = get_matrix(lines)
        except IndexError:
            self.show(th, cnt_img, draw_lines(self.img, lines))
            return self.img

        cropped = cv2.warpPerspective(self.img, matrix, size)
        cropped = rotate_to_horizontal(cropped)

        if show_lines:
            concat = concatenate_images(th, show_cnt, draw_lines(self.img, lines))
            imgs_to_show.append(concat)
        if show_result:
            self.show(*imgs_to_show, cropped, histogram=show_histogram)

        return cropped

    def test(self):
        plt.hist(self.gray.ravel(),256,[0,256])
        plt.axvline(self.gray.mean(), color='k', linestyle='dashed', linewidth=1)
        plt.show()
        th = make_threshold(self.gray)

        self.show(th)
        # threshold = make_threshold(self.gray)
        # image_con = biggest_contour(threshold)

        # self.show(histogram, threshold, image_con)

    def show(self, *images, histogram=False):
        if histogram:
            self.show_gray_hist()

        fig=plt.figure(figsize=(25, 25))
        rows = len(images) + 1
        fig.add_subplot(rows, 1, 1)
        plt.imshow(self.img)

        for i, image in enumerate(images, 2):
            fig.add_subplot(rows, 1, i)
            plt.imshow(image, cmap='gray')
        plt.show()

    def show_gray_hist(self):
        plt.hist(self.gray.ravel(),256,[0,256])
        medium = medium_in_histogram(self.gray)
        plt.axvline(medium, color='k', linestyle='dashed', linewidth=1)
        plt.show()

def crop(image):
    return Cropper(image).crop()


from django.conf.urls import url
from . import views

app_name = 'cricket_center'

urlpatterns = [
    url(r'view_contest/(?P<match_slug>[\w]+)/(?P<contest_slug>[\w]+)', views.ContestsViews, name='view_contest'),
    url(r'capture_payment', views.CapturePayment, name='capture_payment'),
    url(r'^$', views.CricketCenterIndexView, name="cricket_center"),
    url(r'match/(?P<match_slug>[\w]+)', views.SingleMatchView, name='match'),
    url(r'rankings/(?P<match_slug>[\w]+)/(?P<contest_slug>[\w]+)', views.ContestsRankings, name='contest_ranking'),
    url(r'create_team/(?P<match_slug>[\w]+)', views.CreateTeamView, name='create_team'),
    url(r'(?P<match_slug>[\w]+)/(?P<contest_slug>[\w]+)/join_now_contest', views.ContestJoinNow, name='join_now_contest'),
    url(r'^pay_and_join/', views.PayAndJoin, name='pay_and_join'),
    url(r'^proceed_to_pay/', views.ProceedToPay, name='proceed_to_pay'),
    url(r'contest_joined/(?P<match_slug>[\w]+)', views.JoinedContests, name='joined_contests'),
    url(r'all_teams/(?P<match_slug>[\w]+)', views.MyTeams, name='my_teams'),
    url(r'edit/(?P<team>[\w]+)/(?P<match_slug>[\w]+)', views.TeamEdit, name='team_edit'),
    url(r'get/(?P<team>[\w]+)/(?P<match_slug>[\w]+)', views.GetTeamInfo, name='get_team_info'),
    url(r'match_live/(?P<match_slug>[\w]+)', views.SingleMatchLiveView, name='match_live'),
    url(r'ranking_team_preview/(?P<team_no>[\w]+)/(?P<team_name>[\w]+)/(?P<match_slug>[\w]+)', views.RankingPreviewTeam, name='ranking_team_preview'),
    url(r'team_preview/(?P<team>[\w]+)/(?P<match_slug>[\w]+)', views.PreviewTeam, name='team_preview'),
]


import numpy as np
from sklearn.preprocessing import PolynomialFeatures
import matplotlib.pyplot as plt
from scipy import optimize
from scipy import stats

def computeHypothesis(theta, x):
    return np.dot(x, theta)

def featureMapping(x, n):
    poly = PolynomialFeatures(n)
    return poly.fit_transform(x)

def computeCostReg(theta, regfactor, x, y):
    m = np.size(y, 0)
    theta = theta.reshape((np.size(x, 1), 1))
    h = computeHypothesis(theta, x)
    y_reshape = y.reshape(m, 1)
    delta = computeHypothesis(theta, x) - y.reshape(m, 1)
    J_part1 = 1 / (2 * m) * np.dot(np.transpose(delta), delta)
    J_part2 = np.dot(np.transpose(theta), theta) - 1
    J = J_part1 + regfactor / (2*m) * J_part2
    gradient = 1 / m * np.transpose(np.dot(np.transpose(h - y_reshape), x)) + regfactor / m * theta
    gradient[0, 0] = 1 / m * np.dot(np.transpose(h - y_reshape), x[:, 0])
    return J, gradient.flatten()

def addBias(a):
    m = np.size(a, 0)
    return np.append(np.ones((m, 1)), a, 1)

def training(X, y):
    reg_factor = 0
    x = X
    initial_theta = np.zeros(np.size(x, 1))
    res = optimize.minimize(computeCostReg, initial_theta, args=(reg_factor, x, y), jac=True)
    # print('optimized cost: ', res.fun)
    # print('optimized theta: ', res.x)
    return res.fun, res.x

def learningCurve(X_training, y_training, X_val, y_val):
    m = np.size(X_training, 0)
    regfactor = 0
    cost_traning_history = np.zeros((m, 1))
    cost_val_history = np.zeros((m, 1))
    for train_number in np.arange(0, m):
        cost_training, theta_result = training(X_training[0:train_number+1], y_training[0:train_number+1])
        cost_val, _ = computeCostReg(theta_result, regfactor, X_val, y_val)
        cost_traning_history[train_number, 0] = cost_training
        cost_val_history[train_number, 0] = cost_val
    return cost_traning_history, cost_val_history

def featureNormalization(x):
    # x should include the "1" column
    feature_number = np.size(x[0,:])
    mu = np.zeros(feature_number)
    sigma = np.zeros(feature_number)
    for idx in np.arange(1, feature_number):
        mu[idx] = np.mean(x[:, idx])
        sigma[idx] = np.std(x[:, idx])
        x[:, idx] = stats.zscore(x[:, idx])
    return x, mu, sigma


questNum = int(input())

for quest in range(questNum):
    n = int(input())
    s = input().split(' ')

    for i in range(n):
        s[i] = int(s[i])

    num1 = 0
    num2 = 0
    for i in range(1, n + 1):

        if s.count(i) == 0:
            num2 = i
        elif s.count(i) >= 2:
            num1 = i
            break

    print(num1, end = ' ')
    print(num2)


"""
4. Un comercio de electrodomésticos necesita para su línea de cajas un programa que
le indique al cajero el cambio que debe entregarle al cliente. Para eso se ingresan
dos números enteros, correspondientes al total de la compra y al dinero recibido.
Informar cuántos billetes de cada denominación deben ser entregados al cliente
como vuelto, de tal forma que se minimice la cantidad de billetes. Considerar que
existen billetes de $500, $100, $50, $20, $10, $5 y $1. Emitir un mensaje de error
si el dinero recibido fuera insuficiente. Ejemplo: Si la compra es de $317 y se abona con $500,
el vuelto debe contener 1 billete de $100, 1 billete de $50, 1 billete
de $20, 1 billete de $10 y 3 billetes de $1.

"""
def calcularvuelto(total,recibido):
    quinientos = 500
    cien = 100
    cincuenta = 50
    veinte = 20
    diez = 10
    cinco = 5
    uno = 1
    cambio = recibido - total
    faltante = total - recibido
    if cambio > 0:
        print(f"El cambio a dar es {cambio}")
    elif cambio == 0:
        print(f"No es necesario vuelto. El cliente ha pagado el total del producto")
    else:
        print(f"FALTA DINERO. El cliente no ha pagado el total del producto, le faltan {faltante} ")

#Programa Principal
totalcompra = int(input("Ingrese el precio de los articulos: "))
dinerorecibido = int(input("Ingrese el dinero pagado por el cliente: "))
calcularvuelto(totalcompra, dinerorecibido)


__author__ = 'helionix'

# 14. Write a program that maps a list of words into a list of integers representing the lengths of the
#     corresponding words.

def main():

    def word_map(list):
        map = []
        for i in list:
            map.append(len(i))
        return map
    list = ['Hello', 'world', 'This', 'is', 'test']

    print(word_map(list))

if __name__ == "__main__": main()


import time
import tensorflow as tf
import numpy as np
import pandas as pd
from scipy.misc import imread
from lenet import LeNet5
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split

# cifar 10
#from keras.datasets import cifar10
#(features, labels), (X_test, y_test) = cifar10.load_data()
#labels = labels[:,0]
#y_test = y_test[:,0]
#X_train,X_valid,y_train,y_valid = train_test_split(features,labels,test_size = 0.2,random_state = 0)

#nb_train = len(X_train)
#nb_classes = 10

# Load traffic sign
import pickle
training_file = "traffic-signs-data/train.p"
testing_file = "traffic-signs-data/test.p"

with open(training_file, mode='rb') as f:
    train = pickle.load(f)
with open(testing_file, mode='rb') as f:
    test = pickle.load(f)

features, labels = train['features'], train['labels']
X_test, y_test = test['features'], test['labels']
X_train,X_valid,y_train,y_valid = train_test_split(features,labels,test_size = 0.2,random_state = 0)
nb_train = len(X_train)
nb_classes = 43

learning_rate = 0.001

X = tf.placeholder(tf.float32, (None, 32, 32, 3))
y = tf.placeholder(tf.int64,(None))
keep_prob = tf.placeholder(tf.float32)

one_hot_y = tf.one_hot(y,depth=nb_classes)
logits = LeNet5(X,nb_classes)
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits,one_hot_y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)

# evaluation
correct_prediction = tf.equal(tf.argmax(logits,1),tf.argmax(one_hot_y,1))
accuracy_operation = tf.reduce_mean(tf.cast(correct_prediction,tf.float32))

def evaluate(X_data, y_data):
    num_examples = len(X_data)
    total_accuracy = 0
    sess = tf.get_default_session()
    for offset in range(0, num_examples, BATCH_SIZE):
        batch_x, batch_y = X_data[offset:offset+BATCH_SIZE], y_data[offset:offset+BATCH_SIZE]

        accuracy = sess.run(accuracy_operation, feed_dict={X: batch_x, y: batch_y})
        total_accuracy += (accuracy * len(batch_x))
    return total_accuracy / num_examples

saver = tf.train.Saver()

EPOCHS = 10
BATCH_SIZE = 128
# training
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    print("Training ...")
    for epoch in range(EPOCHS):

        X_train, y_train = shuffle(X_train, y_train)
        n_samples = len(X_train)

        # Loop over all batches
        for offset in range(0, n_samples, BATCH_SIZE):
            end = offset + BATCH_SIZE

            # Run optimization op (backprop) and cost op (to get loss value)
            sess.run(optimizer, feed_dict={X: X_train[offset:end], y: y_train[offset:end], keep_prob: 0.5})

        print("EPOCH {} ...".format(epoch + 1))
        validation_accuracy = evaluate(X_valid, y_valid)
        print("Validation Accuracy = {:.3f}".format(validation_accuracy))
        print()

    print("Training finished.")

    saver.save(sess, 'lenet')
    print("Model saved")

with tf.Session() as sess:
    saver.restore(sess, tf.train.latest_checkpoint('.'))

    test_accuracy = evaluate(X_test, y_test)
    print("Test Accuracy = {:.3f}".format(test_accuracy))


"""
DevPaths program sample interview question

Arrays
Problem definition:
    Given a  string consisting of only 0s and 1s, what is the length of the smallest contiguous
    substring containing exactly k '1's?
"""
from collections import deque

# start from the start_index, and proceed until target char is found
# return the index of target char in s, and return -1 if there is no such target
# if there is a index_list parameter provided, function appends the found index to this list
def find_char(s, target, start_index, index_list=None):
    for i in range(start_index, len(s)):
        if s[i] == target:
            if index_list is not None:
                index_list.append(i)
            return i
    return -1

def get_next_index(index_list):
    for index in index_list:
        yield index

def solution(s, k, sol_type):
    """
    Three solutions are provided, with sol_type = {1, 2, 3}
    All solutions work with creating a window string that contains k target chars, and then shift that window to right
    as it finds additional target chars to append to the end of the window (and removes from the beginning)

    All solutions seem Naive, and simple. Only difference between them is calculating the next target char for the
    beginning of the window, while shifting it to the right.

    Solution Type 1:
    Runtime: O(2n) = O(n)
    Space: O(1)
    Each time window is shifted, iterate through the string one by one to find the next target char
    (similar to how we find next target char for the right end of the window)

    Solution Type 2:
    Runtime: O(n)
    Space: O(n)
    Create a list of indexes in the string that are target char. To choose begin_index for the window,
    simply choose the next index from that list

    Solution Type 3:
    Runtime: O(n)
    Space: O(k)
    Similar to Type 2, but replace the list with FIFO (queue), so we store only k indexes
    """
    target = '1'
    smallest_l = k
    index_list = None
    if sol_type == 2:
        index_list = []
    elif sol_type == 3:
        index_list = deque()

    # Edge cases for sol_type 1:
    begin_index = find_char(s, target, 0, index_list)

    # When k == 1 then divide string into two from begin_index, and return the length of smallest
    if k == 1:
        return min(begin_index-0, len(s)-1-begin_index) + 1

    end_index = find_char(s, target, begin_index+1, index_list)

    k -= 2

    # iterate over string, until k '1's are found
    while k > 0 and end_index != -1:
        end_index = find_char(s, target, end_index+1, index_list)
        k -= 1

    if k > 0:
        return 0

    # remove the first begin_index for the next available one
    if sol_type == 2:
        index_generator = get_next_index(index_list[1:])
    elif sol_type == 3:
        index_list.popleft()

    min_length = end_index - begin_index
    while end_index != -1:
        print(end_index, begin_index, min_length)
        if end_index - begin_index < min_length:
            min_length = end_index - begin_index
            print(end_index, begin_index, min_length)
            if min_length == smallest_l:
                return min_length + 1

        end_index = find_char(s, target, end_index + 1, index_list)
        if sol_type == 1:
            begin_index = find_char(s, target, begin_index + 1)
        elif sol_type == 2:
            begin_index = next(index_generator, None)
        else:
            begin_index = index_list.popleft()

    # for the edge cases that take end_index -1, meaning last target value was not in the string
    if min_length <= 0:
        return 0

    return min_length + 1

if __name__ == '__main__':
    s = '0100101010101001'
    m1 = solution(s, 2, 1)
    m2 = solution(s, 2, 2)
    m3 = solution(s, 2, 3)
    print(m1, m1 == m2 == m3)
    s = '0010'
    m1 = solution(s, 2, 1)
    m2 = solution(s, 2, 2)
    m3 = solution(s, 2, 3)
    print(m1, m1 == m2 == m3)
    m1 = solution(s, 1, 1)
    m2 = solution(s, 1, 2)
    m3 = solution(s, 1, 3)
    print(m1, m1 == m2 == m3)


from PIL import Image
import numpy as np
from array_display import display_array
from scipy.ndimage.morphology import binary_erosion
from scipy.spatial.distance import cdist
from scipy.ndimage import distance_transform_cdt
import tensorflow as tf
import time
from sklearn.preprocessing import normalize

def dist_from_edge(img):
    I = binary_erosion(img) # Interior mask
    C = img - I             # Contour mask
    out = C.astype(int)     # Setup o/p and assign cityblock distances
    out[I] = cdist(np.argwhere(C), np.argwhere(I), 'euclidean').min(0) + 1
    return out

def compute_loss_map(array):
    if len(array.shape) == 4:
        array = array[0,:,:,0]
    interior = binary_erosion(array)
    invert = np.logical_not(array).astype(int)
    #mult_mask = np.add(np.multiply(-1,array),invert)
    contour = array - interior
    dist = distance_transform_cdt(np.logical_not(contour).astype(int),metric="taxicab")
    #return np.multiply(dist, mult_mask)
    return dist

def dist_map_loss(y_true,y_pred):
    mult = tf.multiply(tf.abs(y_true),y_pred)
    print(tf.reduce_sum(mult).eval())
    print(tf.cast(tf.count_nonzero(mult), dtype=tf.float32).eval())
    return tf.divide(tf.reduce_sum(mult), tf.cast(tf.count_nonzero(mult).eval(), dtype=tf.float32))

def main():
    im = Image.open("../CharacterImages/Regular/4eae.png")
    im = im.resize((256, 256))
    arr = np.logical_not(np.asarray(im)).astype(int)
    loss_map = compute_loss_map(arr)
    display_array(loss_map)
    display_array(np.where(loss_map == 0, 1, 0))

def main2():
    save_loss_maps_as_images()

if __name__ == "__main__":
    start = time.time()
    main()
    end = time.time()
    print("Execution time: " + str(int(end-start)) + " seconds")


# encoding: utf-8

import os
import math
import json
import threading
import subprocess
from numbers import Integral
from distutils import spawn

import numpy as np

from .py23 import TemporaryDirectory, Queue
from .utils import assert_normal
from .belief import (ArrayBelief, ParticleBelief, MaxSamplesReached,
                     format_belief_array)

SOLVER_NAME = 'pomdp-solve'

class ValueFunctionParseError(ValueError):
    pass

class Impossible(ValueError):
    pass

def parse_value_function(reader):
    has_action = False
    actions = []
    vectors = []
    for line in reader:
        if not line.isspace():
            if has_action:
                # expect vector
                vectors.append(np.fromstring(line, sep=' '))
                has_action = False
            else:
                # expect action
                actions.append(int(line))
                has_action = True
        # else: skip line
    if has_action:
        raise ValueFunctionParseError('Action defined but no vectors follows.')
    return actions, np.vstack(vectors)

def parse_policy_graph(reader):
    actions = []
    transitions = []
    for i, line in enumerate(reader):
        line = line.rstrip('\n').rstrip()
        if not line.isspace():
            # 'N A  Z1 Z2 Z3'.split(' ') -> ['N', 'A', '', 'Z1', 'Z2', 'Z3']
            l = line.split(' ')
            n = int(l[0])  # Node name
            assert(n == i)
            actions.append(int(l[1]))
            transitions.append([None if x == '-' else int(x) for x in l[3:]])
    return actions, transitions

PREAMBLE_FMT = """discount: {discount}
values: reward
states: {states}
actions: {actions}
observations: {observations}
"""
DECIMALS = 5
NUMBER_FORMAT = '{:0.' + str(DECIMALS) + 'f}'

def _as_list(lst_or_int):
    if isinstance(lst_or_int, int):
        return list(range(lst_or_int))
    else:
        return lst_or_int

def _dump_list(lst):
    return ' '.join([str(x) for x in lst])

def _dump_list_or_count(lst_or_int):
    if isinstance(lst_or_int, int):
        return str(lst_or_int)
    else:
        return _dump_list(lst_or_int)

def _dump_1d_array(a):
    # Make sure that sum stays the same even after trunc
    trunc_sum = np.around(a.sum(), decimals=DECIMALS)
    trunc = np.around(a, decimals=DECIMALS)
    imax = np.argmax(trunc)
    # Compensate on max to avoid negative values
    trunc[imax] += trunc_sum - trunc.sum()
    return ' '.join([NUMBER_FORMAT.format(x) for x in trunc])

def _dump_2d_array(a):
    return '\n'.join([_dump_1d_array(x) for x in a])

def _dump_3d_array(a, name, xs):
    """Dump a 3d array for a POMDP file.

    :param a: the array
    :param name: the name of the array in the file
    :param xs: names of the first dimension
    """
    return '\n'.join([
        "{} : {}\n{}".format(name, x, _dump_2d_array(a[ix, :, :]))
        for ix, x in enumerate(xs)
        ])

def _dump_4d_array(a, name, xs, ys):
    """Dump a 4d array for a POMDP file.

    :param a: the array
    :param name: the name of the array in the file
    :param xs: names of the first dimension
    :param ys: names of the second dimension
    """
    return '\n'.join([
        _dump_3d_array(a[ix, :, :, :], name,
                       ["{} : {}".format(x, y) for y in ys])
        for ix, x in enumerate(xs)
        ])

class POMDP:

    """Partially observable Markov model.

    :param T: array of shape (n_actions, n_states, n_states)
        Transition probabilities (must sum to 1 on last dimension)
    :param O: array of shape (n_actions, n_states, n_observations)
        Observation probabilities (action, *end state*, observation)
        (must sum to 1 on last dimension)
    :param R: array of shape (n_actions, n_states, n_states, n_observations)
        Rewards or cost (must sum to 1 on last dimension)
    :param start: array of shape (n_states)
        Initial state probabilities
    :param discount: discount factor (int)
    :param states: None | iterable of states
        Default to range(n_states).
    :param actions: None | iterable of actions
        Default to range(n_actions).
    :param observations: None | iterable of observations
        Default to range(n_observations).
    :values: ('reward' | 'cost')
        How to interpret reward coefficients.
    :solver_path: string
        Path in which to look for the executable (default to $PATH)
    """

    def __init__(self, T, O, R, start, discount, states=None, actions=None,
                 observations=None, values='reward', solver_path=None):
        # Defaults for actions, states and observations
        a, s, o = O.shape
        self._init_states(states, s)
        self._init_actions(actions, a)
        self._init_observations(observations, o)
        self.T = T
        self.O = O
        if values == 'reward':
            self.R = R
        elif values == 'cost':
            self.R = -R
        else:
            raise ValueError(
                "Values must be 'reward' of 'cost. Got '{}'.".format(values))
        self.start = start
        self._assert_shapes()
        self._assert_normal()
        self._assert_unique()
        if discount > 1 or discount < 0:
            raise ValueError('Discount factor must be ≤ 1 and ≥ 0.')
        self.discount = discount
        self._solver_path = spawn.find_executable(SOLVER_NAME,
                                                  path=solver_path)
        if self._solver_path is None:
            raise ImportError('Could not find executable for pomdp-solve.')

    def _init_states(self, states, s):
        if states is not None:
            self._s = list(states)
        else:
            self._s = s

    def _init_actions(self, actions, a):
        if actions is not None:
            self._a = list(actions)
        else:
            self._a = a

    def _init_observations(self, observations, o):
        if observations is not None:
            self._o = list(observations)
        else:
            self._o = o

    @property
    def states(self):
        return _as_list(self._s)

    @property
    def actions(self):
        return _as_list(self._a)

    @property
    def observations(self):
        return _as_list(self._o)

    @property
    def n_states(self):
        return len(self.states)

    @property
    def n_actions(self):
        return len(self.actions)

    @property
    def n_observations(self):
        return len(self.observations)

    def _assert_shapes(self):
        s = self.n_states
        a = self.n_actions
        o = self.n_observations
        message = "Wrong shape for {}: got {}, expected {}."

        def assert_shape(array, name, shape):
            if array.shape != shape:
                raise ValueError(message.format(name, array.shape, shape))

        assert_shape(self.start, 'start', (s,))
        assert_shape(self.T, 'T', (a, s, s))
        assert_shape(self.O, 'O', (a, s, o))
        assert_shape(self.R, 'R', (a, s, s, o))

    def _assert_normal(self):
        assert_normal(self.start, name='start')
        assert_normal(self.T, name='T')
        assert_normal(self.O, name='O')

    def _assert_unique(self):
        message = "Found duplicate {}: {}"

        def assert_no_dup(lst, name):
            if not len(set(lst)) == len(lst):
                dup = list(lst)
                for a in set(lst):
                    dup.remove(a)
                raise ValueError(message.format(name, dup))

        assert_no_dup(self.states, 'states(s)')
        assert_no_dup(self.actions, 'action(s)')
        assert_no_dup(self.observations, 'observation(s)')

    def belief_update(self, a, o, b):
        new_b = b.dot(self.T[a, ...]) * self.O[a, :, o]
        s = new_b.sum()
        if s == 0.:
            raise Impossible('Impossible observation: ' + str(o))
        return new_b / s

    def sample_transition(self, a, s):
        new_s = np.random.choice(self.n_states, p=self.T[a, s, :])
        o = np.random.choice(self.n_observations, p=self.O[a, new_s, :])
        r = self.R[a, s, new_s, o]
        return new_s, o, r

    def sample_start(self):
        return np.random.choice(self.n_states, p=self.start)

    def dump(self):
        """Write POMDP description following:
        `<http://www.pomdp.org/code/pomdp-file-spec.html>`_
        """
        preamble = PREAMBLE_FMT.format(
            discount=self.discount,
            states=_dump_list_or_count(self._s),
            actions=_dump_list_or_count(self._a),
            observations=_dump_list_or_count(self._o))
        start = "start: {}".format(_dump_1d_array(np.asarray(self.start)))
        T = _dump_3d_array(self.T, 'T', self.actions)
        O = _dump_3d_array(self.O, 'O', self.actions)
        R = _dump_4d_array(self.R, 'R', self.actions, self.states)
        return '\n\n'.join([preamble, start, T, O, R])

    def dump_to(self, path, name):
        full_path = os.path.join(path, name + '.pomdp')
        with open(full_path, 'w') as f:
            f.write(self.dump())
        return full_path

    def to_dict(self):
        return {'T': self.T.tolist(),
                'O': self.O.tolist(),
                'R': self.R.tolist(),
                'start': self.start.tolist(),
                'discount': self.discount,
                'states': self.states,
                'actions': self.actions,
                'observations': self.observations,
                }

    def as_json(self):
        return json.dumps(self.to_dict())

    def save_as_json(self, path):
        with open(path, 'w') as f:
            json.dump(self.to_dict(), f)

    @classmethod
    def from_dict(cls, d):
        return cls(np.asarray(d['T']), np.asarray(d['O']), np.asarray(d['R']),
                   np.asarray(d['start']), d['discount'], states=d['states'],
                   actions=d['actions'], observations=d['observations'],
                   values='reward')

    @classmethod
    def from_json(cls, s):
        d = json.loads(s)
        return cls.from_dict(d)

    @classmethod
    def load_from_json(cls, path):
        with open(path) as f:
            d = json.load(f)
            return cls.from_dict(d)

    def randomize(self, p_unexpected=1.e-3):
        self.T += p_unexpected
        self.T /= self.T.sum(-1)[..., None]
        self.O += p_unexpected
        self.O /= self.O.sum(-1)[..., None]

    def solve(self, timeout=None, n_iterations=None, method='incprune',
              grid_type=None, seed=None, verbose=False):
        """
        :param method: incprune | grid (incprune)
        :param grid_type: simplex | pairwise (simplex)
        """
        name = 'tosolve'
        args = []
        if timeout is not None:
            args.extend(['-time_limit', str(timeout)])
        if n_iterations is not None:
            args.extend(['-horizon', str(n_iterations)])
        if seed is None:
            seed = np.random.randint(1.e10)
        args.extend(['-rand_seed', str(seed)])
        if method == 'grid':
            if grid_type is None:
                grid_type = 'simplex'
            args.extend(['-method', method, '-fg_type', grid_type])
        with TemporaryDirectory() as tmpdir:
            pomdp_file = self.dump_to(tmpdir, name)
            args.extend(['-o', name, '-pomdp', pomdp_file])
            with open(os.devnull, 'w') as DEVNULL:
                subprocess.check_call(
                    [self._solver_path] + args, cwd=tmpdir,
                    stdout=None if verbose else DEVNULL)
            return self.load_policy_from(tmpdir, name)

    def load_policy_from(self, path, name):
        value_function_file = os.path.join(path, name + '.alpha')
        policy_graph_file = os.path.join(path, name + '.pg')
        with open(value_function_file, 'r') as vf:
            actions, vf = parse_value_function(vf)
        with open(policy_graph_file, 'r') as pf:
            actions2, pg = parse_policy_graph(pf)
        assert(actions == actions2)
        assert(max(actions) < len(self.actions))
        assert(max([t for ts in pg for t in ts if t is not None]) <= len(pg))
        action_names = [self.actions[a] for a in actions]
        return GraphPolicy(action_names, self.observations, pg, vf,
                           start=self.start)

class GraphPolicy:

    def __init__(self, actions, observations, transitions, values, start=None,
                 init=None):
        self.actions = actions
        self.observations = observations
        self.transitions = np.asarray(transitions)
        assert(self.transitions.shape == (self.n_nodes, len(observations)))
        self.values = np.asarray(values)
        if init is not None:
            assert(init < self.n_nodes)
            self.init = init
        elif start is not None:
            self.init = self.get_node_from_belief(start)
        else:
            raise ValueError('Must specify either init node or start belief.')

    @property
    def n_nodes(self):
        return len(self.actions)

    def get_node_from_belief(self, b):
        return self.values.dot(b[:, np.newaxis]).argmax()

    def get_action(self, current):
        return self.actions[current]

    def next(self, current, observation):
        return self.transitions[current, self.observations.index(observation)]

    def to_dict(self):
        return {'actions': self.actions,
                'observations': self.observations,
                'transitions': self.transitions.tolist(),
                'values': self.values.tolist(),
                'initial': str(self.init),
                }

    def to_json(self, indent=None):
        return json.dumps(self.to_dict(), indent=indent)

    def save_as_json(self, path, indent=None):
        with open(path, 'w') as fp:
            json.dump(self.to_dict(), fp, indent=indent)

    @classmethod
    def from_dict(cls, d):
        return cls(d['actions'], d['observations'], d['transitions'],
                   d['values'], init=int(d['initial']))

    @classmethod
    def from_json(cls, s):
        return cls.from_dict(json.loads(s))

    @classmethod
    def load_from_json(cls, path):
        with open(path) as f:
            return cls.from_dict(json.load(f))

class GraphPolicyRunner(object):

    def __init__(self, graph_policy):
        self.gp = graph_policy
        self.reset()

    def reset(self, belief=None):
        if belief is not None:
            self.current = self.gp.get_node_from_belief(belief)
        else:
            self.current = self.gp.init

    def get_action(self):
        return self.gp.get_action(self.current)

    def step(self, observation):
        self.current = self.gp.next(self.current, observation)
        if self.current is None:
            raise Impossible('Got unexpected observation')

class GraphPolicyBeliefRunner(GraphPolicyRunner):

    def __init__(self, graph_policy, pomdp):
        self.gp = graph_policy
        self.pomdp = pomdp
        self.reset()

    def reset(self, belief=None):
        if belief is None:
            belief = self.pomdp.start
        self.current_belief = belief
        super(GraphPolicyBeliefRunner, self).reset(belief=belief)

    def step(self, observation):
        a = self.pomdp.actions.index(self.get_action())
        o = self.pomdp.observations.index(observation)
        b = self.pomdp.belief_update(a, o, self.current_belief)
        self.reset(belief=b)

    def _rec_trajectory_tree(self, obs, horizon):
        if horizon >= 0:
            try:
                b = self.current_belief
                self.step(obs)
                tree = self.trajectory_tree(horizon)
                self.reset(belief=b)  # Restore state for next obs
                return tree
            except Impossible:  # Observation is impossible here
                pass
        return None  # either horizon is reached or observation is impossible

    def trajectory_tree(self, horizon):
        obs = self.pomdp.observations
        children = [self._rec_trajectory_tree(o, horizon - 1) for o in obs]
        return {"belief": self.current_belief.tolist(),
                "action": self.get_action(),
                "node": int(self.current),
                "observations": [o for i, o in enumerate(obs)
                                 if children[i] is not None],
                "children": [c for c in children if c is not None],
                }

    def trajectory_trees_from_starts(self, horizon=5):
        start = self.pomdp.start
        trees = []
        for s in start.nonzero():
            b = np.zeros(start.shape)
            b[s] = 1.
            self.reset(belief=b)
            trees.append(self.trajectory_tree(horizon))
        return {"graphs": trees}

    def save_trajectories_from_starts(self, dest, horizon=5, indent=None):
        with open(dest, 'w') as f:
            json.dump(self.trajectory_trees_from_starts(horizon=horizon),
                      f, indent=indent)

    def visit(self, max_states=100):
        v = _Aux(self, max_nodes=max_states)
        v.visit()
        return GraphPolicy(v.actions, v.observations, np.asarray(v.trans),
                           np.vstack(v.nodes), init=0)

class _Aux:

    tol = 1.e-2

    def __init__(self, pgbr, max_nodes=100):
        self.pr = pgbr
        self.max_nodes = max_nodes
        self.nodes = []
        self.queue = Queue()  # FIFO
        self.trans = []
        self.actions = []

    @property
    def observations(self):
        return self.pr.pomdp.observations

    @property
    def beliefs(self):
        return np.vstack(self.nodes)

    def closest(self, b):
        if len(self.nodes) < 1:
            return -1, np.inf
        else:
            distances = np.sqrt(((self.beliefs - b) ** 2).sum(-1))
            i = distances.argmin()
            return i, distances[i]

    def index(self, b):
        i, d = self.closest(b)
        if d < self.tol:
            return i
        else:
            i = len(self.nodes)
            self.nodes.append(b)
            self.trans.append([None for _ in self.observations])
            self.pr.reset(np.array(b))
            self.actions.append(self.pr.get_action())
            self.queue.put(i)
            return i

    def visit(self):
        self.index(self.pr.pomdp.start)
        while not (self.queue.empty() or len(self.nodes) > self.max_nodes):
            ib = self.queue.get()
            for io, o in enumerate(self.observations):
                try:
                    self.pr.reset(belief=np.array(self.nodes[ib]))
                    self.pr.step(o)
                    ib_new = self.index(self.pr.current_belief)
                    self.trans[ib][io] = int(ib_new)
                except Impossible:
                    pass

# POMCP

class Horizon(object):

    class _Generator:

        def __init__(self, cls, *args, **kwargs):
            self.cls = cls
            self.args = args
            self.kwargs = kwargs

        def __call__(self):
            return self.cls(*self.args, **self.kwargs)

    def is_reached(self):
        raise NotImplementedError

    def decrement(self, a, s, new_s, o):
        raise NotImplementedError

    def copy(self):
        raise NotImplementedError

    @classmethod
    def generator(cls, model, **parameters):
        raise NotImplementedError

class NTransitionsHorizon(Horizon):

    def __init__(self, n):
        self.n = n

    def is_reached(self):
        return self.n <= 0

    def decrement(self, a, s, new_s, o):
        self.n -= 1

    def copy(self):
        return NTransitionsHorizon(self.n)

    @classmethod
    def generator(cls, model, n=100):
        return cls._Generator(cls, n)

def _children_to_dict(d, children):
    d['children'] = children
    return d

def _null_logger(*args, **kwargs):
    pass

class _SearchTree:

    rollout_it = 100

    def __init__(self, model, horizon_generator, exploration,
                 relative_exploration=False, belief='array', belief_params={},
                 node_params={}, logger=None):
        self._belief = belief
        self._belief_params = belief_params
        self.model = model
        self._node_params = node_params
        self.root = self._observation_node_for_belief(self._belief_start())
        self.horizon_gen = horizon_generator
        self.exploration = exploration
        self.relative_explo = relative_exploration
        self.log = _null_logger if logger is None else logger

    def _belief_start(self):
        if self._belief == 'array':
            return ArrayBelief(self.model.start, **self._belief_params)
        elif self._belief == 'particle':
            return ParticleBelief(self.model.sample_start, self.model.n_states,
                                  **self._belief_params)
        else:
            raise ValueError('Unknown belief type: ' + self._belief)

    def get_node(self, history):
        """Raises ValueError if node does not exist or history is invalid."""
        node = self.root
        last_belief = node.belief
        for i, h in enumerate(history):
            if isinstance(node, _SearchActionNode):  # h is an observation
                if h not in node.children:
                    node.children[h] = self._observation_node_for_belief(
                        last_belief.successor(self.model, history[i - 1], h))
                node = node.children[h]
            else:  # h is an action
                last_belief = node.belief
                node = node.safe_get_child(h)
        return node

    def random_action(self):
        return np.random.randint(self.model.n_actions)

    def rollout_from_node(self, node, horizon):
        if horizon.is_reached():
            return 0
        else:
            full_return = 0.
            for _ in range(self.rollout_it):
                state = node.belief.sample()
                full_return += self._one_rollout_from_node(state, horizon.copy())
            node.update(full_return / self.rollout_it)  # Only counts one visit
            return full_return

    def _one_rollout_from_node(self, state, horizon):
        gamma = 1.
        full_return = 0.
        while not horizon.is_reached():
            a = self.random_action()
            new_state, o, r = self.model.sample_transition(a, state)
            horizon.decrement(a, state, new_state, o)
            state = new_state
            full_return += gamma * r
            gamma *= self.model.discount
        return full_return

    def simulate_from_node(self, node, action=None):
        state = node.belief.sample()
        self._simulate_from_node(node, state, self.horizon_gen(), a=action)

    def _observation_node_for_belief(self, b):
        return _SearchObservationNode(b, self.model.n_actions, **self._node_params)

    def _simulate_from_node(self, node, state, horizon, a=None):
        if horizon.is_reached():
            return node.value
        else:
            if a is None:
                a = node.get_best_action(
                    exploration=self.exploration,
                    relative_exploration=self.relative_explo)
            child = node.safe_get_child(a)
            new_s, o, r = self.model.sample_transition(a, state)
            horizon.decrement(a, state, new_s, o)
            if o not in child.children:
                try:
                    # Create node with updated belief
                    child.children[o] = self._observation_node_for_belief(
                        node.belief.successor(self.model, a, o))
                    # Use rollout
                    partial_return = self.rollout_from_node(child.children[o],
                                                            horizon)
                except MaxSamplesReached:
                    self.log('Maximum number of samples reached, skipping.')
                    partial_return = 0.
                    # Note maybe use more relevant value, but, since the event
                    # is rare, it should not impact the result
            else:
                # Continue regular search
                partial_return = self._simulate_from_node(
                    child.children[o], new_s, horizon)
            full_return = r + self.model.discount * partial_return
            child.update(full_return)
            node.update(full_return)
            # TODO belief update (not needed for exact belief)
            return full_return

    def to_dict(self, as_policy=False):
        return self.root.to_dict(self.model, as_policy=as_policy)

    def map(self, fun, join_children=_children_to_dict):
        """
        :param join_children: function(result, children)
            returns result_with_children
        """
        return self.root._map(fun, join_children)

class _ObservationLookupSearchTree(_SearchTree):

    def __init__(self, model, horizon, exploration,
                 relative_exploration=False, belief='array', belief_params={},
                 node_params={}, logger=None):
        self._obs_nodes = {}  # used in super for root initialization
        if belief == 'particle':
            raise ValueError(
                '_ObservationLookupSearchTree does not support particle belief')
        super(_ObservationLookupSearchTree, self).__init__(
            model, horizon, exploration,
            relative_exploration=relative_exploration,
            belief=belief, belief_params={},
            node_params=node_params, logger=logger)

    def _observation_node_for_belief(self, b):
        # Returns node for given belief, creating one if none exists
        if b not in self._obs_nodes:
            self._obs_nodes[b] = _SearchObservationNode(
                b, self.model.n_actions, **self._node_params)
        return self._obs_nodes[b]

    # Here we need to keep track of visited children since the tree is no more
    # a tree...
    def to_dict(self, as_policy=False):
        return self.root.to_dict(self.model, as_policy=as_policy,
                                 exclude_visited=set())

class _ValueAverage(object):

    def __init__(self, alpha=0):
        self.n_simulations = 0
        self.total_value = 0.
        assert(0 <= alpha <= 1)
        self.alpha = alpha

    @property
    def value(self):
        return 0. if self.n_simulations == 0 \
                else self.total_value / self.n_simulations

    def update(self, value):
        self.total_value = ((self.total_value + value) * (1 - self.alpha)
                            + self.alpha * self.n_simulations * value)
        self.n_simulations += 1

class _SearchNode(object):

    def __init__(self, alpha=.001):
        self._avg = _ValueAverage(alpha=alpha)
        self.children = {}

    def __str__(self):
        return "[" + ", ".join(["{}: {}".format(i, self.children[i])
                                for i in self._children_keys()]) + "]"

    def _children_keys(self):
        return sorted(self.children.keys())

    @property
    def n_simulations(self):
        return self._avg.n_simulations

    @property
    def value(self):
        return self._avg.value

    def update(self, value):
        self._avg.update(value)

    def to_dict(self, model, as_policy=False, exclude_visited=None,
                recursive=True):
        return {"value": self.value,
                "visits": self.n_simulations,
                "node": None,
                }

    def _map(self, fun, join_children):
        result = fun(self)
        child_results = [c._map(fun, join_children)
                         for c in self._iterate_children()]
        return join_children(result, child_results)

    def _iterate_children(self):
        return self.children.values()

class _SearchObservationNode(_SearchNode):
    """
    Children indexed by action.
    """

    def __init__(self, belief, n_actions, alpha=.001):
        super(_SearchObservationNode, self).__init__(alpha=alpha)
        self.belief = belief
        self.children = [None for _ in range(n_actions)]
        self._children_alpha = alpha

    def children_dict(self, model):
        return {model.actions[a]: c
                for a, c in enumerate(self.children) if c is not None}

    def _children_keys(self):
        return [i for i, c in enumerate(self.children) if c is not None]

    def _not_init_children(self):
        return [i for i, c in enumerate(self.children)
                if c is None or c.n_simulations == 0]

    def augmented_values(self, exploration=0, relative=False):
        # Note: nans are returned for not initialized children
        if exploration > 0 and relative:
            vals = [child.value if child is not None else np.nan
                    for child in self.children]
            exploration *= np.nanmax(vals) - np.nanmin(vals)
        l_ns = np.log(self.n_simulations)
        return [child.value + exploration * np.sqrt(l_ns / child.n_simulations)
                if child is not None else np.nan
                for child in self.children]

    def get_best_action(self, exploration=0, relative_exploration=False):
        not_init = self._not_init_children()
        if len(not_init) == 0:
            assert(self.n_simulations > 0)  # explored if children explored
            # Augmented greedy (UCT)
            a = np.argmax([self.augmented_values(
                exploration=exploration, relative=relative_exploration)])
        else:
            # Chose an unexplored action
            a = np.random.choice(not_init)
        return a

    def safe_get_child(self, a):
        if self.children[a] is None:
            self.children[a] = _SearchActionNode(alpha=self._children_alpha)
        return self.children[a]

    def _iterate_children(self):
        return filter(lambda c: c is not None, self.children)

    def to_dict(self, model, as_policy=False, observed=None,
                exclude_visited=None, recursive=True):
        children = recursive
        if exclude_visited is not None:
            if self.belief in exclude_visited:
                children = False
            else:
                exclude_visited.add(self.belief)
        base = super(_SearchObservationNode, self).to_dict(
            model, as_policy=as_policy, exclude_visited=exclude_visited)
        base["belief"] = self.belief.to_list()
        if as_policy:
            a = self.get_best_action()
            grand_children = self.safe_get_child(a).children
            base.update({
                "action": model.actions[a],
                "observed": observed,
                "values": [v if not math.isnan(v) else None
                           for v in self.augmented_values()],  # For json
                "exploration_terms": [
                    np.sqrt(np.log(self.n_simulations) / child.n_simulations)
                    if ((child is not None) and child.n_simulations > 0)
                    else None
                    for child in self.children
                    ],
                "child_visits": [c.n_simulations if c is not None else 0
                                 for c in self.children],
                })
            if children:
                base.update({
                    "observations": [model.observations[o]
                                     for o in grand_children],
                    "children": [
                        grand_children[o].to_dict(
                            model, as_policy=as_policy, observed=i,
                            exclude_visited=exclude_visited)
                        for i, o in enumerate(grand_children)],
                    })
        else:
            if children:
                base.update({
                    "actions": [model.actions[i]
                                for i, c in enumerate(self.children)
                                if c is not None],
                    "children": [c.to_dict(model, as_policy=as_policy,
                                           exclude_visited=exclude_visited)
                                 for c in self.children if c is not None],
                    })
            else:
                base.update({'actions': [], 'children': []})

        return base

class _SearchActionNode(_SearchNode):
    """
    Children indexed by observation.
    """

    def to_dict(self, model, as_policy=False, exclude_visited=None,
                recursive=True):
        if as_policy:
            raise NotImplemented
        else:
            base = super(_SearchActionNode, self).to_dict(
                model, as_policy=as_policy, exclude_visited=exclude_visited)
            base.update({
                "observations": [model.observations[o]
                                 for o in self.children],
                "children": [self.children[o].to_dict(
                                model, as_policy=as_policy,
                                exclude_visited=exclude_visited)
                             for o in self.children],
                })
            return base

class POMCPPolicyRunner(object):
    """
    :param particles: number of particles for belief estimation
    :param horizon: length of simulation episodes
    :param iterations: number of simulation episodes to run
    :param exploration: UCT exploration parameter (c in [Silver2010])
    :param belief_values: group values for histories with same belief
    """

    def __init__(self, model, particles=20, iterations=100, horizon=100,
                 exploration=None, relative_exploration=False,
                 belief_values=False, belief='array', belief_params={},
                 logger=None):
        if logger is None:
            from logging import warning as logger
        if exploration is None:
            exploration = 1. if relative_exploration else 100
        tree_class = (_ObservationLookupSearchTree if belief_values
                      else _SearchTree)
        if isinstance(horizon, Horizon._Generator):
            horizon_generator = horizon
        elif isinstance(horizon, Integral):
            horizon_generator = NTransitionsHorizon.generator(model, n=horizon)
        else:
            raise ValueError('Invalid horizon: ' + str(horizon))
        self.tree = tree_class(model, horizon_generator, exploration,
                               relative_exploration=relative_exploration,
                               belief=belief, belief_params=belief_params,
                               logger=logger)
        self.iterations = iterations
        self._reset()

    @property
    def actions(self):
        return self.tree.model.actions

    @property
    def observations(self):
        return self.tree.model.observations

    def reset(self, belief=None):
        self._reset(belief=belief)

    # Note the reason for having both _reset and reset
    # is that in the async subclass the former needs the thread
    # to be initialized, and __init__ calls _reset before the that.
    def _reset(self, belief=None):
        if belief is not None:
            raise NotImplementedError
        self.history = []
        self._node = self.tree.root
        self._last_action = None

    @property
    def belief(self):
        return self._node.belief

    def get_action(self, iterations=None):
        # Note iterations must be greater than the number of actions
        # to guarantee that any action chosen as best_action is explored first
        if iterations is None:
            iterations = self.iterations
        for _ in range(iterations):
            self.tree.simulate_from_node(self._node)
        a = self._node.get_best_action()
        # No exploration during exploitation?
        self._last_action = a
        return self.actions[a]

    def step(self, observation):
        if self._last_action is None:
            raise ValueError('Unknown last action')
            # TODO rethink the design of the PolicyRunner class
        o = self.observations.index(observation)
        self.history.extend([self._last_action, o])
        # TODO: Make sure that node exists (and explores from previous)
        self._node = self.tree.get_node(self.history)

    def trajectory_trees_from_starts(self, qvalue=False):
        return {"graphs": [self.tree.to_dict(as_policy=not qvalue)]}

    def run_trajectory(self, logger=None):
        if logger is None:
            from logging import info as logger
        model = self.tree.model
        R = 0
        logger('New trajectory')
        self.reset()
        s = model.sample_start()
        belief_quotient = model._int_to_state().belief_quotient
        belief_preferences = model._int_to_state().belief_preferences
        while not model.is_final(s):
            a = self.get_action()
            ns, o, r = model.sample_transition(model.actions.index(a), s)
            self.step(model.observations[o])
            logger('{} -- {} --> {}, {}, {}'.format(
                model._int_to_state(s),
                a,
                model._int_to_state(ns),
                model.observations[o],
                r))
            logger('belief: {} | {:.2f}'.format(
                format_belief_array(belief_quotient(self.belief.array)),
                belief_preferences(self.belief.array)[0]))
            s = ns
            R += r
        logger("Total reward: %f" % R)

class AsyncPOMCPPolicyRunner(POMCPPolicyRunner):

    class _Thread(threading.Thread):
        """Continuously explores and enable another thread to execute
        some exploitation in between two explorations.
        """

        def __init__(self, tree):
            super(AsyncPOMCPPolicyRunner._Thread, self).__init__()
            self.tree = tree
            self._node = tree.root
            self._action = None
            self._done = False
            self._done_exploring = threading.Event()
            self._done_exploiting = threading.Event()
            self._done_exploiting.set()

        def stop(self):
            self._done = True
            self._done_exploiting.set()

        def execute(self, fun, *args, **kwargs):
            """Waits until current exploration is done and execute fun.
            """
            self._done_exploring.wait()
            self._done_exploiting.clear()
            result = fun(*args, **kwargs)
            self._done_exploiting.set()
            return result

        def set_node(self, node):
            self._node = node
            self._action = None

        def set_action(self, action):
            self._action = action

        def explore(self):
            self.tree.simulate_from_node(self._node, action=self._action)

        def run(self):
            while not self._done:
                self._done_exploring.clear()
                self.explore()
                self._done_exploring.set()
                self._done_exploiting.wait()

    def __init__(self, *args, **kwargs):
        super(AsyncPOMCPPolicyRunner, self).__init__(*args, **kwargs)
        self.thread = self._Thread(self.tree)
        self.thread.start()

    def step(self, observation):
        super(AsyncPOMCPPolicyRunner, self).step(observation)
        self.thread.execute(self.thread.set_node, self._node)

    def reset(self, belief=None):
        self._reset(belief=belief)
        self.thread.execute(self.thread.set_node, self._node)

    def get_action(self, iterations=None):
        # Start with minimal number of iterations to have explored all actions
        a = self.thread.execute(super(AsyncPOMCPPolicyRunner, self).get_action)
        self.thread.execute(self.thread.set_action, self._last_action)
        return a

    def stop(self):
        self.thread.stop()
        if self.thread.isAlive():
            self.thread.join()

    def execute(self, *args, **kwargs):
        self.thread.execute(*args, **kwargs)

    def __del__(self):
        self.stop()

def export_pomcp(policy, destination, belief_as_quotient=False):
    if belief_as_quotient:
        dic = {}
        FLAG = '####'
        model = policy.tree.model

        def to_dict(node):
            if isinstance(node, _SearchObservationNode):
                d = node.to_dict(model, as_policy=True, recursive=False)
                d['belief'] = list(model._int_to_state().belief_quotient(
                    np.array(d['belief'])))
                a_i = model.actions.index(d['action'])
                d['ACTION_IDX'] = sum([c is not None
                                       for c in node.children[:a_i]])
            else:
                d = {"observations": [model.observations[o]
                                      for o in node.children]}
                d[FLAG] = True
            return d

        def join_children(d, children):
            if d.get(FLAG, False):  # Action node
                d.pop(FLAG)
                d['children'] = children
            else:  # Observation node
                i = d.pop('ACTION_IDX')
                child = children[i]
                d['observations'] = child['observations']
                d['children'] = child['children']
                for c, o in zip(d['children'], d['observations']):
                    c['observed'] = model.observations.index(o)
            return d

        dic['graphs'] = [policy.tree.map(to_dict, join_children)]
        dic['states'] = model.htm_names
    else:
        dic = policy.trajectory_trees_from_starts()
        dic['states'] = model.states
    dic['actions'] = model.actions
    dic['exploration'] = policy.tree.exploration
    dic['relative_exploration'] = policy.tree.relative_explo

    with open(destination, 'w') as f:
        json.dump(dic, f, indent=2)