import requests

def main():

    print_header()
    city = input('What city do you want the weather information? ')
    key = getLoc(city)
    getForecast(key, city)

def print_header():

    print("-----------------------------------")
    print("       Weather Forecast APP")
    print("-----------------------------------")
    print()

def getLoc(city):
    url = "http://dataservice.accuweather.com/locations/v1/cities/search?apikey=oSiVGZ7dpRMKj7htAbxAXlTnGmSD7yU6&q={}&details=true&offset=0".format(city)
    rqs = requests.get(url)
    data = rqs.json()
    locKey = data[0]['Key']

    return locKey

def getForecast(key, city):
    urf = "http://dataservice.accuweather.com/forecasts/v1/daily/5day/{}?apikey=oSiVGZ7dpRMKj7htAbxAXlTnGmSD7yU6&details=true&metric=true".format(key)
    res = requests.get(urf)
    data = res.json()

    for cuaca1 in data['DailyForecasts']:
        print("Weather Forecast for {} in ".format(city.upper().strip()) + cuaca1['Date'])
        print("Min Temp         :"+ str(cuaca1['Temperature']['Minimum']['Value'])+ "°C")
        print("Max Temp         :"+ str(cuaca1['Temperature']['Maximum']['Value'])+ "°C")
        print("Day Forecast     :"+ str(cuaca1['Day']['ShortPhrase']))
        print("Night Forecast   :"+ str(cuaca1['Night']['ShortPhrase']))
        print("------------------------------------------------------------------------------")

if __name__ == '__main__':

    main()


import scrapy
class Expdb(scrapy.Spider):
	name = 'expdb'
	allowed_domains = ['exploit-db.com']
	start_urls = [
	'https://www.exploit-db.com/remote/'
	]

	def parse(self,response):
		urls = []
		hrefs = response.css('.description a::attr(href)')
		for href in hrefs:
			urls.append(href)
			full_url = response.urljoin(href.extract())
			yield scrapy.Request(full_url,callback = self.parse_question)
		with open('urls.txt','wb') as f:
			f.write(urls)
	def parse_question(self,response):
		yield {
		'id' : response.css('.exploit_list tr td::text')[0].extract(),
		'name' : response.xpath('//title/text()').extract(),
		'author' : response.css('.exploit_list tr')[1].css('td strong::text')[1].extract(),
		'time' : response.css('.exploit_list tr')[1].css('td::text')[2].extract(),
		'link' : response.url
		}


import chess
import discord
import json
from discord.ext import commands

with open("configuration.json", "r") as config:
	data = json.load(config)
	token = data["token"]
	prefix = data["prefix"]

intents = discord.Intents.all()

bot = commands.Bot(prefix, case_insensitive = True, intents = intents)
bot.remove_command("help")

initial_extensions = [
	"Cogs.help",
]

print(initial_extensions)

if __name__ == '__main__':
	for extension in initial_extensions:
		try:
			bot.load_extension(extension)
			print(f"Extensions loaded: {extension}")
		except Exception as e:
			print(f"Failed to load extension {extension}.")

@bot.event
async def on_ready():
    await bot.change_presence(status=discord.Status.online, activity=discord.Game("chessing"))
    print("")
    print(f"Logged in as {bot.user}")

isBoard = False

# @bot.command()
# async def start(ctx):
#     global board
#     global isBoard # using global variables is probably not
#                    # a good idea but what i have to say is

#     isBoard = True
#     board = chess.Board()

#     await ctx.channel.send("Board was created.")

# @bot.command()
# async def move(ctx, arg):
#     global isBoard

#     if isBoard:
#         try:
#             board.push_san(arg)

#             if board.is_game_over():
#                 await ctx.channel.send(board.outcome(claim_draw=True))
#                 await end(ctx)

#             elif board.is_check():
#                 await ctx.channel.send("Check.")
#             else:
#                 await ctx.message.add_reaction("✅")

#         except ValueError:
#             await ctx.channel.send("Invalid move.")
#     else:
#         await ctx.channel.send("Board wasn't created. Use &start to create.")

# @bot.command()
# async def end(ctx):
#     global board

#     await ctx.channel.send("```" + str(board) + "```")
#     await ctx.channel.send("Game ended. Board is reset.")

#     board.reset()

bot.run(token)


import subprocess, os, argparse, platform

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--output', help='Output folder of compiled plugin')

    args = parser.parse_args()
    output_folder = args.output

    unrealcv_folder = os.environ.get('UnrealCV')
    unrealcv_descriptor = os.path.join(unrealcv_folder, 'UnrealCV.uplugin')

    UE4 = os.environ.get('UE4')

    system_name = platform.system()
    if system_name == 'Linux':
        UAT_Script = os.path.join(UE4, 'Engine/Build/BatchFiles/RunUAT.sh')
        platform_name = 'Linux'
    elif system_name == 'Darwin':
        pass
    elif system_name == 'Windows': # ??
        UAT_Script = os.path.join(UE4, 'Engine\\Build\\BatchFiles\\RunUAT.bat')
        platform_name = 'Win64'
    else:
        print('The system %s can not be supported' % system_name)

    abs_unrealcv_descriptor = os.path.abspath(unrealcv_descriptor)
    abs_output_folder = os.path.abspath(output_folder)

    subprocess.call([
        UAT_Script, 'BuildPlugin',
        '-plugin=%s' % abs_unrealcv_descriptor,
        '-package=%s' % abs_output_folder,
        '-rocket', '-targetplatforms=%s' % platform_name
    ], cwd = unrealcv_folder)


from typing import Tuple, Dict, Any, Optional, cast, Iterator
import torch
import torch.fx as fx
import torch.nn as nn
import itertools
from plinio.graph.features_calculation import ConstFeaturesCalculator, FeaturesCalculator
from .features_masker import PITFeaturesMasker
from .timestep_masker import PITTimestepMasker, PITFrozenTimestepMasker
from .dilation_masker import PITDilationMasker, PITFrozenDilationMasker
from .binarizer import PITBinarizer
from .module import PITModule

class PITConv1d(nn.Conv1d, PITModule):
    """A nn.Module implementing a Conv1D layer optimizable with the PIT NAS tool

    :param conv: the inner `torch.nn.Conv1D` layer to be optimized
    :type conv: nn.Conv1d
    :param out_features_masker: the `nn.Module` that generates the output features binary masks
    :type out_features_masker: PITChannelMasker
    :param timestep_masker: the `nn.Module` that generates the output timesteps binary masks
    :type timestep_masker: PITTimestepMasker
    :param dilation_masker: the `nn.Module` that generates the dilation binary masks
    :type dilation_masker: PITDilationMasker
    :raises ValueError: for unsupported regularizers
    :param binarization_threshold: the binarization threshold for PIT masks, defaults to 0.5
    :type binarization_threshold: float, optional
    :param discrete_cost: True if the layer cost should be computed on a discretized sample
    :type discrete_cost: bool, default False
    """
    def __init__(self,
                 conv: nn.Conv1d,
                 out_features_masker: PITFeaturesMasker,
                 timestep_masker: PITTimestepMasker,
                 dilation_masker: PITDilationMasker,
                 binarization_threshold: float = 0.5,
                 discrete_cost: bool = False,
                 ):
        super(PITConv1d, self).__init__(
            conv.in_channels,
            conv.out_channels,
            conv.kernel_size,
            conv.stride,
            conv.padding,
            conv.dilation,
            conv.groups,
            conv.bias is not None,
            conv.padding_mode)
        is_depthwise = (conv.groups == conv.in_channels and conv.groups == conv.out_channels)
        if conv.groups != 1 and (not is_depthwise):
            raise AttributeError(
                "PIT currently supports only full or DepthWise Conv., not other groupwise versions")
        with torch.no_grad():
            self.weight.copy_(conv.weight)
            if conv.bias is not None:
                cast(torch.nn.parameter.Parameter, self.bias).copy_(conv.bias)
            else:
                self.bias = None
        # this will be overwritten later when we process the model graph
        self._input_features_calculator = ConstFeaturesCalculator(conv.in_channels)
        self.out_features_masker = out_features_masker
        self.timestep_masker = timestep_masker
        self.dilation_masker = dilation_masker
        self.binarization_threshold = binarization_threshold
        self.discrete_cost = discrete_cost
        self.following_bn_args: Optional[Dict[str, Any]] = None
        _beta_norm, _gamma_norm = self._generate_norm_constants()
        self.register_buffer('_beta_norm', _beta_norm)
        self.register_buffer('_gamma_norm', _gamma_norm)

    def forward(self, input: torch.Tensor) -> torch.Tensor:
        """The forward function of the NAS-able layer.

        In a nutshell, uses the various maskers to generate the binarized masks, then runs
        the convolution with the masked weights tensor.

        :param input: the input activations tensor
        :type input: torch.Tensor
        :return: the output activations tensor
        :rtype: torch.Tensor
        """
        cout_mask = self._features_mask(discrete=True)
        pruned_weight = torch.mul(self.weight, cout_mask.unsqueeze(1).unsqueeze(1))
        time_mask = self._time_mask(discrete=True)
        pruned_weight = torch.mul(time_mask, pruned_weight)
        return self._conv_forward(input, pruned_weight, self.bias)

    @staticmethod
    def autoimport(n: fx.Node, mod: fx.GraphModule, fm: PITFeaturesMasker):
        """Create a new fx.Node relative to a PITConv1d layer, starting from the fx.Node
        of a nn.Conv1d layer, and replace it into the parent fx.GraphModule

        :param n: a fx.Node corresponding to a nn.Conv1d layer, with shape annotations
        :type n: fx.Node
        :param mod: the parent fx.GraphModule
        :type mod: fx.GraphModule
        :param fm: the output features masker to use for this layer
        :type fm: PITFeaturesMasker
        :raises TypeError: if the input fx.Node is not of the correct type
        """
        submodule = mod.get_submodule(str(n.target))
        if type(submodule) != nn.Conv1d:
            raise TypeError(f"Trying to generate PITConv1d from layer of type{type(submodule)}")
        # here, this is guaranteed
        submodule = cast(nn.Conv1d, submodule)
        rf = submodule.kernel_size[0]
        stride = submodule.stride if isinstance(submodule.stride, int) else submodule.stride[0]
        # PIT cannot optimize rf and dilation with stride != 1
        time_masker = PITFrozenTimestepMasker(rf) if stride != 1 else PITTimestepMasker(rf)
        dil_masker = PITFrozenDilationMasker(rf) if stride != 1 else PITDilationMasker(rf)
        new_submodule = PITConv1d(
            submodule,
            out_features_masker=fm,
            timestep_masker=time_masker,
            dilation_masker=dil_masker,
        )
        mod.add_submodule(str(n.target), new_submodule)

    @staticmethod
    def export(n: fx.Node, mod: fx.GraphModule):
        """Replaces a fx.Node corresponding to a PITConv1D layer, with a standard nn.Conv1D layer
        within a fx.GraphModule

        :param n: the node to be rewritten, corresponds to a Conv1D layer
        :type n: fx.Node
        :param mod: the parent module, where the new node has to be inserted
        :type mod: fx.GraphModule
        """
        submodule = mod.get_submodule(str(n.target))
        if type(submodule) != PITConv1d:
            raise TypeError(f"Trying to export a layer of type{type(submodule)}")
        # here, this is guaranteed
        submodule = cast(PITConv1d, submodule)
        cout_mask = submodule.features_mask.bool()
        cin_mask = submodule.input_features_calculator.features_mask.bool()
        time_mask = submodule.time_mask.bool()
        is_depthwise = (submodule.groups == submodule.in_channels) and (
            submodule.groups == submodule.out_channels)
        if is_depthwise:
            groups_opt = submodule.in_features_opt
        else:
            groups_opt = submodule.groups
        new_submodule = nn.Conv1d(
            submodule.in_features_opt,
            submodule.out_features_opt,
            submodule.kernel_size_opt,
            submodule.stride,
            submodule.padding,
            submodule.dilation_opt,
            groups_opt,
            submodule.bias is not None,
            submodule.padding_mode)
        new_weights = submodule.weight[cout_mask, :, :]
        if not is_depthwise:
            # for DWConv we have dimension 1 in the cin axis
            # note: we don't handle other groupwise variants yet
            new_weights = new_weights[:, cin_mask, :]
        new_weights = new_weights[:, :, time_mask]
        with torch.no_grad():
            new_submodule.weight.copy_(new_weights)
            if submodule.bias is not None:
                cast(nn.parameter.Parameter, new_submodule.bias).copy_(submodule.bias[cout_mask])
        mod.add_submodule(str(n.target), new_submodule)
        # Adjust Padding
        if submodule.padding in ('valid', 0, (0,)):
            pad_amount = (submodule.kernel_size_opt[0] - 1) * submodule.dilation_opt[0]
            new_pad = nn.ConstantPad1d(
                padding=(pad_amount, 0),
                value=0
            )
            # If explicit padding nn.Module exist is simply substituted with new_pad
            # else a new node is created and new_pad inserted
            for inp in n.args:
                inp = cast(fx.Node, inp)
                if inp.op == 'call_module':
                    if isinstance(mod.get_submodule(str(inp.target)), nn.ConstantPad1d):
                        mod.add_submodule(str(inp.target), new_pad)
                        break  # Found it, we can exit and go on
            else:  # Did not find anything
                mod.add_submodule(str(n.target) + "_pad", new_pad)
                with mod.graph.inserting_before(n):
                    new_node = mod.graph.call_module(
                        str(n.target) + "_pad",
                        args=n.args)
        # unfuse the BatchNorm
        if submodule.following_bn_args is not None:
            new_bn = nn.BatchNorm1d(
                submodule.out_features_opt,
                eps=submodule.following_bn_args['eps'],
                momentum=submodule.following_bn_args['momentum'],
                affine=submodule.following_bn_args['affine'],
                track_running_stats=submodule.following_bn_args['track_running_stats']
            )
            mod.add_submodule(str(n.target) + "_exported_bn", new_bn)
            # add the batchnorm just after the conv in the graph
            with mod.graph.inserting_after(n):
                new_node = mod.graph.call_module(
                    str(n.target) + "_exported_bn",
                    args=(n,)
                )
                n.replace_all_uses_with(new_node)
                # The previous line replaces also the input to the BN with the BN itself.
                # The following line fixes it. Not sure if there's a cleaner way to do this?
                new_node.replace_input_with(new_node, n)
        return

    def summary(self) -> Dict[str, Any]:
        """Export a dictionary with the optimized layer hyperparameters

        :return: a dictionary containing the optimized layer hyperparameter values
        :rtype: Dict[str, Any]
        """
        return {
            'in_features': self.in_features_opt,
            'out_features': self.out_features_opt,
            'kernel_size': self.kernel_size_opt,
            'dilation': self.dilation_opt
        }

    def named_nas_parameters(
            self, prefix: str = '', recurse: bool = False) -> Iterator[Tuple[str, nn.Parameter]]:
        """Returns an iterator over the architectural parameters (masks) of this layer, yielding
        both the name of the parameter as well as the parameter itself

        :param prefix: prefix to prepend to all parameter names.
        :type prefix: str
        :param recurse: kept for uniformity with pytorch API, but PITLayers never have sub-layers
        :type recurse: bool
        :return: an iterator over the architectural parameters (masks) of this layer
        :rtype: Iterator[nn.Parameter]
        """
        prfx = prefix
        prfx += "." if len(prefix) > 0 else ""
        for name, param in self.out_features_masker.named_parameters(
                prfx + "out_features_masker", recurse):
            yield name, param
        for name, param in self.timestep_masker.named_parameters(
                prfx + "timestep_masker", recurse):
            yield name, param
        for name, param in self.dilation_masker.named_parameters(
                prfx + "dilation_masker", recurse):
            yield name, param

    @property
    def out_features_opt(self) -> int:
        """Get the number of output features found during the search

        :return: the number of output features found during the search
        :rtype: int
        """
        with torch.no_grad():
            bin_alpha = self.features_mask
            return int(torch.sum(bin_alpha))

    @property
    def out_features_eff(self) -> torch.Tensor:
        return torch.sum(self._features_mask(self.discrete_cost))

    @property
    def k_eff(self) -> torch.Tensor:
        return torch.sum(self._time_mask(self.discrete_cost))

    @property
    def in_features_opt(self) -> int:
        """Get the number of input features found during the search

        :return: the number of input features found during the search
        :rtype: int
        """
        with torch.no_grad():
            bin_alpha = self.input_features_calculator.features_mask
            return int(torch.sum(bin_alpha))

    @property
    def dilation_opt(self) -> Tuple[int]:
        """Get the dilation found during the search

        :return: the dilation found during the search
        :rtype: Tuple[int]
        """
        with torch.no_grad():
            theta_gamma = self.dilation_masker.theta
            bin_theta_gamma = PITBinarizer.apply(theta_gamma, self.binarization_threshold)
            bin_theta_gamma = cast(torch.Tensor, bin_theta_gamma)
            # find the longest sequence of 0s in the bin_theta_gamma mask
            dil = max((sum(1 for _ in group) for value, group in itertools.groupby(bin_theta_gamma)
                      if value == 0), default=0) + 1
            # multiply times dilation[0] because the original layer could have had dilation > 1
            return (int(dil) * self.dilation[0],)

    @property
    def kernel_size_opt(self) -> Tuple[int]:
        """Get the kernel size found during the search

        :return: the kernel sizse found during the search
        :rtype: Tuple[int]
        """
        with torch.no_grad():
            return (int(torch.sum(self.time_mask)),)

    @property
    def features_mask(self) -> torch.Tensor:
        """Return the binarized mask that specifies which output features (channels) are kept by
        the NAS

        :return: the binarized mask over the features axis
        :rtype: torch.Tensor
        """
        with torch.no_grad():
            return self._features_mask(discrete=True)

    @property
    def time_mask(self) -> torch.Tensor:
        """Return the binarized mask that specifies which input timesteps are kept by the NAS

        This includes both the effect of dilation and receptive field searches

        :return: the binarized mask over the time axis
        :rtype: torch.Tensor
        """
        with torch.no_grad():
            return self._time_mask(discrete=True)

    def get_modified_vars(self) -> Dict[str, Any]:
        """Method that returns the modified vars(self) dictionary for the instance, used for
        cost computation

        :return: the modified vars(self) data structure
        :rtype: Dict[str, Any]
        """
        v = dict(vars(self))
        v['in_channels'] = self.input_features_calculator.features
        v['out_channels'] = self.out_features_eff
        v['kernel_size'] = self.k_eff
        # currently we don't know how to compute the "current dilation" in a differentiable way
        # during a search, so we set this to None to force cost models to fail if they use this
        # parameter
        v['dilation'] = None
        return v

    def _features_mask(self, discrete: bool) -> torch.Tensor:
        theta_alpha = self.out_features_masker.theta
        if discrete:
            theta_alpha = PITBinarizer.apply(theta_alpha, self.binarization_threshold)
        return cast(torch.Tensor, theta_alpha)

    def _time_mask(self, discrete: bool) -> torch.Tensor:
        theta_beta = self.timestep_masker.theta
        theta_gamma = self.dilation_masker.theta
        if discrete:
            theta_beta = PITBinarizer.apply(theta_beta, self.binarization_threshold)
            theta_gamma = PITBinarizer.apply(theta_gamma, self.binarization_threshold)
        else:
            # this normalizaton occurs only for the continuous case
            theta_beta = torch.mul(theta_beta, cast(torch.Tensor, self._beta_norm))
            theta_gamma = torch.mul(theta_gamma, cast(torch.Tensor, self._gamma_norm))
        bg_prod = torch.mul(cast(torch.Tensor, theta_gamma), cast(torch.Tensor, theta_beta))
        return bg_prod

    def _generate_norm_constants(self) -> Tuple[torch.Tensor, torch.Tensor]:
        """Method called at construction time to generate the normalization constants for the
        correct evaluation of the effective kernel size.

        The details of how these constants are computed are found in the journal paper.

        :return: A tuple of (beta, gamma) normalization constants.
        :rtype: Tuple[torch.Tensor, torch.Tensor]
        """
        beta_norm = torch.tensor(
            [1.0 / (self.kernel_size[0] - i) for i in range(self.kernel_size[0])],
        )
        # everything on the time-axis is flipped with respect to the paper
        beta_norm = torch.flip(beta_norm, (0,))
        gamma_norm = []
        for i in range(self.kernel_size[0]):
            k_i = 0
            for p in range(self.dilation_masker._gamma_len):
                k_i += 0 if i % 2**p == 0 else 1
            gamma_norm.append(1.0 / (self.dilation_masker._gamma_len - k_i))
        # everything on the time-axis is flipped with respect to the paper
        gamma_norm = torch.flip(torch.tensor(gamma_norm, dtype=torch.float32), (0,))
        return beta_norm, gamma_norm

    @property
    def input_features_calculator(self) -> FeaturesCalculator:
        """Returns the `FeaturesCalculator` instance that computes the number of input features for
        this layer.

        :return: the `FeaturesCalculator` instance that computes the number of input features for
        this layer.
        :rtype: FeaturesCalculator
        """
        return self._input_features_calculator

    @input_features_calculator.setter
    def input_features_calculator(self, calc: FeaturesCalculator):
        """Set the `FeaturesCalculator` instance that computes the number of input features for
        this layer.

        :param calc: the `FeaturesCalculator` instance that computes the number of input features
        for this layer
        :type calc: FeaturesCalculator
        """
        calc.register(self)
        self._input_features_calculator = calc

    @property
    def rf(self) -> int:
        """Returns the static (i.e., maximum) receptive field of this layer

        :return: the static (i.e., maximum) receptive field of this layer
        :rtype: int
        """
        return (self.kernel_size[0] - 1) * self.dilation[0] + 1

    @property
    def train_features(self) -> bool:
        """True if the output features are being optimized by PIT for this layer

        :return: True if the output features are being optimized by PIT for this layer
        :rtype: bool
        """
        return self.out_features_masker.trainable

    @train_features.setter
    def train_features(self, value: bool):
        """Set to True in order to let PIT optimize the output features for this layer

        :param value: set to True in order to let PIT optimize the output features for this layer
        :type value: bool
        """
        self.out_features_masker.trainable = value

    @property
    def train_rf(self) -> bool:
        """True if the receptive field is being optimized by PIT for this layer

        :return: True if the receptive field is being optimized by PIT for this layer
        :rtype: bool
        """
        return self.timestep_masker.trainable

    @train_rf.setter
    def train_rf(self, value: bool):
        """Set to True in order to let PIT optimize the receptive field for this layer

        :param value: set to True in order to let PIT optimize the receptive field for this layer
        :type value: bool
        """
        self.timestep_masker.trainable = value

    @property
    def train_dilation(self) -> bool:
        """True if the dilation is being optimized by PIT for this layer

        :return: True if the dilation is being optimized by PIT for this layer
        :rtype: bool
        """
        return self.dilation_masker.trainable

    @train_dilation.setter
    def train_dilation(self, value: bool):
        """Set to True in order to let PIT optimize the dilation for this layer

        :param value: set to True in order to let PIT optimize the dilation for this layer
        :type value: bool
        """
        self.dilation_masker.trainable = value


import tkinter as tk
from tkinter import filedialog
import pandas as pd
import PySimpleGUI as sg

# Define la ventana emergente
layout = [[sg.Text('Ingresa la nota:'), sg.InputText()],[sg.Button('Ok')]]

# Crea la ventana
window = sg.Window('Ingresar nota', layout)

# Loop para leer los eventos y datos de entrada
while True:
    event, values = window.read()
    if event == 'Ok':
        nota = values[0]
        break

# Cierra la ventana
window.close()

# Imprime la nota ingresada
print("Ultima nota:", nota)

file_path = filedialog.askopenfilename(title="Selecciona 'Captura Cliente'", filetypes=(("Archivo de Excel", "*.xlsx"),))
master_path = filedialog.askopenfilename(title="Selecciona Maestro de clientes", filetypes=(("Archivo de Excel", "*.xlsx"),))
store_path = filedialog.askopenfilename(title="Selecciona Consulta Inventario", filetypes=(("Archivo de Excel", "*.xlsx"),))

df = pd.read_excel(file_path)

## Crea una ventana emergente para que el usuario ingrese la primera nota
#nota = ""
#def get_nota():
#    global nota
#    global file_path
#    nota = entry_nota.get()
#    root_nota.destroy()
#
#    # Lee el archivo de Excel seleccionado con pandas
#    df = pd.read_excel(file_path, header=1)
#
#    # Selecciona solo las columnas necesarias
#    df_parser = df.iloc[:, [0, 1, 2, 3, 4, 6, 7, 8]]
#
#    print(nota)
#    print(len(df_parser.keys()))
#
#
#    sys.exit()
#
#
#
#root_nota = tk.Toplevel()
#root_nota.geometry("200x100") # Agrega esta línea para especificar el tamaño
#label_nota = tk.Label(root_nota, text="Ingresa la primera nota:")
#entry_nota = tk.Entry(root_nota)
#button_nota = tk.Button(root_nota, text="Aceptar", command=get_nota)
#
#label_nota.pack()
#entry_nota.pack()
#button_nota.pack()
#
#root_nota.mainloop()


from __future__ import annotations
from dataclasses import dataclass

import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate, optimize
import torch
from xitorch.optimize import rootfinder
from xitorch.integrate import quad
from xitorch.interpolate import Interp1D

DEFAULT_TEMPERATURE = torch.Tensor([8])

@dataclass
class ThermodynamicalProperties:
    temperature: torch.Tensor
    electron_density: torch.Tensor
    _chemical_potential: torch.Tensor
    chemical_potential_error: torch.Tensor
    dos: object
    fermi_energy: torch.Tensor | None = None

    @property
    def chemical_potential(self):
        raise NotImplementedError("Cannot use the ThermodynamicalProperties class directly.")

    @staticmethod
    def generic_thermal_factor(
        energy: torch.Tensor | float,
        chemical_potential: float,
        temperature: float
    ) -> torch.Tensor | float:
        """signal()
        Parameters
        ----------
        energy : energy of the electronic level [eV]
        chemical_potential : chemical potential [eV]
        temperature : Temperature of the system [eV]

        Returns
        -------
        Thermal factor of energy

        """
        if not isinstance(chemical_potential, torch.Tensor):
            chemical_potential = torch.tensor(chemical_potential).float()
        return 1.0 / (1 + torch.exp((chemical_potential - energy) / temperature))

    def thermal_factor(self, energy: torch.Tensor | float) -> torch.Tensor | float:
        thermal_fac = ThermodynamicalProperties.generic_thermal_factor(
            energy.detach(), self.chemical_potential, self.temperature
        )
        return thermal_fac

class ApproximateThermals(ThermodynamicalProperties):
    @staticmethod
    def fermi_energy_solver(
        dos: object,
        electron_density: float,
        fermi_guess: float = 1.
    ) -> torch.Tensor:
        energies = dos.energies.detach().numpy()
        delta = energies[1] - energies[0]

        def integrand(fermi_energy):
            return np.where(energies < fermi_energy, dos.density, 0)

        def difference(fermi_energy):
            integral = integrate.trapz(integrand(fermi_energy), dx=delta)

            return electron_density - integral

        vfunc = np.vectorize(difference)

        fermi_energy_exit = optimize.fsolve(vfunc, fermi_guess)

        error = difference(fermi_energy_exit)
        return torch.tensor(fermi_energy_exit).float()

    @staticmethod
    def chemical_potential_fit(temp: float, fermi_energy: float) -> float:
        '''
        Parameters
        ----------
        temp: temperature of the system
        fermi_energy: fermi energy of the system

        Returns
        -------
        Chemical potential approximation for the given fermi energy and temperature
        '''

        # uniform electron gas
        # fermi_energy = hartree * 0.5 * (3 * np.pi ** 2 * electron_density) ** (2 / 3)

        part_1 = -0.28468 - 1.5 * torch.log(temp / fermi_energy)

        part_2_num = (
            0.25945 * (temp / fermi_energy) ** (-1.858)
            + 0.072 * (temp / fermi_energy) ** (-0.929)
        )

        part_2_denom = (1 + 0.25945 * (temp / fermi_energy) ** (-0.858))

        eta = part_1 + part_2_num / part_2_denom
        return eta * temp

    @property
    def chemical_potential(self):
        return ApproximateThermals.chemical_potential_fit(
            self.temperature,
            self.fermi_energy.detach()
        )

    @classmethod
    def create(
        cls,
        dos: object,
        temperature: float,
        electron_density: float,
    ) -> ThermodynamicalProperties:
        # This separates out the fitting of the fermi energy from the chemical potential
        fermi_energy = ApproximateThermals.fermi_energy_solver(dos, electron_density)
        chemical_potential = ApproximateThermals.chemical_potential_fit(
            temperature,
            fermi_energy
        )

        return cls(
            temperature=temperature.requires_grad_().float(),
            electron_density=electron_density,
            _chemical_potential=chemical_potential,
            chemical_potential_error=0.0,  # This is not accurate (there is a propagated error)
            dos=dos,
            fermi_energy=fermi_energy
        )

class ExactThermals(ThermodynamicalProperties):
    @property
    def chemical_potential(self):
        return self._chemical_potential

    @staticmethod
    def chemical_potential_solver(
        temperature: float,
        chem_pot_guess: float,
        rho: float,
        dos: torch.Tensor,
        energies: torch.Tensor
    ) -> tuple[float, float]:
        """
        Parameters
        ----------
        temperature : temperature of the system (in eV)
        rho : density of the system
              (# of electrons in the bands present in the DOS taken into consideration / V)
        dos : Density of states (# states/ (eV*V))

        Warning: The volume for rho and the DOS must be expressed in the same units

        Returns
        -------
        Chemical potential

        """
        temperature = temperature.detach().numpy()
        rho = rho.detach().numpy()
        dos = dos.detach()
        energies = energies.detach().numpy()

        delta = energies[1] - energies[0]

        def integrand(mu_prime):
            # vectorized version should be faster than loop
            def partial_thermal(energy):
                return ThermodynamicalProperties.generic_thermal_factor(
                    energy, mu_prime, temperature
                )

            return dos * (1 - partial_thermal(energies))

        def difference(chemical_potential):
            integral = integrate.trapz(integrand(chemical_potential), dx=delta)

            return rho - integral

        vfunc = np.vectorize(difference)

        chem_pot_exit = optimize.fsolve(vfunc, chem_pot_guess)

        error = difference(chem_pot_exit)

        return chem_pot_exit, error

    @classmethod
    def create(
        cls,
        dos: object,
        temperature: float,
        electron_density: float,
        guess: float = 1,
        chemical_potential: torch.Tensor | None = None,
    ) -> ThermodynamicalProperties:
        if chemical_potential is None:
            (
                chemical_potential,
                error,
            ) = ExactThermals.chemical_potential_solver(
                temperature=temperature,
                chem_pot_guess=guess,
                rho=electron_density,
                dos=dos.density,
                energies=dos.energies,
            )

            # assert abs(error / chemical_potential) < 1e-5
            chemical_potential = torch.tensor(chemical_potential)
        else:
            error = 0.0

        return cls(
            temperature=temperature,
            electron_density=electron_density,
            _chemical_potential=chemical_potential,
            chemical_potential_error=torch.tensor(error),
            dos=dos,
        )

class DifferentiableExactThermals(ExactThermals):

    @property
    def chemical_potential(self):
        return DifferentiableExactThermals.chemical_potential_solver(
            self.temperature.detach(),
            torch.tensor([1.]).detach(),
            self.electron_density.detach(),
            self.dos.density.detach(),
            self.dos.energies.detach()
        )[0]

    @classmethod
    def create(
        cls,
        dos: object,
        temperature: float,
        electron_density: float,
        guess: float = 1,
        chemical_potential: torch.Tensor | None = None,
    ) -> ThermodynamicalProperties:
        return super(DifferentiableExactThermals, cls).create(
            dos,
            temperature.requires_grad_().float(),
            electron_density,
            guess=guess,
            chemical_potential=chemical_potential
        )

def get_thermals(
    dos: object,
    temperature: torch.Tensor,
    electron_density: torch.Tensor,
    thermals_type: str = "exact",
    kwargs: dict[str, object] | None = None
) -> ThermodynamicalProperties:
    """Factory to create different subclasses of ThermodynamicalProperties.

    Args:
        dos:
        temperature:
        electron_density:
        approximate:
        kwargs:

    Returns:

    """
    if kwargs is None:
        kwargs = {}
    assert thermals_type in ("exact", "approximate", "exact_differentiable")
    if thermals_type == "approximate":
        return ApproximateThermals.create(dos, temperature, electron_density, **kwargs)
    elif thermals_type == "exact_differentiable":
        return DifferentiableExactThermals.create(dos, temperature, electron_density, **kwargs)
    return ExactThermals.create(dos, temperature, electron_density, **kwargs)


import numpy as np
import copy
from collections import defaultdict
import sys
import warnings
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, random_split
from torchvision import transforms
from matplotlib import pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
import sklearn.metrics as metrics
from sklearn.metrics.pairwise import cosine_distances, euclidean_distances
from sklearn.preprocessing import normalize
import scipy

def fx_calc_map_label(image, text, label, k = 0, dist_method='euclidean'):
  if dist_method == 'euclidean':
    dist = scipy.spatial.distance.cdist(image, text, 'euclidean')
  elif dist_method == 'COS':
    dist = scipy.spatial.distance.cdist(image, text, 'cosine')
  ord = dist.argsort()
  numcases = dist.shape[0]
  if k == 0:
    k = numcases
  res = []
  for i in range(numcases):
    order = ord[i]
    p = 0.0
    r = 0.0
    for j in range(k):
      if label[i] == label[order[j]]:
        r += 1
        p += (r / (j + 1))
    if r > 0:
      res += [p / r]
    else:
      res += [0]
  return np.mean(res)

def compute_topk(X_train, y_train, X_test, y_test):
    print('X_train range: ', np.min(X_train), np.amax(X_train))
    print('X_test  range: ', np.min(X_test), np.amax(X_test))

    ks = [1, 5, 10, 20, 50]
    topk_correct = {k:0 for k in ks}

    distances = euclidean_distances(X_test, X_train)
    # distances = cosine_distances(X_test, X_train)
    indices = np.argsort(distances)

    for k in ks:
        # print(k)
        top_k_indices = indices[:, :k]
        # print(top_k_indices.shape, y_test.shape)
        for ind, test_label in zip(top_k_indices, y_test):
            labels = y_train[ind]
            if test_label in labels:
                # print(test_label)
                topk_correct[k] += 1

    for k in ks:
        correct = topk_correct[k]
        total = len(X_test)
        print('Top-{}, correct = {:.2f}, total = {}, acc = {:.3f}'.format(k, correct, total, correct/total))

    max_rank = 100

    qf = X_test
    gf = X_train
    g_pids = y_train
    q_pids = y_test

    # qf = torch.from_numpy(X_test)

    # gf = torch.from_numpy(X_train)
    # g_pids = torch.from_numpy(y_train)
    # q_pids = torch.from_numpy(y_test)

    # m, n = qf.size(0), gf.size(0)
    # distmat = torch.pow(qf, 2).sum(dim=1, keepdim=True).expand(m, n) + \
    #           torch.pow(gf, 2).sum(dim=1, keepdim=True).expand(n, m).t()
    # distmat.addmm_(1, -2, qf, gf.t())

    distmat = euclidean_distances(qf, gf)

    num_q, num_g = distmat.shape

    if num_g < max_rank:
        max_rank = num_g
        print("Note: number of gallery samples is quite small, got {}".format(num_g))

    indices = np.argsort(distmat, axis=1)
    matches = (g_pids[indices] == q_pids[:, np.newaxis]).astype(np.int32)

    # compute cmc curve for each query
    all_cmc = []
    all_AP = []
    num_valid_q = 0. # number of valid query

    for q_idx in range(num_q):
        # print('XXXXXXXXXXXXXXX')
        # get query pid and camid
        # q_pid = q_pids[q_idx]
        # print('q_pid === ', q_pid)
        # q_camid = q_camids[q_idx]

        # remove gallery samples that have the same pid and camid with query
        # order = indices[q_idx]
        # remove = (g_pids[order] == q_pid)
        # keep = np.invert(remove)

        # compute cmc curve
        raw_cmc = matches[q_idx] # binary vector, positions with value 1 are correct matches
        if not np.any(raw_cmc):
            # this condition is true when query identity does not appear in gallery
            continue

        cmc = raw_cmc.cumsum()
        cmc[cmc > 1] = 1

        all_cmc.append(cmc[:max_rank])
        num_valid_q += 1.

        # compute average precision
        # reference: https://en.wikipedia.org/wiki/Evaluation_measures_(information_retrieval)#Average_precision
        num_rel = raw_cmc.sum()
        tmp_cmc = raw_cmc.cumsum()
        tmp_cmc = [x / (i+1.) for i, x in enumerate(tmp_cmc)]
        tmp_cmc = np.asarray(tmp_cmc) * raw_cmc
        AP = tmp_cmc.sum() / num_rel
        all_AP.append(AP)

    assert num_valid_q > 0, "Error: all query identities do not appear in gallery"

    all_cmc = np.asarray(all_cmc).astype(np.float32)
    all_cmc = all_cmc.sum(0) / num_valid_q
    # print('all_ap ====', len(all_AP))
    mAP = np.mean(all_AP)
    # print(all_cmc, mAP)
    print('the mAP is: ',mAP)

    return all_cmc, mAP

# how to calculate mAP
def topk_retrieval():
    """Evaluation with market1501 metric
    Key: for each query identity, its gallery images from the same camera view are discarded.
    """

    img_pairs = 18
    dataset = 'ModelNet40'
    exp_name = 'ModelNet40-pt1024-mesh-img56-Xentropy-Xcontrast-PointMultiAgreement-T095-Fused-Warmup-2percent_task4_p2_w2'

    train_img_X_name = './extracted_features/%s/%s-%s-%s_NtXent_img_feat.npy'%(exp_name, dataset, 'train', img_pairs)
    test_img_X_name = './extracted_features/%s/%s-%s-%s_NtXent_img_feat.npy'%(exp_name, dataset, 'test', img_pairs)

    train_pt_X_name = './extracted_features/%s/%s-%s_NtXent_cloud1024_feat.npy'%(exp_name, dataset, 'train')
    test_pt_X_name = './extracted_features/%s/%s-%s_NtXent_cloud1024_feat.npy'%(exp_name, dataset, 'test')

    train_mesh_X_name = './extracted_features/%s/%s-%s_NtXent_mesh_feat.npy'%(exp_name, dataset, 'train')
    test_mesh_X_name = './extracted_features/%s/%s-%s_NtXent_mesh_feat.npy'%(exp_name, dataset, 'test')

    train_label_name = './extracted_features/%s/%s-%s_NtXent_label.npy'%(exp_name, dataset, 'train')
    test_label_name = './extracted_features/%s/%s-%s_NtXent_label.npy'%(exp_name, dataset, 'test')

    train_pt_X = np.load(train_pt_X_name)
    test_pt_X = np.load(test_pt_X_name)

    train_img_X = np.load(train_img_X_name)
    test_img_X = np.load(test_img_X_name)

    train_mesh_X = np.load(train_mesh_X_name)
    test_mesh_X = np.load(test_mesh_X_name)

    train_label = np.load(train_label_name)
    test_label = np.load(test_label_name)

    print('--------------------------------------------')
    print('The value of features for point cloud should be from -2 to 2')
    print(np.amin(train_pt_X), np.amax(train_pt_X))
    print('--------------------------------------------')
    print('The value of features for mesh should be from -2 to 2')
    print(np.amin(train_mesh_X), np.amax(train_mesh_X))
    print('--------------------------------------------')
    print('The value of features forimage features')
    print(np.amin(train_img_X), np.amax(train_img_X))
    print('--------------------------------------------')

    print('**********************************************************')
    print('performance of base feature accuracy')

    train_pt_X = normalize(train_pt_X[:512], norm='l1', axis=1)
    test_pt_X = normalize(test_pt_X[:512], norm='l1', axis=1)
    train_img_X = normalize(train_img_X[:512], norm='l1', axis=1)
    test_img_X = normalize(test_img_X[:512], norm='l1', axis=1)
    train_mesh_X = normalize(train_mesh_X[:512], norm='l1', axis=1)
    test_mesh_X = normalize(test_mesh_X[:512], norm='l1', axis=1)

#     train_pt_X = train_pt_X[:512]
#     test_pt_X = test_pt_X[:512]
#     train_img_X = train_img_X[:512]
#     test_img_X = test_img_X[:512]
#     train_mesh_X = train_mesh_X[:512]
#     test_mesh_X = test_mesh_X[:512]

    print('---------  image -> image --------')
    # compute_topk(train_img_X, label_train, test_img_X, label_test)
    img2img = fx_calc_map_label(test_img_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(img2img)

    print('---------  cloud -> cloud --------')
    # compute_topk(train_pt_X, label_train, test_pt_X, test_label)
    pt2pt = fx_calc_map_label(test_pt_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(pt2pt)

    print('---------  mesh -> mesh --------')
    # compute_topk(train_mesh_X, label_train, test_mesh_X, test_label)
    mesh2mesh = fx_calc_map_label(test_mesh_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2mesh)

    print('---------  image -> point cloud --------')
    # compute_topk(train_img_X, label_train, test_pt_X, test_label)
    img2pt = fx_calc_map_label(test_img_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(img2pt)

    print('---------  image -> mesh --------')
    # compute_topk(train_img_X, label_train, test_mesh_X, test_label)
    img2mesh = fx_calc_map_label(test_img_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(img2mesh)

    print('---------  point cloud -> image --------')
    # compute_topk(train_pt_X, label_train, test_img_X, test_label)
    pt2img = fx_calc_map_label(test_pt_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(pt2img)

    print('---------  point cloud -> mesh --------')
    # compute_topk(train_pt_X, label_train, test_mesh_X, test_label)
    pt2mesh = fx_calc_map_label(test_pt_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(pt2mesh)

    print('---------  mesh -> images --------')
    # compute_topk(train_mesh_X, label_train, test_img_X, test_label)
    mesh2img = fx_calc_map_label(test_mesh_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2img)

    print('---------  mesh -> point cloud --------')
    # compute_topk(train_mesh_X, label_train, test_pt_X, test_label)
    mesh2pt = fx_calc_map_label(test_mesh_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2pt)

    print('*******************************************************************')
    print('performance of invariant feature')

    train_pt_X = normalize(train_pt_X[:, 512:-256], norm='l1', axis=1)
    test_pt_X = normalize(test_pt_X[:, 512:-256], norm='l1', axis=1)
    train_img_X = normalize(train_img_X[:, 512:-256], norm='l1', axis=1)
    test_img_X = normalize(test_img_X[:, 512:-256], norm='l1', axis=1)
    train_mesh_X = normalize(train_mesh_X[:, 512:-256], norm='l1', axis=1)
    test_mesh_X = normalize(test_mesh_X[:, 512:-256], norm='l1', axis=1)

#     train_pt_X = train_pt_X[:, 512:-256]
#     test_pt_X = test_pt_X[:, 512:-256]
#     train_img_X = train_img_X[:, 512:-256]
#     test_img_X = test_img_X[:, 512:-256]
#     train_mesh_X = train_mesh_X[:, 512:-256]
#     test_mesh_X = test_mesh_X[:, 512:-256]

    print('---------  image -> image --------')
    # compute_topk(train_img_X, label_train, test_img_X, label_test)
    img2img = fx_calc_map_label(test_img_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(img2img)

    print('---------  cloud -> cloud --------')
    # compute_topk(train_pt_X, label_train, test_pt_X, test_label)
    pt2pt = fx_calc_map_label(test_pt_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(pt2pt)

    print('---------  mesh -> mesh --------')
    # compute_topk(train_mesh_X, label_train, test_mesh_X, test_label)
    mesh2mesh = fx_calc_map_label(test_mesh_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2mesh)

    print('---------  image -> point cloud --------')
    # compute_topk(train_img_X, label_train, test_pt_X, test_label)
    img2pt = fx_calc_map_label(test_img_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(img2pt)

    print('---------  image -> mesh --------')
    # compute_topk(train_img_X, label_train, test_mesh_X, test_label)
    img2mesh = fx_calc_map_label(test_img_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(img2mesh)

    print('---------  point cloud -> image --------')
    # compute_topk(train_pt_X, label_train, test_img_X, test_label)
    pt2img = fx_calc_map_label(test_pt_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(pt2img)

    print('---------  point cloud -> mesh --------')
    # compute_topk(train_pt_X, label_train, test_mesh_X, test_label)
    pt2mesh = fx_calc_map_label(test_pt_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(pt2mesh)

    print('---------  mesh -> images --------')
    # compute_topk(train_mesh_X, label_train, test_img_X, test_label)
    mesh2img = fx_calc_map_label(test_mesh_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2img)

    print('---------  mesh -> point cloud --------')
    # compute_topk(train_mesh_X, label_train, test_pt_X, test_label)
    mesh2pt = fx_calc_map_label(test_mesh_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2pt)

    print('*******************************************************************')
    print('performance of modality-specific feature')

    train_pt_X = normalize(train_pt_X[:, -256:], norm='l1', axis=1)
    test_pt_X = normalize(test_pt_X[:, -256:], norm='l1', axis=1)
    train_img_X = normalize(train_img_X[:, -256:], norm='l1', axis=1)
    test_img_X = normalize(test_img_X[:, -256:], norm='l1', axis=1)
    train_mesh_X = normalize(train_mesh_X[:, -256:], norm='l1', axis=1)
    test_mesh_X = normalize(test_mesh_X[:, -256:], norm='l1', axis=1)

#     train_pt_X = train_pt_X[:, -256:]
#     test_pt_X = test_pt_X[:, -256:]
#     train_img_X = train_img_X[:, -256:]
#     test_img_X = test_img_X[:, -256:]
#     train_mesh_X = train_mesh_X[:, -256:]
#     test_mesh_X = test_mesh_X[:, -256:]

    print('---------  image -> image --------')
    # compute_topk(train_img_X, label_train, test_img_X, label_test)
    img2img = fx_calc_map_label(test_img_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(img2img)

    print('---------  cloud -> cloud --------')
    # compute_topk(train_pt_X, label_train, test_pt_X, test_label)
    pt2pt = fx_calc_map_label(test_pt_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(pt2pt)

    print('---------  mesh -> mesh --------')
    # compute_topk(train_mesh_X, label_train, test_mesh_X, test_label)
    mesh2mesh = fx_calc_map_label(test_mesh_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2mesh)

    print('---------  image -> point cloud --------')
    # compute_topk(train_img_X, label_train, test_pt_X, test_label)
    img2pt = fx_calc_map_label(test_img_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(img2pt)

    print('---------  image -> mesh --------')
    # compute_topk(train_img_X, label_train, test_mesh_X, test_label)
    img2mesh = fx_calc_map_label(test_img_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(img2mesh)

    print('---------  point cloud -> image --------')
    # compute_topk(train_pt_X, label_train, test_img_X, test_label)
    pt2img = fx_calc_map_label(test_pt_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(pt2img)

    print('---------  point cloud -> mesh --------')
    # compute_topk(train_pt_X, label_train, test_mesh_X, test_label)
    pt2mesh = fx_calc_map_label(test_pt_X, test_mesh_X, test_label, k = 0, dist_method='euclidean')
    print(pt2mesh)

    print('---------  mesh -> images --------')
    # compute_topk(train_mesh_X, label_train, test_img_X, test_label)
    mesh2img = fx_calc_map_label(test_mesh_X, test_img_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2img)

    print('---------  mesh -> point cloud --------')
    # compute_topk(train_mesh_X, label_train, test_pt_X, test_label)
    mesh2pt = fx_calc_map_label(test_mesh_X, test_pt_X, test_label, k = 0, dist_method='euclidean')
    print(mesh2pt)

if __name__ == '__main__':
    topk_retrieval()
    # print('mAP ===== ', mAP)
    # print(all_cmc)
    # ks = [1, 5, 10, 20, 50]
    # for i in ks:
    #     print(i, ' = ', all_cmc[i-1])


import numpy as np
import pandas as pd

header = ['user_id', 'item_id', 'rating', 'timestamp']
df = pd.read_csv('../ml-100k/u.data', sep='\t', names=header)

n_users = df.user_id.unique().shape[0]
n_items = df.item_id.unique().shape[0]
print('Number of users = ' + str(n_users) + ' | Number of movies = ' + str(n_items))

from sklearn import cross_validation as cv

train_data, test_data = cv.train_test_split(df, test_size=0.25)

# Create two user-item matrices, one for training and another for testing
train_data_matrix = np.zeros((n_users, n_items))
for line in train_data.itertuples():
    train_data_matrix[line[1] - 1, line[2] - 1] = line[3]

test_data_matrix = np.zeros((n_users, n_items))
for line in test_data.itertuples():
    test_data_matrix[line[1] - 1, line[2] - 1] = line[3]

from sklearn.metrics.pairwise import pairwise_distances

user_similarity = pairwise_distances(train_data_matrix, metric='cosine')
item_similarity = pairwise_distances(train_data_matrix.T, metric='cosine')


import yfinance as yf
import json
import os
from datetime import datetime
from src.definitions import DATA_ROOT, CONTENT_ROOT
import pandas as pd

today = datetime.today().strftime('%Y-%m-%d')

def read_json():

    """
    Read and parse the data-generation paramters from the data.json file.
    These paramaters are primary used to add/remove features when fetching from yfinance.

    :return: tickers, start, end, period, interval, auto_adjust, prepost, time_specific, threads
    """

    period = ""
    start = ""
    end = ""
    data_json = os.path.join(CONTENT_ROOT, "src/data/data.json")
    with open(data_json) as f:
        json_data = json.load(f)

        time_specific = json_data["specify_period"]

        print(json_data)
        tickers = " ".join([i for i in json_data["tickers"]]).strip()
        if time_specific:
            start = json_data["spec_period"][0]["start_date"]
            end = json_data["spec_period"][1]["end_date"]
        else:
            period = json_data["time_period"]

        interval = json_data["time_frequency"]

        auto_adjust = json_data["auto_adjust"]

        prepost = json_data["prepost"]

        threads = json_data["threads"]
    return tickers, start, end, period, interval, auto_adjust, prepost, time_specific, threads

tickers, start, end, period, interval, auto_adjust, prepost, time_specific, threads = read_json()

# date: YYYY-MM-DD
def use_history():
    """
    Fetch data from yfinance using the specified paramaterers in data.json and generate the data_csvs directory.

    :return: None
    """
    for ticker in tickers.split(" "):
        curr_ticker = yf.Ticker(ticker)

        if time_specific:
            data = curr_ticker.history(period=period, interval=interval, start=start, end=end, prepost=prepost, auto_adjust=auto_adjust,
                                       actions=True)
            filename = ticker + "_" + start + "_" + end + "_" + today + ".csv"
        else:
            data = curr_ticker.history(period=period, interval=interval, prepost=prepost, auto_adjust=auto_adjust,
                                       actions=True)
            filename = ticker + "_" + period + "_" + today + ".csv"

        try:
            data_file = os.path.join(DATA_ROOT, filename)
            data.to_csv(data_file)
        except FileExistsError:
            print("This file already exists.")

if __name__ == "__main__":
    if not os.path.exists(DATA_ROOT):
        os.mkdir(DATA_ROOT)
    use_history()


import numpy as np
import contextlib
import torch
from collections import deque
import matplotlib.pyplot as plt

# Configures numpy print options
@contextlib.contextmanager
def _printoptions(*args, **kwargs):
    original = np.get_printoptions()
    np.set_printoptions(*args, **kwargs)
    try:
        yield
    finally:
        np.set_printoptions(**original)

class EnvironmentModel:
    def __init__(self, n_states, n_actions, seed=None):
        self.n_states = n_states
        self.n_actions = n_actions

        self.random_state = np.random.RandomState(seed)

    def p(self, next_state, state, action):
        raise NotImplementedError()

    def r(self, next_state, state, action):
        raise NotImplementedError()

    def draw(self, state, action):
        p = [self.p(ns, state, action) for ns in range(self.n_states)]
        next_state = self.random_state.choice(self.n_states, p=p)
        reward = self.r(next_state, state, action)

        return next_state, reward

class Environment(EnvironmentModel):
    def __init__(self, n_states, n_actions, max_steps, pi, seed=None):
        EnvironmentModel.__init__(self, n_states, n_actions, seed)

        self.max_steps = max_steps

        self.pi = pi
        if self.pi is None:
            self.pi = np.full(n_states, 1./n_states)

    def reset(self):
        self.n_steps = 0
        self.state = self.random_state.choice(self.n_states, p=self.pi)

        return self.state

    def step(self, action):
        if action < 0 or action >= self.n_actions:
            raise Exception('Invalid action.')

        self.n_steps += 1
        done = (self.n_steps >= self.max_steps)

        self.state, reward = self.draw(self.state, action)

        return self.state, reward, done

    def render(self, policy=None, value=None):
        raise NotImplementedError()

class FrozenLake(Environment):
    def __init__(self, lake, slip, max_steps, seed=None):
        """
        lake: A matrix that represents the lake. For example:
        lake =  [['&', '.', '.', '.'],
                ['.', '#', '.', '#'],
                ['.', '.', '.', '#'],
                ['#', '.', '.', '$']]
        slip: The probability that the agent will slip
        max_steps: The maximum number of time steps in an episode
        seed: A seed to control the random number generator (optional)
        """
        # start (&), frozen (.), hole (#), goal ($)
        self.lake = np.array(lake)
        self.lake_flat = self.lake.reshape(-1)

        self.size = len(self.lake_flat)
        self.width = int(np.sqrt(self.size))

        self.holeStates = []
        for i in range(len(self.lake_flat)):
            if self.lake_flat[i] == '#': self.holeStates.append(i)

        self.slip = slip

        n_states = self.lake.size + 1
        n_actions = 4

        pi = np.zeros(n_states, dtype=float)
        pi[np.where(self.lake_flat == '&')[0]] = 1.0

        self.absorbing_state = n_states - 1

        Environment.__init__(self, n_states, n_actions, max_steps, pi, seed=seed)

    actions = {'w':0, 'a':1, 's':2, 'd':3}

    def step(self, action):
        state, reward, done = Environment.step(self, action)

        done = (state == self.absorbing_state) or done

        return state, reward, done

    def isAbsorbingState(self, state): return state == self.size
    def isStartState(self, state): return state == np.where(self.lake_flat == '&')[0][0]
    def isGoalState(self, state): return state == np.where(self.lake_flat == '$')[0][0]
    def isHoleState(self, state): return state in self.holeStates

    def p(self, next_state, state, action):
        if self.isAbsorbingState(state) or self.isGoalState(state) or self.isHoleState(state):
            return self.isAbsorbingState(next_state)
        elif self.isAbsorbingState(next_state): return 0

        # Describes [y, x] movement of the action
        actions = [[-1, 0], [0, -1], [1, 0], [0, 1]]
        a = actions[action]
        w = self.width
        ps = np.ndarray((w, w))
        ps.fill(0)
        getCoords = lambda a: (np.array([state//w, state%w]) + np.array(a)).clip(0, self.width - 1)
        ps[getCoords(a)[0], getCoords(a)[1]] = 1 - self.slip
        for _a in actions: ps[getCoords(_a)[0], getCoords(_a)[1]] += self.slip / self.n_actions
        return ps.flatten()[next_state]

    def r(self, _, state, __):
        return 1 if self.isGoalState(state) else 0

    def render(self, policy=None, value=None):
        if policy is None:
            lake = np.array(self.lake_flat)

            if self.state < self.absorbing_state:
                lake[self.state] = '@'

            print(lake.reshape(self.lake.shape))
        else:
            # UTF-8 arrows look nicer, but cannot be used in LaTeX
            # https://www.w3schools.com/charsets/ref_utf_arrows.asp
            actions = ['^', '<', '_', '>']

            print('Lake:')
            print(self.lake)

            print('Policy:')
            policy = np.array([actions[a] for a in policy[:-1]])
            print(policy.reshape(self.lake.shape))

            print('Value:')
            with _printoptions(precision=3, suppress=True):
                print(value[:-1].reshape(self.lake.shape))

def play(env):
    actions = ['w', 'a', 's', 'd']

    state = env.reset()
    env.render()

    done = False
    while not done:
        c = input('\nMove: ')
        if c not in actions:
            raise Exception('Invalid action')

        state, r, done = env.step(actions.index(c))

        env.render()
        print('Reward: {0}.'.format(r))

def policy_evaluation(env, policy, gamma, theta, max_iterations):
    value = np.zeros(env.n_states, dtype=float)
    inner = lambda s, a: sum([env.p(s_, s, a) * (env.r(s_, s, a) + gamma * value[s_]) for s_ in range(env.n_states)])
    outer = lambda s: inner(s, policy[s])
    for _ in range(max_iterations):
        v = value.copy();
        value = np.array([outer(s) for s in range(env.n_states)])
        if max([abs(v[i] - value[i]) for i in range(len(value))]) < theta: break
    print(f'Policy Evaluation: Number of Iterations: {_+1}')
    return value

def policy_improvement(env, value, gamma):
    inner = lambda s, a: sum([env.p(s_, s, a) * (env.r(s_, s, a) + gamma * value[s_]) for s_ in range(env.n_states)])
    outer = lambda s: np.argmax([inner(s, a) for a in range(env.n_actions)])
    return np.array([outer(s) for s in range(env.n_states)])

def policy_iteration(env, gamma, theta, max_iterations, policy=None):
    if policy is None:
        policy = np.zeros(env.n_states, dtype=int)
    else:
        policy = np.array(policy, dtype=int)
    value = policy_evaluation(env, policy, gamma, theta, max_iterations)

    old_policy = None
    i = 0
    while not np.array_equal(policy, old_policy):
        old_policy = policy.copy()
        policy = policy_improvement(env, value, gamma)
        value = policy_evaluation(env, policy, gamma, theta, max_iterations)
        i += 1
    print(f'Policy Iteration: Number of Iterations: {i}')

    return policy, value

def value_iteration(env, gamma, theta, max_iterations, value=None):
    value = np.zeros(env.n_states) if value is None else np.array(value, dtype=np.float)
    # Define math function as outer
    inner = lambda s, a: sum([env.p(s_, s, a) * (env.r(s_, s, a) + gamma * value[s_]) for s_ in range(env.n_states)])
    outer = lambda s: np.max([inner(s, a) for a in range(env.n_actions)])
    # Iterate
    for _ in range(max_iterations):
        _value = value.copy()
        value = np.array([outer(s) for s in range(env.n_states)])
        if max([abs(_value[i] - value[i]) for i in range(len(value))]) < theta: break
    print(f'Value Iteration: Number of Iterations: {_+1}')
    inner = lambda s, a: sum([env.p(s_, s, a) * value[s_] for s_ in range(env.n_states)])
    outer = lambda s: np.argmax([inner(s, a) for a in range(env.n_actions)])
    policy = np.array([outer(s) for s in range(env.n_states)])
    return policy, value

#region Later

# Returns an argmax from input arr, but randomizes tie breakers
def rand_argmax(arr, random_state = None):
    if random_state is None: random_state = np.random.RandomState()
    max_filtered_arr = [i for i in range(len(arr)) if arr[i] == max(arr)]
    return max_filtered_arr[random_state.randint(0, len(max_filtered_arr))]

# Return action a according to an ε-greedy policy based on Q
# Q should be passed to only include state s, and length of it will be n_actions
def eps_greedy_action(q, epsilon, random_state = None):
    if random_state is None: random_state = np.random.RandomState()
    return rand_argmax(q, random_state) if random_state.uniform() > epsilon else random_state.randint(0, len(q))

def sarsa(env, max_episodes, eta, gamma, epsilon, seed=None):
    random_state = np.random.RandomState(seed)

    eta = np.linspace(eta, 0, max_episodes)
    epsilon = np.linspace(epsilon, 0, max_episodes)

    q = np.zeros((env.n_states, env.n_actions))

    print()
    q_iterations = []
    for i in range(max_episodes):
        s = env.reset()
        a = eps_greedy_action(q[s], epsilon[i], random_state)
        done = False
        while not done:
            s_, r, done = env.step(a)
            a_ = eps_greedy_action(q[s_], epsilon[i], random_state)
            q[s, a] += eta[i] * (r + gamma*q[s_, a_] - q[s,a])
            s, a = s_, a_
        q_iterations += [q.max(axis=1).mean()]
    mp = np.convolve(q_iterations, np.ones(20)/20, mode='valid')
    plt.clf()
    plt.cla()
    plt.plot(np.arange(1, len(mp) + 1), mp)
    plt.savefig('plots/sarsa.png')

    policy = q.argmax(axis=1)
    value = q.max(axis=1)

    return policy, value

def q_learning(env, max_episodes, eta, gamma, epsilon, seed=None):
    random_state = np.random.RandomState(seed)

    eta = np.linspace(eta, 0, max_episodes)
    epsilon = np.linspace(epsilon, 0, max_episodes)

    q = np.zeros((env.n_states, env.n_actions))

    q_iterations = []
    for i in range(max_episodes):
        s = env.reset()
        done = False
        while not done:
            a = eps_greedy_action(q[s], epsilon[i], random_state)
            s_, r, done = env.step(a)
            # Q-Learning formula
            q[s, a] += eta[i] * (r + gamma * q[s_].max() - q[s, a])
            s = s_
        q_iterations += [q.max(axis=1).mean()]
    mp = np.convolve(q_iterations, np.ones(20)/20, mode='valid')
    plt.clf()
    plt.cla()
    plt.plot(np.arange(1, len(mp) + 1), mp)
    plt.savefig('plots/qLearning.png')

    policy = q.argmax(axis=1)
    value = q.max(axis=1)

    return policy, value

class LinearWrapper:
    def __init__(self, env):
        self.env = env

        self.n_actions = self.env.n_actions
        self.n_states = self.env.n_states
        self.n_features = self.n_actions * self.n_states

    def encode_state(self, s):
        features = np.zeros((self.n_actions, self.n_features))
        for a in range(self.n_actions):
            i = np.ravel_multi_index((s, a), (self.n_states, self.n_actions))
            features[a, i] = 1.0

        return features

    def decode_policy(self, theta):
        policy = np.zeros(self.env.n_states, dtype=int)
        value = np.zeros(self.env.n_states)

        for s in range(self.n_states):
            features = self.encode_state(s)
            q = features.dot(theta)

            policy[s] = np.argmax(q)
            value[s] = np.max(q)

        return policy, value

    def reset(self):
        return self.encode_state(self.env.reset())

    def step(self, action):
        state, reward, done = self.env.step(action)

        return self.encode_state(state), reward, done

    def render(self, policy=None, value=None):
        self.env.render(policy, value)

def linear_sarsa(env, max_episodes, eta, gamma, epsilon, seed=None):
    random_state = np.random.RandomState(seed)

    eta = np.linspace(eta, 0, max_episodes)
    epsilon = np.linspace(epsilon, 0, max_episodes)

    theta = np.zeros(env.n_features)

    q_iterations = []
    for i in range(max_episodes):
        features = env.reset()
        q = features.dot(theta)
        a = eps_greedy_action(q, epsilon[i], random_state)
        done = False
        while not done:
            features_, r, done = env.step(a)
            delta = r - q[a]
            q = features_.dot(theta)
            a_ = eps_greedy_action(q, epsilon[i], random_state)
            # Update variables
            delta += gamma * q[a_]
            theta += eta[i] * delta * features[a]
            features, a = features_, a_
        q_iterations += [env.decode_policy(theta)[1].mean()]
    mp = np.convolve(q_iterations, np.ones(20)/20, mode='valid')
    plt.clf()
    plt.cla()
    plt.plot(np.arange(1, len(mp) + 1), mp)
    plt.savefig('plots/linear_sarsa.png')

    return theta

def linear_q_learning(env, max_episodes, eta, gamma, epsilon, seed=None):
    random_state = np.random.RandomState(seed)

    eta = np.linspace(eta, 0, max_episodes)
    epsilon = np.linspace(epsilon, 0, max_episodes)

    theta = np.zeros(env.n_features)

    q_iterations = []
    for i in range(max_episodes):
        features = env.reset()
        q = features.dot(theta)
        done = False
        while not done:
            a = eps_greedy_action(q, epsilon[i], random_state)
            features_, r, done = env.step(a)
            delta = r - q[a]
            # q = np.array([sum(theta * features_[a]) for a in range(env.n_actions)])
            q = features_.dot(theta)
            # Update variables
            delta += gamma * q.max()
            theta += eta[i] * delta * features[a]
            features = features_
        q_iterations += [env.decode_policy(theta)[1].mean()]
    mp = np.convolve(q_iterations, np.ones(20)/20, mode='valid')
    plt.clf()
    plt.cla()
    plt.plot(np.arange(1, len(mp) + 1), mp)
    plt.savefig('plots/linear_qLearning.png')

    return theta

class FrozenLakeImageWrapper:
    def __init__(self, env):
        self.env = env

        lake = self.env.lake

        self.n_actions = self.env.n_actions
        self.state_shape = (4, lake.shape[0], lake.shape[1])

        lake_image = [(lake == c).astype(float) for c in ['&', '#', '$']]

        self.state_image = {env.absorbing_state:
                            np.stack([np.zeros(lake.shape)] + lake_image)}
        for state in range(lake.size):
            x = state // env.width
            y = state % env.width
            channel1 = np.zeros(lake.shape)
            channel1[x, y] = 1.0
            self.state_image[state] = np.stack([channel1] + lake_image)

    def encode_state(self, state):
        return self.state_image[state]

    def decode_policy(self, dqn):
        states = np.array([self.encode_state(s) for s in range(self.env.n_states)])
        q = dqn(states).detach().numpy()  # torch.no_grad omitted to avoid import

        policy = q.argmax(axis=1)
        value = q.max(axis=1)

        return policy, value

    def reset(self):
        return self.encode_state(self.env.reset())

    def step(self, action):
        state, reward, done = self.env.step(action)

        return self.encode_state(state), reward, done

    def render(self, policy=None, value=None):
        self.env.render(policy, value)

class DeepQNetwork(torch.nn.Module):
    def __init__(self, env, learning_rate, kernel_size, conv_out_channels,
                 fc_out_features, seed):
        torch.nn.Module.__init__(self)
        torch.manual_seed(seed)

        self.conv_layer = torch.nn.Conv2d(in_channels=env.state_shape[0],
                                          out_channels=conv_out_channels,
                                          kernel_size=kernel_size, stride=1)

        h = env.state_shape[1] - kernel_size + 1
        w = env.state_shape[2] - kernel_size + 1

        self.fc_layer = torch.nn.Linear(in_features=h * w * conv_out_channels,
                                        out_features=fc_out_features)
        self.output_layer = torch.nn.Linear(in_features=fc_out_features,
                                            out_features=env.n_actions)

        self.optimizer = torch.optim.Adam(self.parameters(), lr=learning_rate)

    def forward(self, x):
        x = torch.tensor(x, dtype=torch.float)
        x = self.conv_layer(x)
        x = x.reshape(x.shape[0],-1)
        x = self.fc_layer(x)
        x = self.output_layer(x)
        return x

    def train_step(self, transitions, gamma, tdqn):
        states = np.array([transition[0] for transition in transitions])
        actions = np.array([transition[1] for transition in transitions])
        rewards = np.array([transition[2] for transition in transitions])
        next_states = np.array([transition[3] for transition in transitions])
        dones = np.array([transition[4] for transition in transitions])

        q = self(states)
        q = q.gather(1, torch.Tensor(actions).view(len(transitions), 1).long())
        q = q.view(len(transitions))

        with torch.no_grad():
            next_q = tdqn(next_states).max(dim=1)[0] * (1 - dones)

        target = torch.Tensor(rewards) + gamma * next_q

        loss = ((q - target) ** 2).mean()

        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

class ReplayBuffer:
    def __init__(self, buffer_size, random_state):
        self.buffer = deque(maxlen=buffer_size)
        self.random_state = random_state

    def __len__(self):
        return len(self.buffer)

    def append(self, transition):
        self.buffer.append(transition)

    def draw(self, batch_size):
        index = self.random_state.choice(range(len(self.buffer)), batch_size)
        return np.array(self.buffer)[index]

def deep_q_network_learning(env, max_episodes, learning_rate, gamma, epsilon,
                            batch_size, target_update_frequency, buffer_size,
                            kernel_size, conv_out_channels, fc_out_features, seed):
    random_state = np.random.RandomState(seed)
    replay_buffer = ReplayBuffer(buffer_size, random_state)

    dqn = DeepQNetwork(env, learning_rate, kernel_size, conv_out_channels,
                       fc_out_features, seed=seed)
    tdqn = DeepQNetwork(env, learning_rate, kernel_size, conv_out_channels,
                        fc_out_features, seed=seed)

    epsilon = np.linspace(epsilon, 0, max_episodes)

    q_iterations = []
    for i in range(max_episodes):
        state = env.reset()

        done = False
        while not done:
            if random_state.rand() < epsilon[i]:
                action = random_state.choice(env.n_actions)
            else:
                with torch.no_grad():
                    q = dqn(np.array([state]))[0].numpy()

                qmax = max(q)
                best = [a for a in range(env.n_actions) if np.allclose(qmax, q[a])]
                action = random_state.choice(best)

            next_state, reward, done = env.step(action)

            replay_buffer.append((state, action, reward, next_state, done))

            state = next_state

            if len(replay_buffer) >= batch_size:
                transitions = replay_buffer.draw(batch_size)
                dqn.train_step(transitions, gamma, tdqn)

        if (i % target_update_frequency) == 0:
            tdqn.load_state_dict(dqn.state_dict())

        q_iterations += [env.decode_policy(dqn)[1].mean()]
    mp = np.convolve(q_iterations, np.ones(20)/20, mode='valid')
    plt.clf()
    plt.cla()
    plt.plot(np.arange(1, len(mp) + 1), mp)
    plt.savefig('plots/DeepQNetworkLearning.png')
    return dqn
#endregion

def main():
    seed = 0

    # Big lake
    # lake = [['&', '.', '.', '.', '.', '.', '.', '.'],
    #         ['.', '.', '.', '.', '.', '.', '.', '.'],
    #         ['.', '.', '.', '#', '.', '.', '.', '.'],
    #         ['.', '.', '.', '.', '.', '#', '.', '.'],
    #         ['.', '.', '.', '#', '.', '.', '.', '.'],
    #         ['.', '#', '#', '.', '.', '.', '#', '.'],
    #         ['.', '#', '.', '.', '#', '.', '#', '.'],
    #         ['.', '.', '.', '#', '.', '.', '.', '$']]

    # Small lake
    lake = [['&', '.', '.', '.'],
            ['.', '#', '.', '#'],
            ['.', '.', '.', '#'],
            ['#', '.', '.', '$']]

    max_steps = len([cell for row in lake for cell in row])

    env = FrozenLake(lake, slip=0.1, max_steps=max_steps, seed=seed)
    gamma = 0.9

    print('# Model-based algorithms')

    print('')

    print('## Policy iteration')
    # policy, value = policy_iteration(env, gamma, theta=0.001, max_iterations=128)
    # env.render(policy, value)

    print('')

    print('## Value iteration')
    # policy, value = value_iteration(env, gamma, theta=0.001, max_iterations=128)
    # env.render(policy, value)

    print('')

    print('# Model-free algorithms')
    max_episodes = 4000

    print('')

    print('## Sarsa')
    policy, value = sarsa(env, max_episodes, eta=0.5, gamma=gamma,
                          epsilon=0.5, seed=seed)
    env.render(policy, value)

    print('')

    print('## Q-learning')
    policy, value = q_learning(env, max_episodes, eta=0.5, gamma=gamma,
                               epsilon=0.5, seed=seed)
    env.render(policy, value)

    print('')

    linear_env = LinearWrapper(env)

    print('## Linear Sarsa')

    parameters = linear_sarsa(linear_env, max_episodes, eta=0.5, gamma=gamma,
                              epsilon=0.5, seed=seed)
    policy, value = linear_env.decode_policy(parameters)
    linear_env.render(policy, value)

    print('')

    print('## Linear Q-learning')

    parameters = linear_q_learning(linear_env, max_episodes, eta=0.5, gamma=gamma,
                                   epsilon=0.5, seed=seed)
    policy, value = linear_env.decode_policy(parameters)
    linear_env.render(policy, value)

    print('')

    image_env = FrozenLakeImageWrapper(env)

    print('## Deep Q-network learning')

    dqn = deep_q_network_learning(image_env, max_episodes, learning_rate=0.001,
                                  gamma=gamma,  epsilon=0.2, batch_size=32,
                                  target_update_frequency=4, buffer_size=256,
                                  kernel_size=3, conv_out_channels=4,
                                  fc_out_features=8, seed=4)
    policy, value = image_env.decode_policy(dqn)
    image_env.render(policy, value)

main()


import requests
from lxml import etree
from bs4 import BeautifulSoup

headers = {
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"
        }

for id in range(0, 1,1):
    url = 'https://movie.douban.com/top250/?start-' + str(id)
    r = requests.get(url, headers=headers)
    print(r.text)
    r.encoding = 'utf-8'
    soup = BeautifulSoup(r.content, 'html.parser')
    print(soup)
    items = soup.find_all('ol', class_='grid_view')
    # items = root.xpath('//*[@id="content"]/div/div[1]/div[1]')
    print(len(items))
    for item in items:
        title = item.xpath('./div[@class="info"]//a/span[@class="title"]/text()')
        name = title[0].encode('gb2312', 'ignore').decode('gb2312')
        # rank = item.xpath('./div[@class="pic"]/em/text()')[0]
        rating = item.xpath('.//div[@class="bd"]//span[@class="rating_num"]/text()')[0]
        print(name, rating)


import webbrowser as web #abrir url de las imagenes y buscador
import openai
import gradio as gr #interface
import logging as log #para llevar registros

import pyttsx3 #convertir texto a voz

import threading #para dividir la ejecucion de programas en hilos
import re, string
import os #

#configura token de openai
#openai.api_key = cf.key

openai.api_key = os.environ['OPENAI_API_KEY']

#configuracion de registros
log.basicConfig(level=log.INFO)

# Función para obtener autocompletado
def bot_completion(prompt):
    response = openai.Completion.create(
        engine="text-davinci-003",
        prompt=prompt,
        max_tokens=100, #cantiad maxima de tokens en la respuesta
        n=1, #cantidad de respuestas
        stop=None, # caracter para marcar el fin de la respuesta
        temperature=0.5 # grado de creatividad 0-2 cuanto mas chico menos random
        )

    return response.choices[0].text

#Funcion para el chat
def chat_completion(prompt, context):
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": context},
            {"role": "user", "content": prompt},
        ]
    )
    texto = response.choices[0].message.content

    return texto

def hablar(texto): #funcion para repoducir en audio el texto
    log.info("Dando respuesta con voz...") #configuracion de voz assitente
    engine = pyttsx3.init() # configuración de voz del asistente
    engine.say(texto) # Se genera la voz a partir de un texto
    engine.runAndWait()   # Se reproduce la voz

def buscar_en_google(consulta):
    url = f"https://www.google.com/search?q={consulta}"
    web.open_new_tab(url)
    return url

def procesar_chat(mensaje):
    resp = chat_completion(mensaje, "Eres un chatbot")
    #resp = chat_completion(mensaje, "Eres un chatbot, mustrate con personalidad divertida, ironica e irritable")
    #resp = chat_completion(mensaje, "Eres un chatbot, muestrate proactivo y participativo")
    return resp

def procesar_traduccion(prompt):
    split = prompt.split(maxsplit=2)
    #(Traduce)
    # al + idioma + texto a traducir
    idioma = split[1]
    texto = split[2]
    log.info("Idioma: " + idioma)
    log.info("Frase: " + texto)
    traduccion = chat_completion(texto, "Eres traductor, asi que traduce esto al " + idioma)
    return traduccion

def procesar_dibujo(prompt):
    response = openai.Image.create(
        prompt = prompt,
        n = 1, # cantidad de imagenes puede ir de 1 a 10
        size = "1024x1024" #tamaño
        )
    url = response["data"][0]["url"]
    web.open(url) # abre imagen en una pestaña
    return  url

def procesar_entrada(audio,voz):
    #print(audio)
    log.info("Procesando audio ...")
    audio_file = open(audio, "rb")
    transcript = openai.Audio.transcribe("whisper-1",audio_file)

    words = transcript["text"].split(maxsplit=1)
    # instruccion + el resto del texto
    instruc = re.sub('[%s]' % re.escape(string.punctuation), '', words[0])
    prompt = words[1]
    log.info("Transcripción: " + transcript["text"])
    log.info("Instrucción: " + instruc)
    log.info("Prompt: " + prompt)

    # diccionario que mapea instrucciones a funciones
    acciones = {
        "Dime": [procesar_chat, True], # orden : [funcion a llamar, muestra texto si/no]
        "Traduce": [procesar_traduccion,True],
        "Dibuja": [procesar_dibujo, False],
        "Busca": [buscar_en_google, False]
    }

    if instruc.capitalize() in acciones:
        funcion = acciones[instruc.capitalize()][0]
        respuesta = funcion(prompt)
        if voz:
            if not acciones[instruc.capitalize()][1]:
                respuesta = "Abriendo URL"
            # Creamos un hilo para ejecutar la función hablar()
            t = threading.Thread(target=hablar, args=(respuesta,))
            t.start()
        return respuesta
    else:
        resp = bot_completion(transcript["text"])
        if voz:
            # Creamos un hilo para ejecutar la función hablar()
            t = threading.Thread(target=hablar, args=(resp,))
            t.start()
        return resp

ui = gr.Interface(fn=procesar_entrada,
                  inputs=[gr.Audio(source="microphone",type="filepath"),
                          gr.Checkbox(label="activar voz")],
                  outputs="text")
ui.launch()