import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pandas_datareader as pdr
import yfinance as yf
from pykalman import KalmanFilter

def plot_spread(prices, errors, stds, neg_stds):
    # Plot the spread
    plt.figure(figsize=(20, 6))
    plt.plot(prices.index[1:], errors, color='blue', label='KF Spread')
    plt.plot(prices.index[1:], stds, linestyle='--', color='red', label='Std Dev')
    plt.plot(prices.index[1:], neg_stds, linestyle='--', color='red', label='-Std Dev')
    plt.grid(True, linestyle='--', alpha=0.5)
    plt.xlabel("Days")
    plt.ylabel("KF Spread")
    plt.title("Kalman Filter Spread")
    plt.legend()
    plt.show()

def plot_spread_rollingOLS(prices, errors, stds, neg_stds, window):
    # Plot the spread
    plt.figure(figsize=(20, 6))
    plt.plot(prices.index[2*window-2:], errors, color='blue', label='z-Score')
    plt.plot(prices.index[2*window-1:], stds, linestyle='--', color='red', label='+1')
    plt.plot(prices.index[2*window-1:], neg_stds, linestyle='--', color='red', label='-1')
    plt.grid(True, linestyle='--', alpha=0.5)
    plt.xlabel("Days")
    plt.ylabel("z-Score")
    plt.title("z-Score of Spread")
    plt.legend()
    plt.show()

def plot_nav(prices, nav, window):
    # Plot Unit NAV
    plt.figure(figsize=(20, 10))
    plt.plot(prices.index[window-1:], nav, color='blue', label='Cumulative Unit NAV')
    plt.grid(True, linestyle='--', alpha=0.5)
    plt.xlabel('Days')
    plt.ylabel('Unit NAV')
    plt.title('Cumulative Unit NAV')
    plt.legend()
    plt.show()

def plot_nav_sp500(prices, nav, sp500_nav, USTreasury_nav):
    # Get S&P 500 data from Yahoo Finance
    # index = prices.index.strftime('%Y-%m-%d')
    # sp500 = yf.download('^GSPC', start=index[0], end=index[-1])['Adj Close']
    # sp500 = pdr.get_data_yahoo('^GSPC', start=prices.index[0], end=prices.index[-1])['Adj Close']

    # Plot Unit NAV and S&P 500
    fig, ax = plt.subplots(figsize=(20, 10))
    ax.plot(prices.index, nav, color='red', label='Portfolio')
    ax.plot(prices.index[1:], sp500_nav, color='blue', label='S&P 500')
    ax.plot(prices.index[1:], USTreasury_nav, color='lightblue', label='10 Year US Treasury')
    ax.grid(True, linestyle='--', alpha=0.5)
    ax.set_xlabel('Days')
    ax.set_ylabel('Cumulative Unit NAV')
    ax.set_title('Cumulative Unit NAV')
    ax.legend()
    plt.show()

def plot_nav_sp500_rollingOLS(prices, nav, sp500_nav, USTreasury_nav, window):
    # Get S&P 500 data from Yahoo Finance
    # index = prices.index.strftime('%Y-%m-%d')
    # sp500 = yf.download('^GSPC', start=index[0], end=index[-1])['Adj Close']
    # sp500 = pdr.get_data_yahoo('^GSPC', start=prices.index[0], end=prices.index[-1])['Adj Close']

    # Plot Unit NAV and S&P 500
    fig, ax = plt.subplots(figsize=(20, 10))
    ax.plot(prices.index[2*window-2:], nav, color='red', label='Portfolio')
    ax.plot(prices.index[2*window-1:], sp500_nav, color='blue', label='S&P 500')
    ax.plot(prices.index[2*window-1:], USTreasury_nav, color='lightblue', label='10 Year US Treasury')
    ax.grid(True, linestyle='--', alpha=0.5)
    ax.set_xlabel('Days')
    ax.set_ylabel('Cumulative Unit NAV')
    ax.set_title('Cumulative Unit NAV')
    ax.legend()
    plt.show()

def plot_cum_returns(prices, cum_returns):
    # Plot cumulative returns
    plt.figure(figsize=(20, 10))
    plt.plot(prices.index[1:], cum_returns, color='blue', label='Cumulative Returns')
    plt.grid(True, linestyle='--', alpha=0.5)
    plt.xlabel('Days')
    plt.ylabel('Cumulative returns')
    plt.title('Cumulative Returns')
    plt.legend()
    plt.show()

def plot_drawdown(prices, drawdowns):
    # Plot the drawdowns
    fig, ax = plt.subplots(figsize=(20, 10))
    ax.plot(prices.index, drawdowns, color='blue')
    # ax.plot(prices.index[1:], drawdowns, color='blue')
    ax.fill_between(prices.index, drawdowns, 0, color='red', alpha=0.3)
    # ax.fill_between(prices.index[1:], drawdowns, 0, color='red', alpha=0.3)
    ax.set_xlabel('Time')
    ax.set_ylabel('Drawdown')
    ax.set_title('Drawdown')

    plt.show()

def plot_drawdown_sp500(prices, drawdowns, sp500_drawdowns, USTreasury_drawdowns):
    # Get S&P 500 data from Yahoo Finance
    # index = prices.index.strftime('%Y-%m-%d')
    # sp500 = yf.download('^GSPC', start=index[0], end=index[-1])['Adj Close']
    # sp500_nav = sp500 / sp500.iloc[0]
    # sp500_highwatermarks = np.maximum.accumulate(sp500_nav)
    # sp500_drawdowns = (sp500_nav - sp500_highwatermarks) / sp500_highwatermarks
    # Plot the drawdowns
    fig, ax = plt.subplots(figsize=(20, 10))
    ax.plot(prices.index, drawdowns, color='red', label='Portfolio Drawdown')
    # ax.plot(prices.index[1:], drawdowns, color='blue')
    ax.fill_between(prices.index, drawdowns, 0, color='red', alpha=0.3)
    # ax.fill_between(prices.index[1:], drawdowns, 0, color='red', alpha=0.3)

    ax.plot(prices.index[1:], sp500_drawdowns, color='blue', label='S&P 500 Drawdown')
    ax.fill_between(prices.index[1:], sp500_drawdowns, 0, color='blue', alpha=0.3)

    ax.plot(prices.index[1:], USTreasury_drawdowns, color='orange', label='10 Year US Treasury Drawdown')
    ax.fill_between(prices.index[1:], USTreasury_drawdowns, 0, color='orange', alpha=0.3)
    ax.grid(True, linestyle='--', alpha=0.5)
    ax.set_xlabel('Time')
    ax.set_ylabel('Drawdown')
    ax.set_title('Drawdown')
    ax.legend()
    plt.show()

def plot_drawdown_sp500_rollingOLS(prices, drawdowns, sp500_drawdowns, USTreasury_drawdowns, window):
    # Get S&P 500 data from Yahoo Finance
    # index = prices.index.strftime('%Y-%m-%d')
    # sp500 = yf.download('^GSPC', start=index[0], end=index[-1])['Adj Close']
    # sp500_nav = sp500 / sp500.iloc[0]
    # sp500_highwatermarks = np.maximum.accumulate(sp500_nav)
    # sp500_drawdowns = (sp500_nav - sp500_highwatermarks) / sp500_highwatermarks
    # Plot the drawdowns
    fig, ax = plt.subplots(figsize=(20, 10))
    ax.plot(prices.index[2*window-2:], drawdowns, color='red', label='Portfolio Drawdown')
    # ax.plot(prices.index[1:], drawdowns, color='blue')
    ax.fill_between(prices.index[2*window-2:], drawdowns, 0, color='red', alpha=0.3)
    # ax.fill_between(prices.index[1:], drawdowns, 0, color='red', alpha=0.3)

    ax.plot(prices.index[2*window-1:], sp500_drawdowns, color='blue', label='S&P 500 Drawdown')
    ax.fill_between(prices.index[2*window-1:], sp500_drawdowns, 0, color='blue', alpha=0.3)

    ax.plot(prices.index[2*window-1:], USTreasury_drawdowns, color='orange', label='10 Year US Treasury Drawdown')
    ax.fill_between(prices.index[2*window-1:], USTreasury_drawdowns, 0, color='orange', alpha=0.3)
    ax.grid(True, linestyle='--', alpha=0.5)
    ax.set_xlabel('Time')
    ax.set_ylabel('Drawdown')
    ax.set_title('Drawdown')
    ax.legend()
    plt.show()

def calculate_daily_returns(portfolio_nav):
    daily_returns = []

    for i in range(1, len(portfolio_nav)):
        daily_return = (portfolio_nav[i] - portfolio_nav[i - 1]) / portfolio_nav[i - 1]
        daily_returns.append(daily_return)

    return daily_returns

def calculate_drawdown(portfolio_nav):
    max_so_far = 0
    drawdowns = []

    for nav in portfolio_nav:
        max_so_far = max(max_so_far, nav)
        drawdown = -(max_so_far - nav) / max_so_far
        drawdowns.append(drawdown)

    return drawdowns

def max_drawdown(daily_returns):
    # Convert daily returns to a Pandas Series object
    daily_returns = pd.Series(daily_returns)

    # Compute the cumulative returns at each time point
    cumulative_unit_nav = (1 + daily_returns).cumprod()
    # cumulative_returns = np.cumprod(1 + daily_returns) - 1

    # Compute the highwatermarks at each time point
    highwatermarks = np.maximum.accumulate(cumulative_unit_nav)

    # Compute the drawdowns at each time point
    drawdowns = (cumulative_unit_nav - highwatermarks) / highwatermarks

    # Compute the maximum drawdown and the start and end dates of the maximum drawdown period
    max_dd = np.min(drawdowns)
    # max_dd_start_date = np.argmax(np.maximum.accumulate(cumulative_unit_nav) - cumulative_unit_nav[:np.argmax(np.maximum.accumulate(cumulative_unit_nav))])
    # max_dd_end_date = np.argmax(cumulative_unit_nav[:np.argmax(np.maximum.accumulate(cumulative_unit_nav))] + max_dd)

    return max_dd

def pnl_metrics(daily_returns, sp500_nav, sp500_drawdowns, USTreasury_nav, USTreasury_drawdowns):
    # Calculate annualized return
    annualized_return = np.mean(daily_returns) * 252

    # Calculate annualized volatility
    annualized_volatility = np.std(daily_returns) * np.sqrt(252)

    # Calculate Sharpe Ratio
    sharpe_ratio = (annualized_return - 0.02) / annualized_volatility

    # Calculate Max Drawdown
    max_dd = max_drawdown(daily_returns)
    # max_drawdown = max_drawdown(cum_returns)

    # Calculate S&P 500 annualized return, volatility and max drawdown
    sp500_annualized_return = np.mean(sp500_nav.pct_change().dropna()) * 252
    sp500_annualized_volatility = np.std(sp500_nav.pct_change().dropna()) * np.sqrt(252)
    sp500_sharpe_ratio = (sp500_annualized_return - 0.02) / sp500_annualized_volatility
    sp500_max_dd = np.min(sp500_drawdowns)

    # Calculate US Treasury annualized return, volatility and max drawdown
    USTreasury_annualized_return = np.mean(USTreasury_nav.pct_change().dropna()) * 252
    USTreasury_annualized_volatility = np.std(USTreasury_nav.pct_change().dropna()) * np.sqrt(252)
    USTreasury_max_dd = np.min(USTreasury_drawdowns)

    results = pd.DataFrame({'KF Pairs Trading': [annualized_return, annualized_volatility, sharpe_ratio, max_dd],
                           'S&P 500': [sp500_annualized_return, sp500_annualized_volatility, sp500_sharpe_ratio, sp500_max_dd],
                           '10 Year US Treasury': [USTreasury_annualized_return, USTreasury_annualized_volatility, np.nan, USTreasury_max_dd]},
                           index=['Annualized Return', 'Annualized Volatility', 'Sharpe Ratio', 'Max Drawdown'])
    # results = results.to_string(header=True,
    #                             col_space=10,
    #                             justify='left',
    #                             float_format="%.3f")
    return results


# coding: utf-8

"""
    Talon.One API

    The Talon.One API is used to manage applications and campaigns, as well as to integrate with your application. The operations in the _Integration API_ section are used to integrate with our platform, while the other operations are used to manage applications and campaigns.  ### Where is the API?  The API is available at the same hostname as these docs. For example, if you are reading this page at `https://mycompany.talon.one/docs/api/`, the URL for the [updateCustomerProfile][] operation is `https://mycompany.talon.one/v1/customer_profiles/id`  [updateCustomerProfile]: #operation--v1-customer_profiles--integrationId--put   # noqa: E501

    The version of the OpenAPI document: 1.0.0
    Generated by: https://openapi-generator.tech
"""

import pprint
import re  # noqa: F401

import six

from talon_one.configuration import Configuration

class Campaign(object):
    """NOTE: This class is auto generated by OpenAPI Generator.
    Ref: https://openapi-generator.tech

    Do not edit the class manually.
    """

    """
    Attributes:
      openapi_types (dict): The key is attribute name
                            and the value is attribute type.
      attribute_map (dict): The key is attribute name
                            and the value is json key in definition.
    """
    openapi_types = {
        'id': 'int',
        'created': 'datetime',
        'application_id': 'int',
        'user_id': 'int',
        'name': 'str',
        'description': 'str',
        'start_time': 'datetime',
        'end_time': 'datetime',
        'attributes': 'object',
        'state': 'str',
        'active_ruleset_id': 'int',
        'tags': 'list[str]',
        'features': 'list[str]',
        'coupon_settings': 'CodeGeneratorSettings',
        'referral_settings': 'CodeGeneratorSettings',
        'limits': 'list[LimitConfig]',
        'campaign_groups': 'list[int]',
        'coupon_redemption_count': 'int',
        'referral_redemption_count': 'int',
        'discount_count': 'float',
        'discount_effect_count': 'int',
        'coupon_creation_count': 'int',
        'referral_creation_count': 'int',
        'created_loyalty_points_count': 'float',
        'created_loyalty_points_effect_count': 'int',
        'redeemed_loyalty_points_count': 'float',
        'redeemed_loyalty_points_effect_count': 'int',
        'last_activity': 'datetime',
        'updated': 'datetime',
        'created_by': 'str',
        'updated_by': 'str'
    }

    attribute_map = {
        'id': 'id',
        'created': 'created',
        'application_id': 'applicationId',
        'user_id': 'userId',
        'name': 'name',
        'description': 'description',
        'start_time': 'startTime',
        'end_time': 'endTime',
        'attributes': 'attributes',
        'state': 'state',
        'active_ruleset_id': 'activeRulesetId',
        'tags': 'tags',
        'features': 'features',
        'coupon_settings': 'couponSettings',
        'referral_settings': 'referralSettings',
        'limits': 'limits',
        'campaign_groups': 'campaignGroups',
        'coupon_redemption_count': 'couponRedemptionCount',
        'referral_redemption_count': 'referralRedemptionCount',
        'discount_count': 'discountCount',
        'discount_effect_count': 'discountEffectCount',
        'coupon_creation_count': 'couponCreationCount',
        'referral_creation_count': 'referralCreationCount',
        'created_loyalty_points_count': 'createdLoyaltyPointsCount',
        'created_loyalty_points_effect_count': 'createdLoyaltyPointsEffectCount',
        'redeemed_loyalty_points_count': 'redeemedLoyaltyPointsCount',
        'redeemed_loyalty_points_effect_count': 'redeemedLoyaltyPointsEffectCount',
        'last_activity': 'lastActivity',
        'updated': 'updated',
        'created_by': 'createdBy',
        'updated_by': 'updatedBy'
    }

    def __init__(self, id=None, created=None, application_id=None, user_id=None, name=None, description=None, start_time=None, end_time=None, attributes=None, state='enabled', active_ruleset_id=None, tags=None, features=None, coupon_settings=None, referral_settings=None, limits=None, campaign_groups=None, coupon_redemption_count=None, referral_redemption_count=None, discount_count=None, discount_effect_count=None, coupon_creation_count=None, referral_creation_count=None, created_loyalty_points_count=None, created_loyalty_points_effect_count=None, redeemed_loyalty_points_count=None, redeemed_loyalty_points_effect_count=None, last_activity=None, updated=None, created_by=None, updated_by=None, local_vars_configuration=None):  # noqa: E501
        """Campaign - a model defined in OpenAPI"""  # noqa: E501
        if local_vars_configuration is None:
            local_vars_configuration = Configuration()
        self.local_vars_configuration = local_vars_configuration

        self._id = None
        self._created = None
        self._application_id = None
        self._user_id = None
        self._name = None
        self._description = None
        self._start_time = None
        self._end_time = None
        self._attributes = None
        self._state = None
        self._active_ruleset_id = None
        self._tags = None
        self._features = None
        self._coupon_settings = None
        self._referral_settings = None
        self._limits = None
        self._campaign_groups = None
        self._coupon_redemption_count = None
        self._referral_redemption_count = None
        self._discount_count = None
        self._discount_effect_count = None
        self._coupon_creation_count = None
        self._referral_creation_count = None
        self._created_loyalty_points_count = None
        self._created_loyalty_points_effect_count = None
        self._redeemed_loyalty_points_count = None
        self._redeemed_loyalty_points_effect_count = None
        self._last_activity = None
        self._updated = None
        self._created_by = None
        self._updated_by = None
        self.discriminator = None

        self.id = id
        self.created = created
        self.application_id = application_id
        self.user_id = user_id
        self.name = name
        self.description = description
        if start_time is not None:
            self.start_time = start_time
        if end_time is not None:
            self.end_time = end_time
        if attributes is not None:
            self.attributes = attributes
        self.state = state
        if active_ruleset_id is not None:
            self.active_ruleset_id = active_ruleset_id
        self.tags = tags
        self.features = features
        if coupon_settings is not None:
            self.coupon_settings = coupon_settings
        if referral_settings is not None:
            self.referral_settings = referral_settings
        self.limits = limits
        if campaign_groups is not None:
            self.campaign_groups = campaign_groups
        if coupon_redemption_count is not None:
            self.coupon_redemption_count = coupon_redemption_count
        if referral_redemption_count is not None:
            self.referral_redemption_count = referral_redemption_count
        if discount_count is not None:
            self.discount_count = discount_count
        if discount_effect_count is not None:
            self.discount_effect_count = discount_effect_count
        if coupon_creation_count is not None:
            self.coupon_creation_count = coupon_creation_count
        if referral_creation_count is not None:
            self.referral_creation_count = referral_creation_count
        if created_loyalty_points_count is not None:
            self.created_loyalty_points_count = created_loyalty_points_count
        if created_loyalty_points_effect_count is not None:
            self.created_loyalty_points_effect_count = created_loyalty_points_effect_count
        if redeemed_loyalty_points_count is not None:
            self.redeemed_loyalty_points_count = redeemed_loyalty_points_count
        if redeemed_loyalty_points_effect_count is not None:
            self.redeemed_loyalty_points_effect_count = redeemed_loyalty_points_effect_count
        if last_activity is not None:
            self.last_activity = last_activity
        if updated is not None:
            self.updated = updated
        if created_by is not None:
            self.created_by = created_by
        if updated_by is not None:
            self.updated_by = updated_by

    @property
    def id(self):
        """Gets the id of this Campaign.  # noqa: E501

        Unique ID for this entity.  # noqa: E501

        :return: The id of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._id

    @id.setter
    def id(self, id):
        """Sets the id of this Campaign.

        Unique ID for this entity.  # noqa: E501

        :param id: The id of this Campaign.  # noqa: E501
        :type: int
        """
        if self.local_vars_configuration.client_side_validation and id is None:  # noqa: E501
            raise ValueError("Invalid value for `id`, must not be `None`")  # noqa: E501

        self._id = id

    @property
    def created(self):
        """Gets the created of this Campaign.  # noqa: E501

        The exact moment this entity was created.  # noqa: E501

        :return: The created of this Campaign.  # noqa: E501
        :rtype: datetime
        """
        return self._created

    @created.setter
    def created(self, created):
        """Sets the created of this Campaign.

        The exact moment this entity was created.  # noqa: E501

        :param created: The created of this Campaign.  # noqa: E501
        :type: datetime
        """
        if self.local_vars_configuration.client_side_validation and created is None:  # noqa: E501
            raise ValueError("Invalid value for `created`, must not be `None`")  # noqa: E501

        self._created = created

    @property
    def application_id(self):
        """Gets the application_id of this Campaign.  # noqa: E501

        The ID of the application that owns this entity.  # noqa: E501

        :return: The application_id of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._application_id

    @application_id.setter
    def application_id(self, application_id):
        """Sets the application_id of this Campaign.

        The ID of the application that owns this entity.  # noqa: E501

        :param application_id: The application_id of this Campaign.  # noqa: E501
        :type: int
        """
        if self.local_vars_configuration.client_side_validation and application_id is None:  # noqa: E501
            raise ValueError("Invalid value for `application_id`, must not be `None`")  # noqa: E501

        self._application_id = application_id

    @property
    def user_id(self):
        """Gets the user_id of this Campaign.  # noqa: E501

        The ID of the account that owns this entity.  # noqa: E501

        :return: The user_id of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._user_id

    @user_id.setter
    def user_id(self, user_id):
        """Sets the user_id of this Campaign.

        The ID of the account that owns this entity.  # noqa: E501

        :param user_id: The user_id of this Campaign.  # noqa: E501
        :type: int
        """
        if self.local_vars_configuration.client_side_validation and user_id is None:  # noqa: E501
            raise ValueError("Invalid value for `user_id`, must not be `None`")  # noqa: E501

        self._user_id = user_id

    @property
    def name(self):
        """Gets the name of this Campaign.  # noqa: E501

        A friendly name for this campaign.  # noqa: E501

        :return: The name of this Campaign.  # noqa: E501
        :rtype: str
        """
        return self._name

    @name.setter
    def name(self, name):
        """Sets the name of this Campaign.

        A friendly name for this campaign.  # noqa: E501

        :param name: The name of this Campaign.  # noqa: E501
        :type: str
        """
        if self.local_vars_configuration.client_side_validation and name is None:  # noqa: E501
            raise ValueError("Invalid value for `name`, must not be `None`")  # noqa: E501
        if (self.local_vars_configuration.client_side_validation and
                name is not None and len(name) < 1):
            raise ValueError("Invalid value for `name`, length must be greater than or equal to `1`")  # noqa: E501

        self._name = name

    @property
    def description(self):
        """Gets the description of this Campaign.  # noqa: E501

        A detailed description of the campaign.  # noqa: E501

        :return: The description of this Campaign.  # noqa: E501
        :rtype: str
        """
        return self._description

    @description.setter
    def description(self, description):
        """Sets the description of this Campaign.

        A detailed description of the campaign.  # noqa: E501

        :param description: The description of this Campaign.  # noqa: E501
        :type: str
        """
        if self.local_vars_configuration.client_side_validation and description is None:  # noqa: E501
            raise ValueError("Invalid value for `description`, must not be `None`")  # noqa: E501

        self._description = description

    @property
    def start_time(self):
        """Gets the start_time of this Campaign.  # noqa: E501

        Datetime when the campaign will become active.  # noqa: E501

        :return: The start_time of this Campaign.  # noqa: E501
        :rtype: datetime
        """
        return self._start_time

    @start_time.setter
    def start_time(self, start_time):
        """Sets the start_time of this Campaign.

        Datetime when the campaign will become active.  # noqa: E501

        :param start_time: The start_time of this Campaign.  # noqa: E501
        :type: datetime
        """

        self._start_time = start_time

    @property
    def end_time(self):
        """Gets the end_time of this Campaign.  # noqa: E501

        Datetime when the campaign will become in-active.  # noqa: E501

        :return: The end_time of this Campaign.  # noqa: E501
        :rtype: datetime
        """
        return self._end_time

    @end_time.setter
    def end_time(self, end_time):
        """Sets the end_time of this Campaign.

        Datetime when the campaign will become in-active.  # noqa: E501

        :param end_time: The end_time of this Campaign.  # noqa: E501
        :type: datetime
        """

        self._end_time = end_time

    @property
    def attributes(self):
        """Gets the attributes of this Campaign.  # noqa: E501

        Arbitrary properties associated with this campaign  # noqa: E501

        :return: The attributes of this Campaign.  # noqa: E501
        :rtype: object
        """
        return self._attributes

    @attributes.setter
    def attributes(self, attributes):
        """Sets the attributes of this Campaign.

        Arbitrary properties associated with this campaign  # noqa: E501

        :param attributes: The attributes of this Campaign.  # noqa: E501
        :type: object
        """

        self._attributes = attributes

    @property
    def state(self):
        """Gets the state of this Campaign.  # noqa: E501

        A disabled or archived campaign is not evaluated for rules or coupons.   # noqa: E501

        :return: The state of this Campaign.  # noqa: E501
        :rtype: str
        """
        return self._state

    @state.setter
    def state(self, state):
        """Sets the state of this Campaign.

        A disabled or archived campaign is not evaluated for rules or coupons.   # noqa: E501

        :param state: The state of this Campaign.  # noqa: E501
        :type: str
        """
        if self.local_vars_configuration.client_side_validation and state is None:  # noqa: E501
            raise ValueError("Invalid value for `state`, must not be `None`")  # noqa: E501
        allowed_values = ["enabled", "disabled", "archived"]  # noqa: E501
        if self.local_vars_configuration.client_side_validation and state not in allowed_values:  # noqa: E501
            raise ValueError(
                "Invalid value for `state` ({0}), must be one of {1}"  # noqa: E501
                .format(state, allowed_values)
            )

        self._state = state

    @property
    def active_ruleset_id(self):
        """Gets the active_ruleset_id of this Campaign.  # noqa: E501

        ID of Ruleset this campaign applies on customer session evaluation.  # noqa: E501

        :return: The active_ruleset_id of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._active_ruleset_id

    @active_ruleset_id.setter
    def active_ruleset_id(self, active_ruleset_id):
        """Sets the active_ruleset_id of this Campaign.

        ID of Ruleset this campaign applies on customer session evaluation.  # noqa: E501

        :param active_ruleset_id: The active_ruleset_id of this Campaign.  # noqa: E501
        :type: int
        """

        self._active_ruleset_id = active_ruleset_id

    @property
    def tags(self):
        """Gets the tags of this Campaign.  # noqa: E501

        A list of tags for the campaign.  # noqa: E501

        :return: The tags of this Campaign.  # noqa: E501
        :rtype: list[str]
        """
        return self._tags

    @tags.setter
    def tags(self, tags):
        """Sets the tags of this Campaign.

        A list of tags for the campaign.  # noqa: E501

        :param tags: The tags of this Campaign.  # noqa: E501
        :type: list[str]
        """
        if self.local_vars_configuration.client_side_validation and tags is None:  # noqa: E501
            raise ValueError("Invalid value for `tags`, must not be `None`")  # noqa: E501

        self._tags = tags

    @property
    def features(self):
        """Gets the features of this Campaign.  # noqa: E501

        A list of features for the campaign.  # noqa: E501

        :return: The features of this Campaign.  # noqa: E501
        :rtype: list[str]
        """
        return self._features

    @features.setter
    def features(self, features):
        """Sets the features of this Campaign.

        A list of features for the campaign.  # noqa: E501

        :param features: The features of this Campaign.  # noqa: E501
        :type: list[str]
        """
        if self.local_vars_configuration.client_side_validation and features is None:  # noqa: E501
            raise ValueError("Invalid value for `features`, must not be `None`")  # noqa: E501
        allowed_values = ["coupons", "referrals", "loyalty"]  # noqa: E501
        if (self.local_vars_configuration.client_side_validation and
                not set(features).issubset(set(allowed_values))):  # noqa: E501
            raise ValueError(
                "Invalid values for `features` [{0}], must be a subset of [{1}]"  # noqa: E501
                .format(", ".join(map(str, set(features) - set(allowed_values))),  # noqa: E501
                        ", ".join(map(str, allowed_values)))
            )

        self._features = features

    @property
    def coupon_settings(self):
        """Gets the coupon_settings of this Campaign.  # noqa: E501

        :return: The coupon_settings of this Campaign.  # noqa: E501
        :rtype: CodeGeneratorSettings
        """
        return self._coupon_settings

    @coupon_settings.setter
    def coupon_settings(self, coupon_settings):
        """Sets the coupon_settings of this Campaign.

        :param coupon_settings: The coupon_settings of this Campaign.  # noqa: E501
        :type: CodeGeneratorSettings
        """

        self._coupon_settings = coupon_settings

    @property
    def referral_settings(self):
        """Gets the referral_settings of this Campaign.  # noqa: E501

        :return: The referral_settings of this Campaign.  # noqa: E501
        :rtype: CodeGeneratorSettings
        """
        return self._referral_settings

    @referral_settings.setter
    def referral_settings(self, referral_settings):
        """Sets the referral_settings of this Campaign.

        :param referral_settings: The referral_settings of this Campaign.  # noqa: E501
        :type: CodeGeneratorSettings
        """

        self._referral_settings = referral_settings

    @property
    def limits(self):
        """Gets the limits of this Campaign.  # noqa: E501

        The set of limits that will operate for this campaign  # noqa: E501

        :return: The limits of this Campaign.  # noqa: E501
        :rtype: list[LimitConfig]
        """
        return self._limits

    @limits.setter
    def limits(self, limits):
        """Sets the limits of this Campaign.

        The set of limits that will operate for this campaign  # noqa: E501

        :param limits: The limits of this Campaign.  # noqa: E501
        :type: list[LimitConfig]
        """
        if self.local_vars_configuration.client_side_validation and limits is None:  # noqa: E501
            raise ValueError("Invalid value for `limits`, must not be `None`")  # noqa: E501

        self._limits = limits

    @property
    def campaign_groups(self):
        """Gets the campaign_groups of this Campaign.  # noqa: E501

        The IDs of the campaign groups that own this entity.  # noqa: E501

        :return: The campaign_groups of this Campaign.  # noqa: E501
        :rtype: list[int]
        """
        return self._campaign_groups

    @campaign_groups.setter
    def campaign_groups(self, campaign_groups):
        """Sets the campaign_groups of this Campaign.

        The IDs of the campaign groups that own this entity.  # noqa: E501

        :param campaign_groups: The campaign_groups of this Campaign.  # noqa: E501
        :type: list[int]
        """

        self._campaign_groups = campaign_groups

    @property
    def coupon_redemption_count(self):
        """Gets the coupon_redemption_count of this Campaign.  # noqa: E501

        Number of coupons redeemed in the campaign.  # noqa: E501

        :return: The coupon_redemption_count of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._coupon_redemption_count

    @coupon_redemption_count.setter
    def coupon_redemption_count(self, coupon_redemption_count):
        """Sets the coupon_redemption_count of this Campaign.

        Number of coupons redeemed in the campaign.  # noqa: E501

        :param coupon_redemption_count: The coupon_redemption_count of this Campaign.  # noqa: E501
        :type: int
        """

        self._coupon_redemption_count = coupon_redemption_count

    @property
    def referral_redemption_count(self):
        """Gets the referral_redemption_count of this Campaign.  # noqa: E501

        Number of referral codes redeemed in the campaign.  # noqa: E501

        :return: The referral_redemption_count of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._referral_redemption_count

    @referral_redemption_count.setter
    def referral_redemption_count(self, referral_redemption_count):
        """Sets the referral_redemption_count of this Campaign.

        Number of referral codes redeemed in the campaign.  # noqa: E501

        :param referral_redemption_count: The referral_redemption_count of this Campaign.  # noqa: E501
        :type: int
        """

        self._referral_redemption_count = referral_redemption_count

    @property
    def discount_count(self):
        """Gets the discount_count of this Campaign.  # noqa: E501

        Total amount of discounts redeemed in the campaign.  # noqa: E501

        :return: The discount_count of this Campaign.  # noqa: E501
        :rtype: float
        """
        return self._discount_count

    @discount_count.setter
    def discount_count(self, discount_count):
        """Sets the discount_count of this Campaign.

        Total amount of discounts redeemed in the campaign.  # noqa: E501

        :param discount_count: The discount_count of this Campaign.  # noqa: E501
        :type: float
        """

        self._discount_count = discount_count

    @property
    def discount_effect_count(self):
        """Gets the discount_effect_count of this Campaign.  # noqa: E501

        Total number of times discounts were redeemed in this campaign.  # noqa: E501

        :return: The discount_effect_count of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._discount_effect_count

    @discount_effect_count.setter
    def discount_effect_count(self, discount_effect_count):
        """Sets the discount_effect_count of this Campaign.

        Total number of times discounts were redeemed in this campaign.  # noqa: E501

        :param discount_effect_count: The discount_effect_count of this Campaign.  # noqa: E501
        :type: int
        """

        self._discount_effect_count = discount_effect_count

    @property
    def coupon_creation_count(self):
        """Gets the coupon_creation_count of this Campaign.  # noqa: E501

        Total number of coupons created by rules in this campaign.  # noqa: E501

        :return: The coupon_creation_count of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._coupon_creation_count

    @coupon_creation_count.setter
    def coupon_creation_count(self, coupon_creation_count):
        """Sets the coupon_creation_count of this Campaign.

        Total number of coupons created by rules in this campaign.  # noqa: E501

        :param coupon_creation_count: The coupon_creation_count of this Campaign.  # noqa: E501
        :type: int
        """

        self._coupon_creation_count = coupon_creation_count

    @property
    def referral_creation_count(self):
        """Gets the referral_creation_count of this Campaign.  # noqa: E501

        Total number of referrals created by rules in this campaign.  # noqa: E501

        :return: The referral_creation_count of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._referral_creation_count

    @referral_creation_count.setter
    def referral_creation_count(self, referral_creation_count):
        """Sets the referral_creation_count of this Campaign.

        Total number of referrals created by rules in this campaign.  # noqa: E501

        :param referral_creation_count: The referral_creation_count of this Campaign.  # noqa: E501
        :type: int
        """

        self._referral_creation_count = referral_creation_count

    @property
    def created_loyalty_points_count(self):
        """Gets the created_loyalty_points_count of this Campaign.  # noqa: E501

        Total number of loyalty points created by rules in this campaign.  # noqa: E501

        :return: The created_loyalty_points_count of this Campaign.  # noqa: E501
        :rtype: float
        """
        return self._created_loyalty_points_count

    @created_loyalty_points_count.setter
    def created_loyalty_points_count(self, created_loyalty_points_count):
        """Sets the created_loyalty_points_count of this Campaign.

        Total number of loyalty points created by rules in this campaign.  # noqa: E501

        :param created_loyalty_points_count: The created_loyalty_points_count of this Campaign.  # noqa: E501
        :type: float
        """

        self._created_loyalty_points_count = created_loyalty_points_count

    @property
    def created_loyalty_points_effect_count(self):
        """Gets the created_loyalty_points_effect_count of this Campaign.  # noqa: E501

        Total number of loyalty point creation effects triggered by rules in this campaign.  # noqa: E501

        :return: The created_loyalty_points_effect_count of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._created_loyalty_points_effect_count

    @created_loyalty_points_effect_count.setter
    def created_loyalty_points_effect_count(self, created_loyalty_points_effect_count):
        """Sets the created_loyalty_points_effect_count of this Campaign.

        Total number of loyalty point creation effects triggered by rules in this campaign.  # noqa: E501

        :param created_loyalty_points_effect_count: The created_loyalty_points_effect_count of this Campaign.  # noqa: E501
        :type: int
        """

        self._created_loyalty_points_effect_count = created_loyalty_points_effect_count

    @property
    def redeemed_loyalty_points_count(self):
        """Gets the redeemed_loyalty_points_count of this Campaign.  # noqa: E501

        Total number of loyalty points redeemed by rules in this campaign.  # noqa: E501

        :return: The redeemed_loyalty_points_count of this Campaign.  # noqa: E501
        :rtype: float
        """
        return self._redeemed_loyalty_points_count

    @redeemed_loyalty_points_count.setter
    def redeemed_loyalty_points_count(self, redeemed_loyalty_points_count):
        """Sets the redeemed_loyalty_points_count of this Campaign.

        Total number of loyalty points redeemed by rules in this campaign.  # noqa: E501

        :param redeemed_loyalty_points_count: The redeemed_loyalty_points_count of this Campaign.  # noqa: E501
        :type: float
        """

        self._redeemed_loyalty_points_count = redeemed_loyalty_points_count

    @property
    def redeemed_loyalty_points_effect_count(self):
        """Gets the redeemed_loyalty_points_effect_count of this Campaign.  # noqa: E501

        Total number of loyalty point redemption effects triggered by rules in this campaign.  # noqa: E501

        :return: The redeemed_loyalty_points_effect_count of this Campaign.  # noqa: E501
        :rtype: int
        """
        return self._redeemed_loyalty_points_effect_count

    @redeemed_loyalty_points_effect_count.setter
    def redeemed_loyalty_points_effect_count(self, redeemed_loyalty_points_effect_count):
        """Sets the redeemed_loyalty_points_effect_count of this Campaign.

        Total number of loyalty point redemption effects triggered by rules in this campaign.  # noqa: E501

        :param redeemed_loyalty_points_effect_count: The redeemed_loyalty_points_effect_count of this Campaign.  # noqa: E501
        :type: int
        """

        self._redeemed_loyalty_points_effect_count = redeemed_loyalty_points_effect_count

    @property
    def last_activity(self):
        """Gets the last_activity of this Campaign.  # noqa: E501

        Timestamp of the most recent event received by this campaign.  # noqa: E501

        :return: The last_activity of this Campaign.  # noqa: E501
        :rtype: datetime
        """
        return self._last_activity

    @last_activity.setter
    def last_activity(self, last_activity):
        """Sets the last_activity of this Campaign.

        Timestamp of the most recent event received by this campaign.  # noqa: E501

        :param last_activity: The last_activity of this Campaign.  # noqa: E501
        :type: datetime
        """

        self._last_activity = last_activity

    @property
    def updated(self):
        """Gets the updated of this Campaign.  # noqa: E501

        Timestamp of the most recent update to the campaign or any of its elements.  # noqa: E501

        :return: The updated of this Campaign.  # noqa: E501
        :rtype: datetime
        """
        return self._updated

    @updated.setter
    def updated(self, updated):
        """Sets the updated of this Campaign.

        Timestamp of the most recent update to the campaign or any of its elements.  # noqa: E501

        :param updated: The updated of this Campaign.  # noqa: E501
        :type: datetime
        """

        self._updated = updated

    @property
    def created_by(self):
        """Gets the created_by of this Campaign.  # noqa: E501

        Name of the user who created this campaign if available.  # noqa: E501

        :return: The created_by of this Campaign.  # noqa: E501
        :rtype: str
        """
        return self._created_by

    @created_by.setter
    def created_by(self, created_by):
        """Sets the created_by of this Campaign.

        Name of the user who created this campaign if available.  # noqa: E501

        :param created_by: The created_by of this Campaign.  # noqa: E501
        :type: str
        """

        self._created_by = created_by

    @property
    def updated_by(self):
        """Gets the updated_by of this Campaign.  # noqa: E501

        Name of the user who last updated this campaign if available.  # noqa: E501

        :return: The updated_by of this Campaign.  # noqa: E501
        :rtype: str
        """
        return self._updated_by

    @updated_by.setter
    def updated_by(self, updated_by):
        """Sets the updated_by of this Campaign.

        Name of the user who last updated this campaign if available.  # noqa: E501

        :param updated_by: The updated_by of this Campaign.  # noqa: E501
        :type: str
        """

        self._updated_by = updated_by

    def to_dict(self):
        """Returns the model properties as a dict"""
        result = {}

        for attr, _ in six.iteritems(self.openapi_types):
            value = getattr(self, attr)
            if isinstance(value, list):
                result[attr] = list(map(
                    lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                    value
                ))
            elif hasattr(value, "to_dict"):
                result[attr] = value.to_dict()
            elif isinstance(value, dict):
                result[attr] = dict(map(
                    lambda item: (item[0], item[1].to_dict())
                    if hasattr(item[1], "to_dict") else item,
                    value.items()
                ))
            else:
                result[attr] = value

        return result

    def to_str(self):
        """Returns the string representation of the model"""
        return pprint.pformat(self.to_dict())

    def __repr__(self):
        """For `print` and `pprint`"""
        return self.to_str()

    def __eq__(self, other):
        """Returns true if both objects are equal"""
        if not isinstance(other, Campaign):
            return False

        return self.to_dict() == other.to_dict()

    def __ne__(self, other):
        """Returns true if both objects are not equal"""
        if not isinstance(other, Campaign):
            return True

        return self.to_dict() != other.to_dict()


class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        You must do this in-place without making a copy of the array.
        """
        #two pointer approach

        # Count the zeros
        numOfZeros = 0
        for x in nums:
            if x == 0:
                numOfZeros += 1

        # Same method as in LC27
        i = 0
        for x in nums:
            if x != 0:
                nums[i] = x
                i += 1

        if numOfZeros == 1:
            nums[-numOfZeros] = 0
        elif numOfZeros != 0:
            for w in range(1, numOfZeros + 1):
                nums[-w] = 0

########################################################################
        # Much cleaner version of the two pointer appraoch
        # Slow pointer = i
        # Faster pointer is the for loop.  Does the job of processing new elements
        i = 0
        for x in nums:
            if x != 0:
                nums[i] = x
                i += 1
        for w in range(i, len(nums)):
            nums[w] = 0


import jittor as jt
from jittor import nn
from jittor import Module
from jittor import init
from jittor.contrib import concat

class Single_ASPPModule(Module):
    def __init__(self, inplanes, planes, kernel_size, padding, dilation):
        super(Single_ASPPModule, self).__init__()
        self.atrous_conv = nn.Conv(inplanes, planes, kernel_size=kernel_size,
                                            stride=1, padding=padding, dilation=dilation, bias=False)
        self.bn = nn.BatchNorm(planes)
        self.relu = nn.ReLU()

    def execute(self, x):
        x = self.atrous_conv(x)
        x = self.bn(x)
        x = self.relu(x)
        return x

class ASPP(Module):
    def __init__(self, output_stride):
        super(ASPP, self).__init__()
        inplanes = 2048
        if output_stride == 16:
            dilations = [1, 6, 12, 18]
        elif output_stride == 8:
            dilations = [1, 12, 24, 36]
        else:
            raise NotImplementedError

        self.aspp1 = Single_ASPPModule(inplanes, 256, 1, padding=0, dilation=dilations[0])
        self.aspp2 = Single_ASPPModule(inplanes, 256, 3, padding=dilations[1], dilation=dilations[1])
        self.aspp3 = Single_ASPPModule(inplanes, 256, 3, padding=dilations[2], dilation=dilations[2])
        self.aspp4 = Single_ASPPModule(inplanes, 256, 3, padding=dilations[3], dilation=dilations[3])
        self.global_avg_pool = nn.Sequential(GlobalPooling(),
                                             nn.Conv(inplanes, 256, 1, stride=1, bias=False),
                                             nn.BatchNorm(256),
                                             nn.ReLU())
        self.conv1 = nn.Conv(1280, 256, 1, bias=False)
        self.bn1 = nn.BatchNorm(256)
        self.relu = nn.ReLU()

    def execute(self, x):
        x1 = self.aspp1(x)
        x2 = self.aspp2(x)
        x3 = self.aspp3(x)
        x4 = self.aspp4(x)
        x5 = self.global_avg_pool(x)
        x5 = x5.broadcast((1,1,x4.shape[2],x4.shape[3]))
        x = concat((x1, x2, x3, x4, x5), dim=1)
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        return x

class GlobalPooling (Module):
    def __init__(self):
        super(GlobalPooling, self).__init__()
    def execute (self, x):
        return jt.mean(x, dims=[2,3], keepdims=1)


"""
Hyperparameters
Initialization of all hyperparameters

"""

# initialize parameters for the environment and algorithm
def initParams():
    # name of the repository
    repo_name = 'StatArbitrage_ex1'

    # parameters for the model
    envParams = {'kappa' : 2, # kappa of the OU process
              'sigma' : 0.2, # standard deviation of the OU process
              'theta' : 1, # mean-reversion level of the OU process
              'phi' : 0.005, # transaction costs
              'psi' : 0.5, # terminal penalty on the inventory
              'T' : 1, # trading horizon
              'Ndt' : 5+1, # number of periods
              'max_q' : 5, # maximum value for the inventory
              'max_u' : 2} # maximum value for the trades

    # parameters for the algorithm
    algoParams = {'Ntrajectories' : 500, # number of generated trajectories
                    'Mtransitions' : 500, # number of additional transitions for each state
                    'Nepochs' : 300, # number of epochs of the whole algorithm
                    'gamma' : 1.00, # discount factor
                    'Nepochs_V_init' : 500, # number of epochs for the estimation of V during the first epoch
                    'Nepochs_V' : 50, # number of epochs for the estimation of V
                    'lr_V' : 1e-3, # learning rate of the neural net associated with V
                    'batch_V' : 200, # number of trajectories for each mini-batch in estimating V
                    'hidden_V' : 32, # number of hidden nodes in the neural net associated with V
                    'layers_V' : 4, # number of layers in the neural net associated with V
                    'Nepochs_pi' : 10, # number of epoch for the update of pi
                    'lr_pi' : 1e-3, # learning rate of the neural net associated with pi
                    'batch_pi' : 200, # number of trajectories for each mini-batch when updating pi
                    'hidden_pi' : 32, # number of hidden nodes in the neural net associated with pi
                    'layers_pi' : 3, # number of layers in the neural net associated with pi
                    'Nsims_optimal' : 1000, # number of simulations when using the brute force method
                    'seed' : None} # set seed for replication purposes

    return repo_name, envParams, algoParams

# print parameters for the environment and algorithm
def printParams(envParams, algoParams):
    print('*  T: ', envParams["T"],
            ' Ndt: ', envParams["Ndt"],
            ' kappa: ', envParams["kappa"],
            ' sigma: ', envParams["sigma"],
            ' theta: ', envParams["theta"],
            ' phi: ', envParams["phi"],
            ' psi: ', envParams["psi"],
            ' max_q: ', envParams["max_q"],
            ' max_u: ', envParams["max_u"])
    print('*  Ntrajectories: ', algoParams["Ntrajectories"],
            ' Mtransitions: ', algoParams["Mtransitions"],
            ' Nepochs: ', algoParams["Nepochs"],
            ' Nsims_optimal: ', algoParams["Nsims_optimal"])
    print('*  Nepochs_V_init: ', algoParams["Nepochs_V_init"],
            ' Nepochs_V: ', algoParams["Nepochs_V"],
            ' lr_V: ', algoParams["lr_V"],
            ' batch_V: ', algoParams["batch_V"],
            ' hidden_V: ', algoParams["hidden_V"],
            ' layers_V: ', algoParams["layers_V"])
    print('*  Nepochs_pi: ', algoParams["Nepochs_pi"],
            ' lr_pi: ', algoParams["lr_pi"],
            ' batch_pi: ', algoParams["batch_pi"],
            ' hidden_pi: ', algoParams["hidden_pi"],
            ' layers_pi: ', algoParams["layers_pi"])


from collections import Counter

try:
    file = open('try.txt', 'r')
except FileNotFoundError:
    print('You are an idiot, choose a file in the laptop or the correct path')
    raise

file_read = file.read()

file.close()

file_read = file_read.split(' ')
# print(file_read)
# file_read.count('and')

list_of_word = []
for i in file_read:
    i = i.replace(',','').replace('.','').replace('[','').replace('\n','').replace(']','').lower()
    list_of_word.append(i)

bella = Counter(list_of_word)
print(bella)
print(type(bella))

D = {}
for word in list_of_word:
    if word in D.keys():
        D[word] += 1
    else:
        D[word] = 1
print(D['by'])


import streamlit as st
import pandas as pd
import plotly.express as px
from email.policy import default
from operator import index
import matplotlib.pyplot as plt
from streamlit_option_menu import option_menu

import numpy as np
from flask import Flask, request, jsonify, render_template, url_for
import joblib
from sklearn import svm

@st.cache(allow_output_mutation=True, persist = True)
def cargar_de_datos():
    leer_columnas = ['ESTU_DEPTO_RESIDE','ESTU_GENERO','FAMI_TIENECOMPUTADOR','FAMI_TIENEINTERNET','FAMI_ESTRATOVIVIENDA','FAMI_COMECARNEPESCADOHUEVO','PUNT_GLOBAL']
    df = pd.read_csv("icfes_data.csv",low_memory=False, usecols=leer_columnas)
    return df

st.set_page_config(
     page_title="PREDICCIÓN ICFES",
     layout="wide",
     initial_sidebar_state="expanded"
 )

df = cargar_de_datos().copy()
with st.sidebar:
    st.image('logo.png')
    menu = option_menu(
        menu_title = "BIENVENIDO",
        options = ['Inicio','Exploración', 'Predicción','Contactenos'],
        default_index =0
    )

if menu == 'Inicio':

    # Título
    html_temp = """

ICFES COLOMBIA 2018-2021

    """
    st.markdown(html_temp,unsafe_allow_html=True)

    st.subheader("El dataset en el cual estamos trabajando es *Icfes Colombia 2018-2021* en este encontramos datos relacionados con las características de estudiantes que realizaron las  pruebas icfes en unos periodos determinados por año. En el dataset original se describe información   general, académica, familiar y socioeconómica del los estudiantes.")
    ver = st.button('Mostrar datos')
    if ver:
        st.dataframe(df)

        st.text("PUNTAJE GLOBAL POR DEPARTAMENTOS")
        cols = [i for i in df.columns if i.startswith('PUNT_')]
        inf = df.groupby(["ESTU_DEPTO_RESIDE"])[cols].mean()
        st.dataframe(inf)

        st.text("PUNTAJE GLOBAL - INTERNET")
        internet= df.groupby(["FAMI_TIENEINTERNET"])[["PUNT_GLOBAL"]].mean()
        st.dataframe(internet)

        st.text("PUNTAJE GLOBAL - COMPUTADOR")
        internet= df.groupby(["FAMI_TIENECOMPUTADOR"])[["PUNT_GLOBAL"]].mean()
        st.dataframe(internet)

        st.text("PUNTAJE GLOBAL POR ESTRATO")
        internet= df.groupby(["FAMI_ESTRATOVIVIENDA"])[["PUNT_GLOBAL"]].mean()
        st.dataframe(internet)

        st.text("PUNTAJE GLOBAL - ALIMENTACIÓN DEL ESTUDIANTE(CARNES-PESCADO-HUEVOS)")
        internet= df.groupby(["FAMI_COMECARNEPESCADOHUEVO"])[["PUNT_GLOBAL"]].mean()
        st.dataframe(internet)

elif menu == 'Exploración':

    # Título
    html_temp = """

EXPLORACIÓN

    """
    st.markdown(html_temp,unsafe_allow_html=True)

    a1 = df.groupby(by=['ESTU_DEPTO_RESIDE']).mean()[['PUNT_GLOBAL']]

    fig1 = px.bar(

        a1,
        x = "PUNT_GLOBAL",
        y = a1.index,
        orientation = "h",
        height=700, width = 1200,
        title= " PUNTAJE GLOBAL POR DEPARTAMENTOS ",
        color_discrete_sequence = ['#618AE8'] * len(a1),
        template = "plotly_white"
        ).update_layout(xaxis_title="PRORMEDIO DE PUNTAJE GLOBAL", yaxis_title="DEPARTAMENTOS",yaxis={'categoryorder':'total ascending'})

    fig1

    a2 = df.groupby(by=['FAMI_ESTRATOVIVIENDA']).mean()[['PUNT_GLOBAL']]

    fig2 = px.bar(

        a2,
        x = "PUNT_GLOBAL",
        y = a2.index,
        height=400, width = 1200,
        title= " PROMEDIO DE PUNTAJE GLOBAL POR ESTRATOS ",
        color_discrete_sequence = ['#618AE8'] * len(a2),
        template = "plotly_white"
        ).update_layout(xaxis_title=" PUNTAJE DE PUNTAJE GLOBAL", yaxis_title="ESTRATO",yaxis={'categoryorder':'total ascending'})

    fig2

    a3 = df.groupby(by=['FAMI_COMECARNEPESCADOHUEVO']).mean()[['PUNT_GLOBAL']]
    fig3 = px.bar(

        a3,
        x = "PUNT_GLOBAL",
        y = a3.index,
        height=400, width = 1200,
        title= " PUNTAJE GLOBAL POR ALIMENTACIÓN ",
        color_discrete_sequence = ['#618AE8'] * len(a3),
        template = "plotly_white"
        ).update_layout(xaxis_title="PROMEDIO DE PUNTAJE GLOBAL", yaxis_title="VECES QUE CONSUME CARNES-PESCADO-HUEVO",yaxis={'categoryorder':'total ascending'})

    fig3

    a3 = df.groupby(by=['ESTU_GENERO']).mean()[['PUNT_GLOBAL']]

    fig4 = px.bar(

        a3,
        x = "PUNT_GLOBAL",
        y = a3.index,
        orientation = "h",
        height=700, width = 1200,
        title= " PUNTAJE GLOBAL POR GENERO ",
        color_discrete_sequence = ['#618AE8'] * len(a3),
        template = "plotly_white"
        ).update_layout(xaxis_title="PRORMEDIO DE PUNTAJE GLOBAL", yaxis_title="GENERO",yaxis={'categoryorder':'total ascending'})

    fig4

    def genero_est():
            genero= df['FAMI_TIENEINTERNET'].value_counts()

            datos = ['Si', 'No']
            colores = ["#618AE8","#5c60e6"]

            plt.pie(genero, labels=datos,autopct="%0.lf %%", colors= colores,radius=0.5)

            plt.title(' PORCENTAJE CON INTERNET/SIN INTERNET')
            plt.show()

            st.pyplot(plt)

    genero_est()

#PREDICCION

elif menu == 'Predicción':

    # Path del modelo preentrenado
    MODEL_PATH = 'my_model.pkl'

    # Se recibe la imagen y el modelo, devuelve la predicción
    def model_prediction(x_in, model):

        x = np.asarray(x_in).reshape(1,-1)
        preds=model.predict(x)

        return preds

    with open(MODEL_PATH, 'rb') as file:
        model = joblib.load(file)

    # Título
    html_temp = """

SISTEMA DE PREDICCIÓN APROBACIÓN PRUEBAS ICFES EN COLOMBIA

    """
    st.markdown(html_temp,unsafe_allow_html=True)

    st.subheader("Se pretende predecir si el estudiante gana o pierde las pruebas icfes teniendo en cuenta las variables socioeconomicas ")

    st.subheader(" Nota: Gana las pruebas icfes apartir de un puntacion mayor o igual a 250, de lo contrario pierde la prueba")

    estratos = {'Sin Estrato':0,'Estrato 1':1 , 'Estrato 2':2 ,'Estrato 3':3 , 'Estrato 4':4 ,'Estrato 5':5,'Estrato 6':6}

    genero = {'Femenino':0,'Masculino':1}

    internet = {'No':0,'Si':1}
    computador = {'No':0,'Si':1}

    N = st.selectbox("Genero" , ('Masculino','Femenino'))
    C = st.selectbox("Tiene Computador:", ('Si','No'))
    K = st.selectbox("Tiene Internet:", ('Si','No'))
    P = st.selectbox("Estrato de Vivienda:", ('Sin Estrato','Estrato 1','Estrato 2','Estrato 3','Estrato 4','Estrato 5','Estrato 6'))

    N = genero[N]
    P = estratos[P]
    K = internet[K]
    C = computador[C]

    # El botón predicción se usa para iniciar el procesamiento
    if st.button("Predicción"):
        #x_in = list(np.float_((Datos.title().split('\t'))))
        x_in =[np.float_(N),
                    np.float_(P),
                    np.float_(K),
                    np.float_(C)
            ]
        predictS = model_prediction(x_in, model)

        pred = format(predictS[0]).upper()

        if(int(pred) == 1):
            a = "PERDIO"
        else:
            a = "GANO"

        st.success('Se predice que el estudiante ' + a + " las pruebas ICFES")

elif menu == 'Contactenos':

        # Título
    html_temp = """

GRUPO 1

JORGE LUIS MONTES GOMEZ

CUADRADO TRUJILLO DIEGO.

PERALTA OSORIO LUIS

DANIELA TORRES MINA

DIAZ MONTIEL JEAN CARLOS

MACEA PIMIENTA EDER

    """
    st.markdown(html_temp,unsafe_allow_html=True)


import torch

from torch import nn
from transformers import PreTrainedModel
from transformers.models.bert.modeling_bert import BertOnlyMLMHead
from Bert_Mutil_Replaced import myBertModel

class Similarity(nn.Module):

    def __init__(self, temp):
        super().__init__()
        self.temp = temp
        self.cos = nn.CosineSimilarity(dim=-1)

    def forward(self, x, y):
        return self.cos(x, y) / self.temp

class myModel(PreTrainedModel):

    def __init__(self, config=None, model_args=None):
        super(myModel, self).__init__(config)
        self.config = config
        self.model_args = model_args

        self.encoder = myBertModel.from_pretrained(self.model_args.pretrained_model_path)
        self.cls = BertOnlyMLMHead(self.config)
        self.sim = Similarity(self.model_args.temp)

    def forward(self, sentence_encoded, augged_info_idx, augged_info_input_ids, mlm_input_encoded, mlm_labels):
        # ==========================
        # 1. 对比的损失: CL_loss
        # ==========================
        ori_text_pooler_outs = self.encoder(**sentence_encoded).pooler_output  # shape: (batch_size, hidden_size)

        if augged_info_idx is not None:
            explain_pooler_outs = self.encoder(**augged_info_input_ids).pooler_output  # shape: (num_sememes, hidden_size)

            sentence_encoded.update({
                "explain_pooler_outs": explain_pooler_outs,
                "batch_index": augged_info_idx
            })
            exp_text_pooler_outs = self.encoder(**sentence_encoded).pooler_output  # shape: (batch_size, hidden_size)
        else:
            exp_text_pooler_outs = self.encoder(**sentence_encoded).pooler_output  # shape: (batch_size, hidden_size)

        z1, z2 = ori_text_pooler_outs.unsqueeze(1), exp_text_pooler_outs.unsqueeze(0)
        cos_sim = self.sim(z1, z2)

        labels = torch.arange(cos_sim.size(0)).long().to(self.model_args.device)
        loss_fct = nn.CrossEntropyLoss()

        CL_loss = loss_fct(cos_sim, labels)

        # ==========================
        # 2. MLM的损失: mlm_loss
        # ==========================
        sequence_output = self.encoder(**mlm_input_encoded).last_hidden_state
        prediction_scores = self.cls(sequence_output)

        mlm_loss = loss_fct(prediction_scores.view(-1, self.config.vocab_size), mlm_labels.view(-1))

        return CL_loss + self.model_args.mlm_weight * mlm_loss


# -*- coding: utf-8 -*-
"""
Created on Sun Jan 30 17:15:32 2022

@author: pyliu
"""
from copy import copy
import numpy as np

def adaptive_window(P, df_precise, note_density):
    """
    Creates rolling_window containing adaptive window_size for each frame
    P: warping path, of size 2xn_frames, with the warped musescore/ground truth indices in the 2nd column
    df_precise: pandas df of XML containing columns=['start', 't_dur','dur', 'pitch', 'vol', 'instrument', 'frame_start']
    note_density: how many notes are covered by each window
    """

    # create notes_per_frame (what number of notes are in a frame)
    path_gt = copy(P[:,1])
    frame_start = copy(df_precise["frame_start"])
    notes_per_frame = [] #how many notes per frame
    frames_per_note = [] #how many frames per note
    counter = 1
    j = 1
    for i in range(len(path_gt)-1):
        if path_gt[i] < frame_start[j]:
            counter += 1
        else:
            append_list = list(np.ones(counter)/counter)
            notes_per_frame = [*notes_per_frame, *append_list]
            frames_per_note.append(counter)
            counter = 1
            j += 1
    counter = len(path_gt) - len(notes_per_frame)
    append_list = list(np.ones(counter)/counter)
    notes_per_frame = [*notes_per_frame, *append_list]
    notes_per_frame = np.array(notes_per_frame)
    frames_per_note = np.array(frames_per_note)

    #converts note_per_frame into window sizes for a specified note_density
    i = 0   #left pointer for rolling window
    j = 0   #right pointer
    rolling_sum = notes_per_frame[0]
    rolling_window = []
    while j < len(notes_per_frame)-1:
        if rolling_sum >= note_density:
            rolling_sum -= notes_per_frame[j]
            j -= 1
            if rolling_sum <= note_density:
                rolling_window.append(j-i)
                rolling_sum -= notes_per_frame[i]
                i += 1
        if rolling_sum < note_density:
            j += 1
            rolling_sum += notes_per_frame[j]
    append_list = [rolling_window[-1] for k in range(len(notes_per_frame)-len(rolling_window))]
    rolling_window = [*rolling_window, *append_list]
    rolling_window = np.array(rolling_window)
    return rolling_window


import struct
import socket
import typing as T

def send_msg(sock: socket.socket, msg: bytes):
    assert isinstance(msg, bytes), "Expected msg to be of type bytes"

    # Prefix each message with a 4-byte length (network byte order)
    msg = struct.pack('>I', len(msg)) + msg
    sock.sendall(msg)

def recv_msg(sock: socket.socket) -> T.Optional[bytes]:
    # Read message length and unpack it into an integer
    raw_msglen = _recvall(sock, 4)
    if not raw_msglen:
        raise IOError
    msglen = struct.unpack('>I', raw_msglen)[0]
    # Read the message data
    data = _recvall(sock, msglen)
    if not data:
        raise IOError
    return data

def _recvall(sock: socket.socket, n: int) -> T.Optional[bytes]:
    # Helper function to recv n bytes or return None if EOF is hit
    data = b''
    while len(data) < n:
        packet = sock.recv(n - len(data))
        if not packet:
            return None
        data += packet
    return data


from .Veicle import Veicle

class Suply:
    def __init__(self, date, amount, value, veicle: Veicle, gastype) -> None:

        self.date = date
        self.amount = amount
        self.value = value
        self.veicle = veicle
        self.gasType = gastype


from selenium import webdriver
import unittest

class PageTest(unittest.TestCase):
	def setUp(self):
		self.browser = webdriver.Firefox()
	#def tearDown(self):
		#self.browser.quit()
	def test_browser_title(self):
		self.browser.get('http://127.0.0.1:8000')
		self.assertIn('Contact Tracing List', self.browser.title)
		self.fail('Finish the test!')

if __name__ == '__main__':
	unittest.main(warnings='ignore')


# gra szubienica

from random import choice

words_database = ['rabarbar']

searched_word = choice(words_database)

hidden_word = ['_ '] * len(searched_word)
#hidden_word = '_ ' * len(searched_word)

attempt_number = 0
is_game_won = False
while attempt_number < 2*len(searched_word):
    letter = input('Guess a letter: ')

    index = 0
    for character in searched_word:
        if character == letter:
            hidden_word[index] = letter
        index += 1
    print(hidden_word)
    print(' '.join(hidden_word))

    if'_' not in hidden_word:
        is_game_won = True
        break

    attempt_number += 1
if is_game_won:
    print('You won!')
else:
    print('You lost!')

if attempt_number == 5:
    break


# Logic and Approach:
# I used two arrays count_array and seq_array. count_array to capture the sequence for the nth term and sequence array as an input array.
# Sequence array has a base input as "1", after that i calculate the sequence and append it to count_array.

class Solution:
    def countAndSay(self, n: int) -> str:
        # if n == 1:
        #     return "1"

        count_n = 1
        count_char = 1
        seq_array = []
        count_array = []
        for i in range(1):
            seq_array.append("1")

        # print(seq_array)

        while count_n < n:
            for i in range(0,len(seq_array)):
                if i+1 < len(seq_array):
                    if seq_array[i] == seq_array[i+1]:
                        count_char+=1
                        i+=1
                        continue;

                count_array.append(str(count_char))
                count_array.append(seq_array[i])
                count_char = 1
                i+=1

            seq_array = count_array
            count_array = []
            count_n+=1

        return ''.join(seq_array)


# Example 1:

def array(arr, n):
    n = n % len(arr)
    return arr[-n:] + arr[:-n]
arr = [3, 1, 2, 5, 7]
rotated_array = array(arr, 2)
print(rotated_array)

# Example 2:

def array(arr, n):
    n = n % len(arr)
    return arr[-n:] + arr[:-n]
arr = [1, 2, 3, 4, 5]
rotated_array = array(arr, 1)
print(rotated_array)


import telebot
import random
import logging
from telebot import types

list1 = ["Пизда", "Хуй на"]

logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG)

bot = telebot.TeleBot("5345165100:AAF04G_rkh_uoxECYrTaOw_nTwV8PczpfX4", parse_mode=None)

@bot.message_handler(content_types=['text'])
def send_text(message):
    with open('tq.txt') as f:
        lines = f.readlines()
    try:
        with open("tq.txt", "a+") as file_object:
            print(message.text)
            file_object.seek(0)
            data = file_object.read(100)
            if len(data) > 0 :
                file_object.write("\n")
            file_object.write(message.text)
    except:
        pass
    if message.text.lower() == "да" or message.text.lower() == "@mrgshit_bot да":
        bot.reply_to(message, random.choice(list1))
    elif "твоя" in message.text.lower() and "мама" in message.text.lower():
        bot.reply_to(message, "Польская кошкодевочка с миской риса")
    elif "твой" in message.text.lower() and "папа" in message.text.lower():
        bot.reply_to(message, "Гений, миллиардер, плейбой, филантроп")
    elif message.text.lower() == "асу" or message.text.lower() == "@mrgshit_bot асу":
        bot.reply_to(message, "Пов: ввели санкции на буквы Ж Д А Ю")
    elif message.text.lower() == 'пизда':
        bot.reply_to(message, "Это моя фраза, чмо")
    elif message.text.lower() == 'хуй на':
        bot.reply_to(message, "Соси два")
    elif "алина" in message.text.lower():
        bot.reply_to(message, "Алина шлюха")
    elif "путин" in message.text.lower():
        bot.reply_to(message, "Путин хуйло")
    else:
        if random.randint(0,100) < 15 or "@mrgshit_bot" in message.text.lower():
            try:
                bot.reply_to(message, random.choice(lines))
            except:
                pass

@bot.message_handler(content_types=['document', 'audio'])
def handle_docs_audio(message):
    bot.reply_to(message, "2")
bot.polling()


##
##
##

import torch
from jaxtyping import Float
from torch import Tensor, nn

from deepsight.data.structs import BoundingBoxes
from deepsight.modeling.layers import LayerScale, gnn, position
from deepsight.utils.torch import Batched3DTensors, BatchedGraphs

from ._config import DecoderConfig

class Decoder(nn.Module):
    def __init__(self, config: DecoderConfig) -> None:
        super().__init__()

        self._num_heads = config.num_heads

        self.gaussian_heatmaps = position.GaussianHeatmaps()
        self.node_embeddings = position.SinusoidalBoxEmbeddings(
            config.hidden_dim, include_wh=True
        )
        self.edge_embeddings = position.SinusoidalPairwiseBoxEmbeddings(
            config.hidden_dim
        )

        self.layers = nn.ModuleList(
            [DecoderLayer(config) for _ in range(config.num_layers)]
        )

    def forward(
        self,
        features: Batched3DTensors,
        graphs: BatchedGraphs,
        boxes: BoundingBoxes,
    ) -> list[BatchedGraphs]:
        H, W = features.shape[-2:]

        edge_indices = graphs.edge_indices  # (2, E)
        first_boxes = boxes[edge_indices[0]]
        second_boxes = boxes[edge_indices[1]]
        union_boxes = first_boxes | second_boxes  # (E, 4)

        node_mask_list = []
        edge_mask_list = []
        for idx, (num_nodes, num_edges) in enumerate(graphs.sizes):
            node_mask_list.append(features.mask[idx, None].expand(num_nodes, -1, -1))
            edge_mask_list.append(features.mask[idx, None].expand(num_edges, -1, -1))

        nodes_mask = torch.cat(node_mask_list, dim=0)  # (N, H, W)
        edges_mask = torch.cat(edge_mask_list, dim=0)  # (E, H, W)

        node_heatmaps = self.gaussian_heatmaps(boxes, nodes_mask)  # (N, H, W)
        union_heatmaps = self.gaussian_heatmaps(union_boxes, edges_mask)  # (E, H, W)
        first_heatmaps = node_heatmaps[edge_indices[0]]  # (E, H, W)
        second_heatmaps = node_heatmaps[edge_indices[1]]  # (E, H, W)
        edge_heatmaps = torch.maximum(
            torch.maximum(first_heatmaps, second_heatmaps),
            union_heatmaps,
        )  # (E, H, W)

        node_heatmaps = node_heatmaps.flatten(1)  # (N, H * W)
        edge_heatmaps = edge_heatmaps.flatten(1)  # (E, H * W)
        heatmaps_graph = graphs.new_like(node_heatmaps, edge_heatmaps)

        heatmaps = torch.cat(
            [
                heatmaps_graph.nodes(pad_value=0.0),
                heatmaps_graph.edges(pad_value=0.0),
            ],
            dim=1,
        )  # (B, N + E, H * W)

        flattened_features = features.to_batched2d()  # (B, H * W, C)
        mask = flattened_features.mask[:, None].expand_as(heatmaps)
        attn_mask = heatmaps.masked_fill_(mask, -torch.inf)
        attn_mask = attn_mask.repeat(self._num_heads, 1, 1)  # (B * heads, N + E, H * W)

        node_embeddings = self.node_embeddings(boxes)  # (N, D)
        edge_embeddings = self.edge_embeddings(first_boxes, second_boxes)  # (E, D)
        embeddings_graph = graphs.new_like(node_embeddings, edge_embeddings)

        layer: DecoderLayer
        outputs = []
        for layer in self.layers:
            graphs = layer(
                graphs,
                embeddings_graph,
                flattened_features.tensor,
                attn_mask,
            )
            outputs.append(graphs)

        return outputs

    def __call__(
        self,
        features: Batched3DTensors,
        graphs: BatchedGraphs,
        boxes: BoundingBoxes,
    ) -> list[BatchedGraphs]:
        return super().__call__(features, graphs, boxes)  # type: ignore

class DecoderLayer(nn.Module):
    def __init__(self, config: DecoderConfig) -> None:
        super().__init__()

        self.pre_cross_attn_layernorm = nn.LayerNorm(config.hidden_dim)
        self.cross_attn = nn.MultiheadAttention(
            config.hidden_dim,
            config.num_heads,
            dropout=config.dropout,
            batch_first=True,
        )
        self.post_cross_attn_layerscale = LayerScale(config.hidden_dim)

        self.pre_gat_layernorm = nn.LayerNorm(config.hidden_dim)
        self.gat = gnn.GATConv(
            config.hidden_dim,
            config.num_heads,
            bias=True,
            dropout=config.dropout,
        )
        self.post_gat_layerscale = LayerScale(config.hidden_dim)

        self.pre_ffn_layernorm = nn.LayerNorm(config.hidden_dim)
        self.ffn = nn.Sequential(
            nn.Linear(config.hidden_dim, 4 * config.hidden_dim),
            nn.GELU(),
            nn.Dropout(config.dropout),
            nn.Linear(4 * config.hidden_dim, config.hidden_dim),
        )
        self.post_ffn_layerscale = LayerScale(config.hidden_dim)

    def _perform_cross_attention(
        self,
        graphs: BatchedGraphs,
        features: Float[Tensor, "B HW D"],
        attn_mask: Float[Tensor, "Bh (N+E) HW"],
    ) -> BatchedGraphs:
        nodes = graphs.nodes(pad_value=0.0)  # (B, N, D)
        edges = graphs.edges(pad_value=0.0)  # (B, E, D)
        # padded number of nodes and edges
        N, E = nodes.shape[1], edges.shape[1]

        queries = torch.cat([nodes, edges], dim=1)  # (B, N + E, D)
        queries, _ = self.cross_attn(
            queries,
            features,
            features,
            attn_mask=attn_mask,
            need_weights=False,
        )

        nodes, edges = torch.split(queries, [N, E], dim=1)
        return graphs.new_like(nodes, edges)

    def forward(
        self,
        graphs: BatchedGraphs,
        embeddings: BatchedGraphs,
        features: Float[Tensor, "B HW D"],
        attn_mask: Float[Tensor, "Bh (N+E) HW"],
    ) -> BatchedGraphs:
        # Perform cross-attention.
        nodes, edges = graphs.nodes(None), graphs.edges(None)
        N, E = nodes.shape[0], edges.shape[0]
        pre_cross_attn_queries = torch.cat([nodes, edges], dim=0)  # (N + E, D)
        queries = self.pre_cross_attn_layernorm(pre_cross_attn_queries)
        nodes, edges = torch.split(queries, [N, E], dim=0)
        graphs = graphs.new_like(nodes, edges)
        graphs = self._perform_cross_attention(graphs, features, attn_mask)
        nodes, edges = graphs.nodes(None), graphs.edges(None)
        queries = torch.cat([nodes, edges], dim=0)  # (N + E, D)
        post_cross_attn_queries = (
            pre_cross_attn_queries + self.post_cross_attn_layerscale(queries)
        )

        # Perform GAT
        pre_gat_queries = post_cross_attn_queries
        pre_gat_queries = self.pre_gat_layernorm(pre_gat_queries)
        nodes, edges = torch.split(pre_gat_queries, [N, E], dim=0)
        graphs = graphs.new_like(nodes, edges)
        graphs = self.gat(graphs, embeddings)
        nodes, edges = graphs.nodes(None), graphs.edges(None)
        queries = torch.cat([nodes, edges], dim=0)  # (N + E, D)
        post_gat_queries = pre_gat_queries + self.post_gat_layerscale(queries)

        # Perform FFN
        pre_ffn_queries = post_gat_queries
        queries = self.pre_ffn_layernorm(pre_gat_queries)
        queries = self.ffn(queries)
        post_fnn_queries = pre_ffn_queries + self.post_ffn_layerscale(queries)

        # Update graphs
        nodes, edges = torch.split(post_fnn_queries, [N, E], dim=0)
        return graphs.new_like(nodes, edges)

    def __call__(
        self,
        graphs: BatchedGraphs,
        embeddings: BatchedGraphs,
        features: Float[Tensor, "B HW D"],
        attn_mask: Float[Tensor, "Bh (N+E) HW"],
    ) -> BatchedGraphs:
        return super().__call__(graphs, embeddings, features, attn_mask)  # type: ignore


"""
Element
-------

A generic class for creating Elements.

"""

import base64
import json
import warnings
from binascii import hexlify
from collections import OrderedDict
from html import escape
from os import urandom
from pathlib import Path
from urllib.request import urlopen

from jinja2 import Environment, PackageLoader, Template

from .utilities import _camelify, _parse_size, none_max, none_min

ENV = Environment(loader=PackageLoader("branca", "templates"))

class Element:
    """Basic Element object that does nothing.
    Other Elements may inherit from this one.

    Parameters
    ----------
    template : str, default None
        A jinaj2-compatible template string for rendering the element.
        If None, template will be:

        .. code-block:: jinja

            {% for name, element in this._children.items() %}
            {{element.render(**kwargs)}}
            {% endfor %}

        so that all the element's children are rendered.
    template_name : str, default None
        If no template is provided, you can also provide a filename.

    """

    _template = Template(
        "{% for name, element in this._children.items() %}\n"
        "    {{element.render(**kwargs)}}"
        "{% endfor %}",
    )

    def __init__(self, template=None, template_name=None):
        self._name = "Element"
        self._id = hexlify(urandom(16)).decode()
        self._env = ENV
        self._children = OrderedDict()
        self._parent = None
        self._template_str = template
        self._template_name = template_name

        if template is not None:
            self._template = Template(template)
        elif template_name is not None:
            self._template = ENV.get_template(template_name)

    def __getstate__(self):
        """Modify object state when pickling the object.
        jinja2 Environment cannot be pickled, so set
        the ._env attribute to None. This will be added back
        when unpickling (see __setstate__)
        """
        state: dict = self.__dict__.copy()
        state["_env"] = None
        state.pop("_template", None)
        return state

    def __setstate__(self, state: dict):
        """Re-add ._env attribute when unpickling"""
        state["_env"] = ENV

        if state["_template_str"] is not None:
            state["_template"] = Template(state["_template_str"])
        elif state["_template_name"] is not None:
            state["_template"] = ENV.get_template(state["_template_name"])

        self.__dict__.update(state)

    def get_name(self):
        """Returns a string representation of the object.
        This string has to be unique and to be a python and
        javascript-compatible
        variable name.
        """
        return _camelify(self._name) + "_" + self._id

    def _get_self_bounds(self):
        """Computes the bounds of the object itself (not including it's children)
        in the form [[lat_min, lon_min], [lat_max, lon_max]]
        """
        return [[None, None], [None, None]]

    def get_bounds(self):
        """Computes the bounds of the object and all it's children
        in the form [[lat_min, lon_min], [lat_max, lon_max]].
        """
        bounds = self._get_self_bounds()

        for child in self._children.values():
            child_bounds = child.get_bounds()
            bounds = [
                [
                    none_min(bounds[0][0], child_bounds[0][0]),
                    none_min(bounds[0][1], child_bounds[0][1]),
                ],
                [
                    none_max(bounds[1][0], child_bounds[1][0]),
                    none_max(bounds[1][1], child_bounds[1][1]),
                ],
            ]
        return bounds

    def add_children(self, child, name=None, index=None):
        """Add a child."""
        warnings.warn(
            "Method `add_children` is deprecated. Please use `add_child` instead.",
            FutureWarning,
            stacklevel=2,
        )
        return self.add_child(child, name=name, index=index)

    def add_child(self, child, name=None, index=None):
        """Add a child."""
        if name is None:
            name = child.get_name()
        if index is None:
            self._children[name] = child
        else:
            items = [item for item in self._children.items() if item[0] != name]
            items.insert(int(index), (name, child))
            self._children = OrderedDict(items)
        child._parent = self
        return self

    def add_to(self, parent, name=None, index=None):
        """Add element to a parent."""
        parent.add_child(self, name=name, index=index)
        return self

    def to_dict(self, depth=-1, ordered=True, **kwargs):
        """Returns a dict representation of the object."""
        if ordered:
            dict_fun = OrderedDict
        else:
            dict_fun = dict
        out = dict_fun()
        out["name"] = self._name
        out["id"] = self._id
        if depth != 0:
            out["children"] = dict_fun(
                [
                    (name, child.to_dict(depth=depth - 1))
                    for name, child in self._children.items()
                ],
            )  # noqa
        return out

    def to_json(self, depth=-1, **kwargs):
        """Returns a JSON representation of the object."""
        return json.dumps(self.to_dict(depth=depth, ordered=True), **kwargs)

    def get_root(self):
        """Returns the root of the elements tree."""
        if self._parent is None:
            return self
        else:
            return self._parent.get_root()

    def render(self, **kwargs):
        """Renders the HTML representation of the element."""
        return self._template.render(this=self, kwargs=kwargs)

    def save(self, outfile, close_file=True, **kwargs):
        """Saves an Element into a file.

        Parameters
        ----------
        outfile : str or file object
            The file (or filename) where you want to output the html.
        close_file : bool, default True
            Whether the file has to be closed after write.
        """
        if isinstance(outfile, (str, bytes, Path)):
            fid = open(outfile, "wb")
        else:
            fid = outfile

        root = self.get_root()
        html = root.render(**kwargs)
        fid.write(html.encode("utf8"))
        if close_file:
            fid.close()

class Link(Element):
    """An abstract class for embedding a link in the HTML."""

    def get_code(self):
        """Opens the link and returns the response's content."""
        if self.code is None:
            self.code = urlopen(self.url).read()
        return self.code

    def to_dict(self, depth=-1, **kwargs):
        """Returns a dict representation of the object."""
        out = super().to_dict(depth=-1, **kwargs)
        out["url"] = self.url
        return out

class JavascriptLink(Link):
    """Create a JavascriptLink object based on a url.

    Parameters
    ----------
    url : str
        The url to be linked
    download : bool, default False
        Whether the target document shall be loaded right now.

    """

    _template = Template(
        '{% if kwargs.get("embedded",False) %}'
        ""
        "{% else %}"
        ''
        "{% endif %}",
    )

    def __init__(self, url, download=False):
        super().__init__()
        self._name = "JavascriptLink"
        self.url = url
        self.code = None
        if download:
            self.get_code()

class CssLink(Link):
    """Create a CssLink object based on a url.

    Parameters
    ----------
    url : str
        The url to be linked
    download : bool, default False
        Whether the target document shall be loaded right now.

    """

    _template = Template(
        '{% if kwargs.get("embedded",False) %}'
        ""
        "{% else %}"
        '<link rel="stylesheet" href="{{this.url}}"/>'
        "{% endif %}",
    )

    def __init__(self, url, download=False):
        super().__init__()
        self._name = "CssLink"
        self.url = url
        self.code = None
        if download:
            self.get_code()

class Figure(Element):
    """Create a Figure object, to plot things into it.

    Parameters
    ----------
    width : str, default "100%"
        The width of the Figure.
        It may be a percentage or pixel value (like "300px").
    height : str, default None
        The height of the Figure.
        It may be a percentage or a pixel value (like "300px").
    ratio : str, default "60%"
        A percentage defining the aspect ratio of the Figure.
        It will be ignored if height is not None.
    title : str, default None
        Figure title.
    figsize : tuple of two int, default None
        If you're a matplotlib addict, you can overwrite width and
        height. Values will be converted into pixels in using 60 dpi.
        For example figsize=(10, 5) will result in
        width="600px", height="300px".
    """

    _template = Template(
        "<!DOCTYPE html>\n"
        "\n"
        "\n"
        "{% if this.title %}<title>{{this.title}}</title>{% endif %}"
        "    {{this.header.render(**kwargs)}}\n"
        "\n"
        "\n"
        "    {{this.html.render(**kwargs)}}\n"
        "\n"
        "\n"
        "\n",
    )

    def __init__(
        self,
        width="100%",
        height=None,
        ratio="60%",
        title=None,
        figsize=None,
    ):
        super().__init__()
        self._name = "Figure"
        self.header = Element()
        self.html = Element()
        self.script = Element()

        self.header._parent = self
        self.html._parent = self
        self.script._parent = self

        self.width = width
        self.height = height
        self.ratio = ratio
        self.title = title
        if figsize is not None:
            self.width = str(60 * figsize[0]) + "px"
            self.height = str(60 * figsize[1]) + "px"

        # Create the meta tag.
        self.header.add_child(
            Element(
                '<meta http-equiv="content-type" content="text/html; charset=UTF-8" />',
            ),  # noqa
            name="meta_http",
        )

    def to_dict(self, depth=-1, **kwargs):
        """Returns a dict representation of the object."""
        out = super().to_dict(depth=depth, **kwargs)
        out["header"] = self.header.to_dict(depth=depth - 1, **kwargs)
        out["html"] = self.html.to_dict(depth=depth - 1, **kwargs)
        out["script"] = self.script.to_dict(depth=depth - 1, **kwargs)
        return out

    def get_root(self):
        """Returns the root of the elements tree."""
        return self

    def render(self, **kwargs):
        """Renders the HTML representation of the element."""
        for name, child in self._children.items():
            child.render(**kwargs)
        return self._template.render(this=self, kwargs=kwargs)

    def _repr_html_(self, **kwargs):
        """Displays the Figure in a Jupyter notebook."""
        html = escape(self.render(**kwargs))
        if self.height is None:
            iframe = (
                '
'
                '
'  # noqa
                'Make this Notebook Trusted to load map: File -> Trust Notebook'  # noqa
                '"
                ""
                "

"
            ).format(html=html, width=self.width, ratio=self.ratio)
        else:
            iframe = (
                ''
                ""
            ).format(html=html, width=self.width, height=self.height)
        return iframe

    def add_subplot(self, x, y, n, margin=0.05):
        """Creates a div child subplot in a matplotlib.figure.add_subplot style.

        Parameters
        ----------
        x : int
            The number of rows in the grid.
        y : int
            The number of columns in the grid.
        n : int
            The cell number in the grid, counted from 1 to x*y.

        Example:
        >>> fig.add_subplot(3, 2, 5)
        # Create a div in the 5th cell of a 3rows x 2columns
        grid(bottom-left corner).
        """
        width = 1.0 / y
        height = 1.0 / x
        left = ((n - 1) % y) * width
        top = ((n - 1) // y) * height

        left = left + width * margin
        top = top + height * margin
        width = width * (1 - 2.0 * margin)
        height = height * (1 - 2.0 * margin)

        div = Div(
            position="absolute",
            width=f"{100.0 * width}%",
            height=f"{100.0 * height}%",
            left=f"{100.0 * left}%",
            top=f"{100.0 * top}%",
        )
        self.add_child(div)
        return div

class Html(Element):
    """Create an HTML div object for embedding data.

    Parameters
    ----------
    data : str
        The HTML data to be embedded.
    script : bool
        If True, data will be embedded without escaping
        (suitable for embedding html-ready code)
    width : int or str, default '100%'
        The width of the output div element.
        Ex: 120 , '80%'
    height : int or str, default '100%'
        The height of the output div element.
        Ex: 120 , '80%'
    """

    _template = Template(
        '
'  # noqa
        "{% if this.script %}{{this.data}}{% else %}{{this.data|e}}{% endif %}
",
    )  # noqa

    def __init__(self, data, script=False, width="100%", height="100%"):
        super().__init__()
        self._name = "Html"
        self.script = script
        self.data = data

        self.width = _parse_size(width)
        self.height = _parse_size(height)

class Div(Figure):
    """Create a Div to be embedded in a Figure.

    Parameters
    ----------
    width: int or str, default '100%'
        The width of the div in pixels (int) or percentage (str).
    height: int or str, default '100%'
        The height of the div in pixels (int) or percentage (str).
    left: int or str, default '0%'
        The left-position of the div in pixels (int) or percentage (str).
    top: int or str, default '0%'
        The top-position of the div in pixels (int) or percentage (str).
    position: str, default 'relative'
        The position policy of the div.
        Usual values are 'relative', 'absolute', 'fixed', 'static'.
    """

    _template = Template(
        "{% macro header(this, kwargs) %}"
        ""
        "{% endmacro %}"
        "{% macro html(this, kwargs) %}"
        '
{{this.html.render(**kwargs)}}
'
        "{% endmacro %}",
    )

    def __init__(
        self,
        width="100%",
        height="100%",
        left="0%",
        top="0%",
        position="relative",
    ):
        super(Figure, self).__init__()
        self._name = "Div"

        # Size Parameters.
        self.width = _parse_size(width)
        self.height = _parse_size(height)
        self.left = _parse_size(left)
        self.top = _parse_size(top)
        self.position = position

        self.header = Element()
        self.html = Element(
            "{% for name, element in this._children.items() %}"
            "{{element.render(**kwargs)}}"
            "{% endfor %}",
        )
        self.script = Element()

        self.header._parent = self
        self.html._parent = self
        self.script._parent = self

    def get_root(self):
        """Returns the root of the elements tree."""
        return self

    def render(self, **kwargs):
        """Renders the HTML representation of the element."""
        figure = self._parent
        assert isinstance(figure, Figure), (
            "You cannot render this Element " "if it is not in a Figure."
        )

        for name, element in self._children.items():
            element.render(**kwargs)

        for name, element in self.header._children.items():
            figure.header.add_child(element, name=name)

        for name, element in self.script._children.items():
            figure.script.add_child(element, name=name)

        header = self._template.module.__dict__.get("header", None)
        if header is not None:
            figure.header.add_child(Element(header(self, kwargs)), name=self.get_name())

        html = self._template.module.__dict__.get("html", None)
        if html is not None:
            figure.html.add_child(Element(html(self, kwargs)), name=self.get_name())

        script = self._template.module.__dict__.get("script", None)
        if script is not None:
            figure.script.add_child(Element(script(self, kwargs)), name=self.get_name())

    def _repr_html_(self, **kwargs):
        """Displays the Div in a Jupyter notebook."""
        if self._parent is None:
            self.add_to(Figure())
            out = self._parent._repr_html_(**kwargs)
            self._parent = None
        else:
            out = self._parent._repr_html_(**kwargs)
        return out

class IFrame(Element):
    """Create a Figure object, to plot things into it.

    Parameters
    ----------
    html : str, default None
        Eventual HTML code that you want to put in the frame.
    width : str, default "100%"
        The width of the Figure.
        It may be a percentage or pixel value (like "300px").
    height : str, default None
        The height of the Figure.
        It may be a percentage or a pixel value (like "300px").
    ratio : str, default "60%"
        A percentage defining the aspect ratio of the Figure.
        It will be ignored if height is not None.
    figsize : tuple of two int, default None
        If you're a matplotlib addict, you can overwrite width and
        height. Values will be converted into pixels in using 60 dpi.
        For example figsize=(10, 5) will result in
        width="600px", height="300px".
    """

    def __init__(self, html=None, width="100%", height=None, ratio="60%", figsize=None):
        super().__init__()
        self._name = "IFrame"

        self.width = width
        self.height = height
        self.ratio = ratio
        if figsize is not None:
            self.width = str(60 * figsize[0]) + "px"
            self.height = str(60 * figsize[1]) + "px"

        if isinstance(html, str) or isinstance(html, bytes):
            self.add_child(Element(html))
        elif html is not None:
            self.add_child(html)

    def render(self, **kwargs):
        """Renders the HTML representation of the element."""
        html = super().render(**kwargs)
        html = "data:text/html;charset=utf-8;base64," + base64.b64encode(
            html.encode("utf8"),
        ).decode(
            "utf8",
        )  # noqa

        if self.height is None:
            iframe = (
                '
'
                '
'  # noqa
                ''
                ""
                "

"
            ).format(html=html, width=self.width, ratio=self.ratio)
        else:
            iframe = (
                ''
            ).format(html=html, width=self.width, height=self.height)
        return iframe

class MacroElement(Element):
    """This is a parent class for Elements defined by a macro template.
    To compute your own element, all you have to do is:

    * To inherit from this class
    * Overwrite the '_name' attribute
    * Overwrite the '_template' attribute with something of the form::

        {% macro header(this, kwargs) %}
            ...
        {% endmacro %}

        {% macro html(this, kwargs) %}
            ...
        {% endmacro %}

        {% macro script(this, kwargs) %}
            ...
        {% endmacro %}

    """

    _template = Template("")

    def __init__(self):
        super().__init__()
        self._name = "MacroElement"

    def render(self, **kwargs):
        """Renders the HTML representation of the element."""
        figure = self.get_root()
        assert isinstance(figure, Figure), (
            "You cannot render this Element " "if it is not in a Figure."
        )

        header = self._template.module.__dict__.get("header", None)
        if header is not None:
            figure.header.add_child(Element(header(self, kwargs)), name=self.get_name())

        html = self._template.module.__dict__.get("html", None)
        if html is not None:
            figure.html.add_child(Element(html(self, kwargs)), name=self.get_name())

        script = self._template.module.__dict__.get("script", None)
        if script is not None:
            figure.script.add_child(Element(script(self, kwargs)), name=self.get_name())

        for name, element in self._children.items():
            element.render(**kwargs)