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

import datetime
from collections import deque

import numpy as np
import pandas as pd

from portfolios import Asset
from portfolios.security.security import Security
from portfolios.stats.basics import returns_column
from portfolios.utils.helpers import standard_date_format, todays_date

class Portfolio(Asset):
    """
       Class that holds several securities
    """

    def __init__(self, name):
        super().__init__(name)
        self.securities = {}
        self.securities_archive = {}
        self.tickers = []
        self.tickers_archive = []
        self.dividends = pd.DataFrame(columns=["Date", "Ticker", "Amount"])
        self.payments = pd.DataFrame(columns=["Date", "In", "Out"])
        self.wallet = pd.DataFrame(columns=["Date", "Change"])
        self.total_portfolio_value = 0.0
        self.total_security_value = 0.0
        self.cash = 0.0
        self.return_value = 0.0
        self.return_rate = 0.0
        self.prices = {}
        self.prices_fifo = {}
        self.prices_lifo = {}
        self.index = 0
        self.benchmark_ticker = "sp500"
        self.benchmark = None
        self.transactions = pd.DataFrame(
            columns=["Date", "Transaction", "Ticker", "Currency", "Price", "Quantity"]
        )
        self.date = standard_date_format(todays_date())

    def __iter__(self):
        return self

    def __next__(self):
        if len(self.tickers) == 0:
            self.index = 0
            raise StopIteration
        elif self.index >= len(self.tickers):
            self.index = 0
            raise StopIteration
        else:
            self.index = self.index + 1
            return self.tickers[self.index - 1]

    def get_cash(self, date="2100-01-01"):
        date = standard_date_format(date)

        return self.wallet.loc[(self.wallet.Date <= date), "Change"].sum()

    def deposit_cash(self, date, currency="USD", price=1.0, quantity=0):
        """
           Adds an amount of quantity*price to the wallet
           Price acts as exchange rate if currency is not USD
        """
        self.wallet = self.wallet.append(
            {"Date": date, "Change": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "deposit",
                "Ticker": np.nan,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        # store payment in df
        self.payments = self.payments.append(
            {"Date": date, "In": 1.0 * price * quantity, "Out": 0.0}, ignore_index=True
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        print(
            "depositing {0:.2f} {2} (new balance: {1:.2f} {2})".format(
                quantity * price, self.cash, currency
            )
        )

    def withdraw_cash(self, date, currency="USD", price=1.0, quantity=0):
        """
           Takes amount of quantity*price out of wallet
        """
        self.wallet = self.wallet.append(
            {"Date": date, "Change": -1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "withdraw",
                "Ticker": np.nan,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        # store payments in df
        self.payments = self.payments.append(
            {"Date": date, "In": 0.0, "Out": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        if self.cash < 0.0:
            print(
                "Warning, cash balance negative: {0:.2f} {1}".format(
                    self.cash, currency
                )
            )
        else:
            print(
                "withdrawing {0:.2f} {2} (new balance: {1:.2f} {2})".format(
                    quantity * price, self.cash, currency
                )
            )  #

    def dividend(self, date, ticker="", currency="USD", price=1.0, quantity=0):
        """

        """
        self.wallet = self.wallet.append(
            {"Date": date, "Change": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "dividend",
                "Ticker": ticker,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        # store dividends in df
        self.dividends = self.dividends.append(
            {"Date": date, "Ticker": ticker, "Amount": 1.0 * price * quantity},
            ignore_index=True,
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        if ticker == "" or ticker != ticker:
            print(
                "interest {0:.2f} {2} (new balance: {1:.2f} {2})".format(
                    quantity * price, self.cash, currency
                )
            )
        else:
            print(
                "dividend {0} {1:.2f} {3} (new balance: {2:.2f} {3})".format(
                    ticker, quantity * price, self.cash, currency
                )
            )

    def add_security(self, ticker, min_date="2000-01-01"):
        if ticker in self.securities_archive:
            self.securities[ticker] = self.securities_archive[ticker]
            self.tickers.append(ticker)
        else:
            _security = Security(ticker, start=min_date, end=self.date)
            self.securities[_security.ticker] = _security
            self.tickers.append(ticker)
            self.prices[ticker] = deque()
            self.prices_lifo[ticker] = deque()
            self.prices_fifo[ticker] = deque()

    def add_security_archive(self, ticker, min_date="2000-01-01"):
        if ticker in self.securities:
            self.securities_archive[ticker] = self.securities[ticker]
            self.tickers_archive.append(ticker)
        else:
            _security = Security(ticker, start=min_date, end=self.date)
            self.securities_archive[_security.ticker] = _security
            self.tickers_archive.append(ticker)
            self.prices[ticker] = deque()
            self.prices_lifo[ticker] = deque()
            self.prices_fifo[ticker] = deque()

    def remove_security(self, ticker):
        # copy state of security to archive
        self.securities_archive[ticker] = self.securities[ticker]
        # delete from current list of securities
        del self.securities[ticker]
        self.tickers.remove(ticker)

    def buy_security(self, date, ticker, currency="USD", price=None, quantity=0):

        # potentially add ticker to list
        if ticker not in self.tickers:
            self.add_security(ticker)
            # print('adding', ticker)

        # and to archive
        if ticker not in self.tickers_archive:
            self.add_security_archive(ticker)

        # modify quantity for subsequent stock splits
        quantity, modifier = self.securities[ticker].modify_quantity(date, quantity)

        # get closing price of security for transaction date if price not provided
        # modify price according to stock splits
        try:
            if np.isnan(price):
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier
        except:
            if price is None:
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier

        # add number of securities to price deques
        # multiply by 1000 to capture fractional shares
        for i in range(np.int(np.ceil(quantity*1000))):
            self.prices[ticker].append(price)
            self.prices_lifo[ticker].append(price)
            self.prices_fifo[ticker].append(price)

        # store point in time value in wallet
        self.wallet = self.wallet.append(
            {"Date": date, "Change": -1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "buy",
                "Ticker": ticker,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        print(
            "buying {0:.2f} {1} (new balance: {2:.2f} {3})".format(
                quantity, ticker, self.cash, currency
            )
        )

    def sell_security(self, date, ticker, currency="USD", price=None, quantity=0):

        # modify quantity for subsequent stock splits
        quantity, modifier = self.securities[ticker].modify_quantity(date, quantity)

        # get closing price of security for transaction date if price not provided
        # modify price according to stock splits
        try:
            if np.isnan(price):
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier
        except:
            if price is None:
                price = self.securities[ticker].get_price_at(date)
            else:
                price = price / modifier

        # remove number of securities from price deques
        # multiply by 1000 to capture fractional shares
        for i in range(np.int(np.floor(quantity*1000))):
            _ = self.prices_fifo[ticker].popleft()
            _ = self.prices_lifo[ticker].pop()

        # make sure security is fully removed, correct for rounding errors
        _df = self.transactions.loc[
            self.transactions.Transaction.isin(("buy", "sell")), :
        ].copy()
        _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
            _df.Transaction == "sell", "Quantity"
        ].apply(lambda x: -x)

        # set quantity to exactly match remaining quantity in portfolio
        if _df.groupby(by=["Ticker"])["Quantity"].sum()[ticker] <= quantity * 1.0001:
            quantity = _df.groupby(by=["Ticker"])["Quantity"].sum()[ticker]

        # store point in time value in wallet
        self.wallet = self.wallet.append(
            {"Date": date, "Change": 1.0 * price * quantity}, ignore_index=True
        ).sort_values("Date")

        # store transaction in df
        self.transactions = self.transactions.append(
            {
                "Date": date,
                "Transaction": "sell",
                "Ticker": ticker,
                "Currency": currency,
                "Price": 1.0 * price,
                "Quantity": 1.0 * quantity,
                "TradeValue": 1.0 * price * quantity,
            },
            ignore_index=True,
            sort=False,
        ).sort_values("Date")

        self.cash = self.get_cash(date=todays_date())

        print(
            "selling {0:.2f} {1} (new balance: {2:.2f} {3})".format(
                quantity, ticker, self.cash, currency
            )
        )

        # potentially remove ticker from list
        _df = self.transactions.loc[
            self.transactions.Transaction.isin(("buy", "sell")), :
        ].copy()
        _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
            _df.Transaction == "sell", "Quantity"
        ].apply(lambda x: -x)
        if _df.groupby(by=["Ticker"])["Quantity"].sum()[ticker] <= 0.0001:
            self.remove_security(ticker)
            # print('removing', ticker)

    def overview(self):

        # you have to have one security in the portfolio for meaningful output
        if len(self.securities) > 0:

            # sum up by ticker
            _df = self.transactions.loc[
                self.transactions.Transaction.isin(("buy", "sell", "dividend")), :
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "sell", "TradeValue"] = _df.loc[
                _df.Transaction == "sell", "TradeValue"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "dividend", "TradeValue"] = _df.loc[
                _df.Transaction == "dividend", "TradeValue"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "dividend", "Quantity"] = 0
            self.overview_df = _df.groupby(by=["Ticker"])[
                "Quantity", "TradeValue"
            ].sum()

            # check if sum over volume of a security is < 0
            for index, row in self.overview_df.iterrows():
                if row["Quantity"] < -0.0001:
                    print(
                        "Negative volume encountered: {0:5}\t{1}".format(
                            index, row["Quantity"]
                        )
                    )

            # restrict to securities with volume > 0
            self.overview_df = self.overview_df.loc[self.overview_df.Quantity > 0.0001]

            for ticker in self.tickers:
                self.overview_df.loc[
                    self.overview_df.index == ticker, "LastPrice"
                ] = self.securities[ticker].get_last_price()
                # calculate average security prices from deque of prices
                self.overview_df.loc[
                    self.overview_df.index == ticker, "AvgPriceAll"
                ] = np.mean(self.prices[ticker])
                # calculate average security prices from deque of prices
                self.overview_df.loc[
                    self.overview_df.index == ticker, "AvgPriceFiFo"
                ] = np.mean(self.prices_fifo[ticker])
                # calculate average security prices from deque of prices (last in first out)
                self.overview_df.loc[
                    self.overview_df.index == ticker, "AvgPriceLiFo"
                ] = np.mean(self.prices_lifo[ticker])

            self.overview_df["CurrentValue"] = (
                self.overview_df["LastPrice"] * self.overview_df["Quantity"]
            )

            # sum up dividends by ticker
            self.overview_df["Dividends"] = self.dividends.groupby(by="Ticker")[
                "Amount"
            ].sum()

            self.overview_df["AvgPrice"] = self.overview_df["TradeValue"] / (
                1.0 * self.overview_df["Quantity"]
            )
            self.overview_df.fillna(0.0, inplace=True)
            self.overview_df["Return"] = (
                self.overview_df["CurrentValue"]
                - self.overview_df["TradeValue"]
                + self.overview_df["Dividends"]
            )

            # average price to value ratio
            self.overview_df["AvgPriceToValue"] = (
                self.overview_df["TradeValue"] / (self.overview_df["CurrentValue"] + self.overview_df["Dividends"])
            )

            # join in names of securities
            for ticker in self.tickers:
                self.overview_df.loc[
                    self.overview_df.index == ticker, "Description"
                ] = self.securities[ticker].name

        # make dummy df when no (more) securities in portfolio
        else:
            _d = {
                "Quantity": [0],
                "AvgPriceAll": [0],
                "AvgPriceFiFo": [0],
                "AvgPriceLiFo": [0],
                "AvgPrice": [0],
                "TradeValue": [0],
                "LastPrice": [0],
                "CurrentValue": [0],
                "Dividends": [0],
                "Return": [0],
                "AvgPriceToValue": [0],
                "Description": [np.nan],
            }
            self.overview_df = pd.DataFrame(data=_d, index=[""])

        # update portfolio stats
        self.total_security_value = self.overview_df[["CurrentValue"]].sum().values[0]
        self.total_portfolio_value = self.total_security_value + self.cash
        self.return_value = (
            self.total_security_value
            + self.cash
            - self.payments["In"].sum()
            + self.payments["Out"].sum()
        )
        if self.total_portfolio_value:
            try:
                self.return_rate = self.return_value / (self.payments["In"].sum() - self.payments["Out"].sum())
            except:
                self.return_rate = 0.0
        else:
            self.return_rate = 0.0

        print(
            self.overview_df[
                [
                    "Quantity",
                    "AvgPriceAll",
                    "AvgPriceFiFo",
                    "AvgPriceLiFo",
                    "AvgPrice",
                    "LastPrice",
                    "TradeValue",
                    "CurrentValue",
                    "Dividends",
                    "Return",
                    "AvgPriceToValue",
                    "Description",
                ]
            ]
        )
        print()
        print(
            "Total portfolio value:\t{0:8.2f} USD\nTotal security value:\t{1:8.2f} USD\nCash in wallet:\t\t{2:8.2f} USD\nTotal return:\t\t{3:8.2f} USD\t({4:.2f}%)".format(
                self.total_portfolio_value,
                self.total_security_value,
                self.cash,
                self.return_value,
                self.return_rate * 100.0,
            )
        )

    def overview_archive(self):

        # you have to have one security historically in the portfolio for meaningful output
        if len(self.securities_archive) > 0:

            # sum up by ticker
            _df = self.transactions.loc[
                self.transactions.Transaction.isin(("buy", "sell")), :
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "sell", "TradeValue"] = _df.loc[
                _df.Transaction == "sell", "TradeValue"
            ].apply(lambda x: -x)
            self.overview_archive_df = _df.groupby(by=["Ticker"])[
                "Quantity", "TradeValue"
            ].sum()

            # check if sum over volume of a security is < 0
            for index, row in self.overview_archive_df.iterrows():
                if row["Quantity"] < 0.0:
                    print(
                        "Negative volume encountered: {0:5}\t{1}".format(
                            index, row["Quantity"]
                        )
                    )

            # restrict to securities with volume == 0 (if in portfolio)
            if len(
                self.overview_archive_df.loc[self.overview_archive_df.Quantity == 0]
            ):
                self.overview_archive_df = self.overview_archive_df.loc[
                    self.overview_archive_df.Quantity == 0
                ]

                for ticker in self.tickers_archive:
                    self.overview_archive_df.loc[
                        self.overview_archive_df.index == ticker, "LastPrice"
                    ] = self.securities_archive[ticker].get_last_price()
                    # calculate average security prices from deque of prices
                    self.overview_archive_df.loc[
                        self.overview_archive_df.index == ticker, "AvgPriceAll"
                    ] = np.mean(self.prices[ticker])

                # sum up dividends by ticker
                self.overview_archive_df["Dividends"] = self.dividends.groupby(
                    by="Ticker"
                )["Amount"].sum()
                self.overview_archive_df.fillna(0.0, inplace=True)
                self.overview_archive_df["Return"] = (
                    -self.overview_archive_df["TradeValue"]
                    + self.overview_archive_df["Dividends"]
                )

                # join in names of securities
                for ticker in self.tickers_archive:
                    self.overview_archive_df.loc[
                        self.overview_archive_df.index == ticker, "Description"
                    ] = self.securities_archive[ticker].name

            # make dummy df when no (more) securities are in portfolio archive that are not in portfolio
            else:
                _d = {
                    "Quantity": [0],
                    "AvgPriceAll": [0],
                    "TradeValue": [0],
                    "LastPrice": [0],
                    "Dividends": [0],
                    "Return": [0],
                    "Description": [np.nan],
                }
                self.overview_archive_df = pd.DataFrame(data=_d, index=[""])

        # make dummy df when no (more) securities are in portfolio archive that are not in portfolio
        else:
            _d = {
                "Quantity": [0],
                "AvgPriceAll": [0],
                "TradeValue": [0],
                "LastPrice": [0],
                "Dividends": [0],
                "Return": [0],
                "Description": [np.nan],
            }
            self.overview_archive_df = pd.DataFrame(data=_d, index=[""])

        print(
            self.overview_archive_df[
                [
                    "Quantity",
                    "AvgPriceAll",
                    "LastPrice",
                    "TradeValue",
                    "Dividends",
                    "Return",
                    "Description",
                ]
            ]
        )

    def positions(self):
        # you have to have one security in the portfolio for meaningful output
        if len(self.securities) > 0:
            # sum up by ticker

            _df = self.transactions.loc[
                self.transactions.Transaction.isin(("buy", "sell")), :
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df.loc[_df.Transaction == "sell", "TradeValue"] = _df.loc[
                _df.Transaction == "sell", "TradeValue"
            ].apply(lambda x: -x)
            self.positions_df = _df.groupby(by=["Ticker"])[
                "Quantity", "TradeValue"
            ].sum()

            # sum up purchases
            self.positions_df["Bought"] = (
                _df.loc[_df.Transaction == "buy", :]
                .groupby(by=["Ticker"])["Quantity"]
                .sum()
            )
            self.positions_df["Invested"] = (
                _df.loc[_df.Transaction == "buy", :]
                .groupby(by=["Ticker"])["TradeValue"]
                .sum()
            )

            # sum up sales
            self.positions_df["Sold"] = (
                _df.loc[_df.Transaction == "sell", :]
                .groupby(by=["Ticker"])["Quantity"]
                .sum()
            )
            self.positions_df["Devested"] = (
                _df.loc[_df.Transaction == "sell", :]
                .groupby(by=["Ticker"])["TradeValue"]
                .sum()
            )

            # check if sum over volume of a security is < 0
            for index, row in self.positions_df.iterrows():
                if row["Quantity"] < 0.0:
                    print(
                        "Negative volume encountered: {0:5}\t{1}".format(
                            index, row["Quantity"]
                        )
                    )

            # join in last price and names of securities
            for ticker in set(self.positions_df.index):
                if ticker in self.tickers:
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "LastPrice"
                    ] = self.securities[ticker].get_last_price()
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "Description"
                    ] = self.securities[ticker].name
                elif ticker in self.tickers_archive:
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "LastPrice"
                    ] = self.securities_archive[ticker].get_last_price()
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "Description"
                    ] = self.securities_archive[ticker].name
                else:
                    _security = Security(ticker)
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "LastPrice"
                    ] = _security.get_last_price()
                    self.positions_df.loc[
                        self.positions_df.index == ticker, "Description"
                    ] = _security.name

            self.positions_df["CurrentValue"] = (
                self.positions_df["LastPrice"] * self.positions_df["Quantity"]
            )

            # sum up dividends by ticker
            self.positions_df["Dividends"] = self.dividends.groupby(by="Ticker")[
                "Amount"
            ].sum()

            # self.positions_df['AvgPrice'] = self.positions_df['TradeValue'] / (1.0*self.positions_df['Quantity'])
            self.positions_df.fillna(0.0, inplace=True)
            self.positions_df["Return"] = (
                self.positions_df["CurrentValue"]
                - self.positions_df["TradeValue"]
                + self.positions_df["Dividends"]
            )
            self.positions_df["PercentGrowth"] = (
                100.0 * self.positions_df["Return"] / self.positions_df["Invested"]
            )
            self.positions_df.PercentGrowth.replace(
                [np.inf, -np.inf], np.nan, inplace=True
            )

            print(
                self.positions_df[
                    [
                        "Quantity",
                        "Bought",
                        "Sold",
                        "CurrentValue",
                        "Invested",
                        "Devested",
                        "Dividends",
                        "Return",
                        "PercentGrowth",
                        "Description",
                    ]
                ].sort_values(by=["CurrentValue", "Invested"], ascending=False)
            )

        else:
            print("No positions in portfolio.")

    def get_timeseries(self):

        # get date range from the transaction list
        self.min_date = min(self.transactions.Date.min(), self.payments.Date.min())
        self.max_date = datetime.datetime.now()

        # make a list of days between min and max date as index for timeseries df
        date_index = pd.date_range(self.min_date, self.max_date, freq="D")
        _ts = pd.Series(range(len(date_index)), index=date_index)

        # create timeseries df from date index and wallet entries
        _df_ts = (
            _ts.to_frame("Day")
            .join(self.wallet[["Date", "Change"]].groupby(by="Date").sum(), how="left")
            .fillna(method="ffill")
        )

        # join in daily price data for each security
        for security in self.tickers_archive:
            _series = self.securities_archive[security].data["Close"]
            _series = _series.rename(str(_series.name) + "_" + security)
            _df_ts = _df_ts.join(_series, how="left", rsuffix="").fillna(method="ffill")

        for security in self.tickers_archive:
            _df = self.transactions.loc[
                (self.transactions.Ticker == self.securities_archive[security].ticker)
                & (self.transactions.Transaction.isin(("buy", "sell"))),
                :,
            ].copy()
            _df.loc[_df.Transaction == "sell", "Quantity"] = _df.loc[
                _df.Transaction == "sell", "Quantity"
            ].apply(lambda x: -x)
            _df = _df[["Date", "Quantity"]].groupby(by=["Date"]).sum().cumsum()
            _df.columns = _df.columns.map(lambda x: str(x) + "_" + security)
            _df_ts = _df_ts.join(_df, how="left", rsuffix="").fillna(method="ffill")

        # fill all values forward then calculate value in portfolio
        _df_ts = _df_ts.fillna(method="ffill")
        for security in self.tickers_archive:
            _df_ts[security] = (
                _df_ts["Close_" + security] * _df_ts["Quantity_" + security]
            )

        # calculate portfolio value
        _df_ts = _df_ts.join(
            self.wallet[["Date", "Change"]].groupby(by="Date").sum().cumsum(),
            how="left",
            rsuffix="_tot",
        ).fillna(method="ffill")
        _df_ts["Cash"] = _df_ts["Change_tot"]
        _df_ts["Total"] = _df_ts["Change_tot"]
        for security in self.tickers_archive:
            _df_ts["Total"] = _df_ts["Total"] + _df_ts[security].fillna(0)

        # calculate portfolio value
        _df = self.payments.groupby(by=["Date"]).sum().cumsum()
        _df["Total_deposited"] = _df["In"] - _df["Out"]
        _df_ts = _df_ts.join(_df, how="left", rsuffix="").fillna(method="ffill")
        _df_ts["Growth"] = _df_ts["Total"] / _df_ts["Total_deposited"]

        self.data = _df_ts[self.tickers_archive + ["Cash", "Total"]]
        self.data_growth = _df_ts[["Total", "Total_deposited", "Growth"]]

    def get_returns(self, column="Total"):
        try:
            self.data
        except AttributeError:
            self.get_timeseries()
        else:
            pass

        self.data_growth, _ = returns_column(df=self.data_growth, column=column)

        # get date range from the transaction list
        self.min_date = min(self.transactions.Date.min(), self.payments.Date.min())
        self.max_date = datetime.datetime.now()

        # make a list of days between min and max date as index for timeseries df
        date_index = pd.date_range(self.min_date, self.max_date, freq="D")
        _ts = pd.Series(range(len(date_index)), index=date_index)

        # create timeseries df from date index and wallet entries
        _df_ts = _ts.to_frame("Day")
        #

        # join in daily price change data for each security
        cols = []
        for security in self.tickers:
            _series = self.securities[security].data["Close"]
            _col_name = str(_series.name) + "_" + security
            _series = _series.rename(_col_name)
            _df_ts = _df_ts.join(_series, how="left", rsuffix="").fillna(method="ffill")
            _df_ts, _ = returns_column(df=_df_ts, column=_col_name, outname=security)
            cols.append(_col_name)

        self.returns = _df_ts.drop(cols, axis=1)

    def get_benchmark(self, benchmark_ticker="^GSPC"):

        if benchmark_ticker == "sp500" or benchmark_ticker == "^GSPC":
            _ticker = "^GSPC"
        else:
            _ticker = "^GSPC"  # S&P 500 as default

        # create security class object for benchmark ticker
        _benchmark = Security(_ticker, start=self.min_date, end=self.max_date)

        # calculate returns for the benchmark
        _benchmark.data, _ = returns_column(df=_benchmark.data, column="Close")

        self.benchmark = _benchmark

    def get_performance(self):
        try:
            self.data
        except AttributeError:
            self.get_timeseries()
        else:
            pass

        years = []
        growths = []
        dollar_returns = []
        for year in self.data_growth.index.year.unique():
            # get first and last day of year
            min_date = self.data_growth.loc[
                self.data_growth.index.year == year, :
            ].index.min()
            max_date = self.data_growth.loc[
                self.data_growth.index.year == year, :
            ].index.max()

            # compare growth (percentage and dollars) between end of year and beginning of year
            growth = 100 * (
                self.data_growth.loc[
                    self.data_growth.index == max_date, "Growth"
                ].values[0]
                - self.data_growth.loc[
                    self.data_growth.index == min_date, "Growth"
                ].values[0]
            )
            dollar_return = (
                self.data_growth.loc[
                    self.data_growth.index == max_date, "Total"
                ].values[0]
                - self.data_growth.loc[
                    self.data_growth.index == max_date, "Total_deposited"
                ].values[0]
            ) - (
                self.data_growth.loc[
                    self.data_growth.index == min_date, "Total"
                ].values[0]
                - self.data_growth.loc[
                    self.data_growth.index == min_date, "Total_deposited"
                ].values[0]
            )
            years.append(year)
            growths.append(growth)
            dollar_returns.append(dollar_return)

        _df = pd.DataFrame({"Year": years, "Growth": growths, "Return": dollar_returns})

        return _df

class TotalPortfolioValue(object):
    """
       Class that turns the portfolio total into a security-like object
    """

    def __init__(self, name, data):
        self.ticker = name
        self.data = data

    def set_name(self, name):
        self.name = name

    def update_data(self, data):
        self.data = data

    def get_returns(self, column="Total"):
        self.data, _ = returns_column(df=self.data, column=column)

    def get_benchmark(self, benchmark_ticker="^GSPC"):

        self.min_date = self.data.index.min()
        self.max_date = min(self.data.index.max(), datetime.datetime.now())

        if benchmark_ticker == "sp500" or benchmark_ticker == "^GSPC":
            _ticker = "^GSPC"
        else:
            _ticker = "^GSPC"  # S&P 500 as default

        # create security class object for benchmark ticker
        _benchmark = Security(_ticker, start=self.min_date, end=self.max_date)

        # calculate returns for the benchmark
        _benchmark.data, _ = returns_column(df=_benchmark.data, column="Close")

        self.benchmark = _benchmark


import re
from collections import namedtuple

class Files:
    def __init__(self, document, filepaths):
        self.d = document
        self.fl = filepaths  # list

    def order_files(self):
        File = namedtuple("File", ["num", "filepath"])
        # On récupère le modèle du nom de chemin complet vers un fichier
        exempleChemin = str(self.fl[0])
        exempleNom = exempleChemin.split("/")[-1]

        # On adapte l'ordonnancement des fichiers à CDS
        if exempleNom[:3] == "CdS":
            # Modèle de nom CdS02_Konv002-02_0027
            ordered_files = sorted([File(int(re.search(r"(CdS\d+_Konv\d+-?\d*_)(\d+)", f.name).group(2)), f)
                                    for f in self.fl])
        else:
            ordered_files = sorted([File(int(re.search(r"(.*f)(\d+)", f.name).group(2)), f) for f in self.fl])

        return ordered_files


# !/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@author  : v_jiaohaicheng@baidu.com
@des     :

"""
WIN = {
    "MAX_X": 641,
    "MAX_Y": 411,
    "MIN_X": 641,
    "MIN_Y": 411,
    "TITLE": "Chatgpt-皮卡丘",
}
SLEEP = {
    # 输出结果每个字符间隔时间
    "OUT_STEP": 0.1,
}
STYLE = {
    # 样式文件路径
    "THEME": "dark_teal.xml"
}
NET = {
    # 连接网络获取数据(开启后变慢)
    "NETWORK":True,
    # 上下文支持
    "WITHOUTCONTEXT":False,
    # 流式输出
    "STREAM":True,
}


import torch.cuda
from dataset_processing.dataset_processing_pipeline import data_pipeline
import torch.nn as nn
from torch.optim import lr_scheduler
import torch.optim as optim
import json
from model.model import CNNNLPModel
from tqdm import tqdm

from saving_loading_model.saving import save_checkpoint
from training.train import train
from training.evaluate import evaluate

def main(
        train_file,
        test_file,
        config_file,
        checkpoint_path,
        best_model_path
    ):
    device = 'cuda' if torch.cuda.is_available() else 'cpu'

    with open(config_file, 'r') as j:
        config = json.loads(j.read())

    for k,v in config['model'].items():
        v = float(v)
        if v < 1.0:
            config['model'][k] = float(v)
        else:
            config['model'][k] = int(v)

    for k,v in config['training'].items():
        v = float(v)
        if v < 1.0:
            config['training'][k] = float(v)
        else:
            config['training'][k] = int(v)

    train_itr, val_itr, test_itr = data_pipeline(
        train_file,
        test_file,
        config['training']['max_vocab'],
        config['training']['min_freq'],
        config['training']['batch_size'],
        device
    )

    model = CNNNLPModel(
        config['training']['max_vocab']+2,
        config['model']['emb_dim'],
        config['model']['hid_dim'],
        config['model']['model_layer'],
        config['model']['model_kernel_size'],
        config['model']['model_dropout'],
        device
    )
    optimizer = optim.Adam(model.parameters())
    criterion = nn.BCEWithLogitsLoss()
    scheduler = lr_scheduler.StepLR(optimizer,step_size=10,gamma=0.5)
    num_epochs = config['training']['n_epoch']
    clip = config['training']['clip']
    is_best = False
    best_valid_loss = float('inf')
    model = model.to(device)
    for epoch in tqdm(range(num_epochs)):

        train_loss = train(model, train_itr, optimizer, criterion, clip)
        valid_loss = evaluate(model, val_itr, criterion)

        # if (epoch + 1) % 2 == 0:
        print("training loss {}, validation_loss{}".format(train_loss,valid_loss))

        if valid_loss < best_valid_loss:
            best_valid_loss = valid_loss
            is_best = True
        checkpoint = {
            'epoch': epoch + 1,
            'valid_loss_min': valid_loss,
            'state_dict': model.state_dict(),
            'optimizer': optimizer.state_dict(),
        }
        save_checkpoint(checkpoint, is_best, checkpoint_path, best_model_path)
        scheduler.step()

if __name__=="__main__":
    train_file = r'C:\Users\Ankan\Downloads\sub_sub_train.csv'
    test_file = r'C:\Users\Ankan\Downloads\sub_test.csv'
    config_file = r'C:\Users\Ankan\Desktop\Github\CNN-with-NLP\config.json'
    ckpt_path = r'C:\Users\Ankan\Desktop\Github\CNN-with-NLP\ckpt_best_path\latest.pt'
    best_model_path = r'C:\Users\Ankan\Desktop\Github\CNN-with-NLP\ckpt_best_path\best.pt'
    main(train_file,test_file,config_file,ckpt_path,best_model_path)


import pika
import csv
import datetime

connection = pika.BlockingConnection(pika.ConnectionParameters(host="localhost"))
channel = connection.channel()
channel.queue_declare(queue="task_queue", durable=True)

def write_file(message_body):
    """
    Write a csv file in current working directory that appends with new
    values for each new message sent from the meter, as well as a timestamp,
    the value in kilowatts, and the sum of the powers (meter + PV), as requested in
    the project specifications.
    :param message body: string with the randomly generated watt amount
    :return: csv file object
    """
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    meter_value_in_kilowatts = int(message_body) / 1000

    with open("output_file.csv", "a") as f:
        writer = csv.DictWriter(
            f, fieldnames=["timestamp", "meter_value", "pv_power_value", "power_sum"]
        )
        if f.tell() == 0:  # only write header row once, at position 0
            writer.writeheader()
        writer.writerow(
            {
                "timestamp": timestamp,
                "meter_value": f"{message_body} watts",
                "pv_power_value": f"{meter_value_in_kilowatts} kilowatts",
                "power_sum": sum([int(message_body), meter_value_in_kilowatts]),
            }
        )

def callback(ch, method, properties, body):
    print(f"Recieved meter reading: {body.decode()} watts")
    write_file(body.decode())
    # basic_ack confirms successful delivery
    ch.basic_ack(delivery_tag=method.delivery_tag)

# basic_qos limits the unacknowledged deliveries to 1
channel.basic_qos(prefetch_count=1)
channel.basic_consume(queue="task_queue", on_message_callback=callback)

channel.start_consuming()


from django.urls import path
from . import views

# 设置命名空间
app_name = 'cms'

# 新闻管理url
urlpatterns = [
    path('', views.index, name='index'),
    path('write_news/', views.WriteNewsView.as_view(), name='write_news'),
    path('edit_news/', views.EditNewsView.as_view(), name='edit_news'),
    path('delete_news/', views.delete_news, name='delete_news'),
    path('news_category/', views.news_category, name='news_category'),
    path('add_news_category/', views.add_news_category, name='add_news_category'),
    path('edit_news_category/', views.edit_news_category, name='edit_news_category'),
    path('delete_news_category/', views.delete_news_category, name='delete_news_category'),
    path('banners/', views.banners, name='banners'),
    path('add_banner/', views.add_banner, name='add_banner'),
    path('banner_list/', views.banner_list, name='banner_list'),
    path('delete_banner/', views.delete_banner, name='delete_banner'),
    path('edit_banner/', views.edit_banner, name='edit_banner'),
    path('news_list/', views.NewsListView.as_view(), name='news_list'),
    path('upload_file/', views.upload_file, name='upload_file'),
    path('qntoken/', views.qntoken, name='qntoken'),
]

from . import course_views

# 课程管理url
urlpatterns += [
    path('pub_course/', course_views.PubCourseView.as_view(), name='pub_course'),
    path('course_category/', course_views.course_category, name='course_category'),
    path('add_course_category/', course_views.add_course_category, name='add_course_category'),
    path('edit_course_category/', course_views.edit_course_category, name='edit_course_category'),
    path('delete_course_category/', course_views.delete_course_category, name='delete_course_category'),
]


import plotly.graph_objects as go
import numpy as np
from scipy.interpolate import lagrange
from numpy.polynomial.polynomial import Polynomial
bots = [i+1 for i in range(15)]
time = [1.570,3.072,4.902,7.116,9.775,12.198,15.136,17.763,21.305,25.232,28.219,32.909,36.303,40.997,45.345]
poly = lagrange(bots,time)
x_new = np.arange(1,15.1,0.1)
y_new = Polynomial(poly.coef[::-1])(x_new)
print(type(y_new),type(x_new))
fig = go.Figure()
fig.add_trace(go.Scatter(x=bots,y=time,mode='lines'))
# fig.add_trace(go.Scatter(x=x_new,y=y_new,mode='lines'))
fig.add_trace(go.Scatter(x=x_new,y=np.interp(x_new,bots,time),mode='lines'))

fig.show()


t = int(input())
for _ in range(t):
    n = int(input())
    a = list(map(int, input().split()))
    ans = n
    for i in range(n):
        if a[i] != 1:
            ans = i
            break
    if (ans == n and ans % 2 == 1) or (ans < n and ans % 2 == 0):
        print("First")
    else:
        print("Second")


from writer import Writer

class PythonWriter(Writer):

    def __init__(self, functions, template_name):
        super().__init__(functions)
        self.language = "Python 3"
        self.main = template_name

    def write_template(self, file):
        try:
            file.write(self.get_template_header())

            for fxn in self.fxns:
                fxn_template = self.get_template_function(fxn)
                file.writelines(fxn_template)

        except IOError:
            return False

        return True

    def write_unittest(self, file):
        try:
            file.write(self.get_unittest_header())
            file.write(self.get_unittest_class_wrapper())

            for fxn in self.fxns:
                tests = self.get_unittests(fxn)
                file.writelines(tests)

            file.write(self.get_unittest_footer())

        except IOError:
            return False

        return True

    def get_template_header(self):
        return '"""\nProject _\n\nName: Boaty MacBoatface\nInstructor: Mike Ryu\nSection: __\n"""\n\n' \
               'from math import sqrt\n\n'

    def get_template_function(self, fxn):
        indent = '    '

        header = 'def {}({}):\n'.format(fxn.name, str(fxn.args_names).lstrip('[').rstrip(']').replace('\'', ''))

        design_recipe = indent + '"""\n'
        for drl in fxn.design_recipe_lines:
            design_recipe += indent + drl
        design_recipe += indent + '"""\n'

        body = indent + 'pass    # delete "pass" once you have real code for this function!\n'

        outline = '\n'
        for oln, level in fxn.body_outlines:
            outline += indent + (level * indent) + oln + '\n\n'

        return header + design_recipe + body + outline + '\n'

    def get_unittest_header(self):
        return 'import unittest\nfrom {} import *\n\n'.format(self.main)

    def get_unittest_footer(selfs):
        return '\nif __name__ == \'__main__\':\n    unittest.main()\n'

    def get_unittest_class_wrapper(self):
        return '\nclass TestCases(unittest.TestCase):\n'

    def get_unittests(self, fxn):
        indent = '    '
        tests = '\n' if fxn.examples else ''

        for count, ex in enumerate(fxn.examples):
            header = indent + 'def test_{}_{:d}(self):\n'.format(fxn.name, count + 1)
            expl = (indent * 2) + ex.expl + '\n' if ex.expl else ''

            expected = self.format_return_val(ex.expt)
            assert_type = self.determine_assert_type(ex.expt)
            args = str(ex.args).strip()[1:-1]
            body = (indent * 2) + self.determine_body(assert_type, fxn.name, args, expected)

            tests += header + expl + body + '\n'

        return tests

    def determine_body(self, assert_type, fxn_name, args, expected_val):
        if assert_type in ['assertTrue', 'assertFalse']:
            return 'self.{}({}({}))\n'.format(assert_type, fxn_name, args)
        else:
            return 'self.{}({}({}), {})\n'.format(assert_type, fxn_name, args, expected_val)

    def determine_assert_type(self, return_val):
        return_type = type(return_val)
        if return_type in [float, complex]:
            return 'assertAlmostEqual'
        elif return_type == bool:
            return 'assertTrue' if return_val else 'assertFalse'
        else:
            return 'assertEqual'

    def format_return_val(self, return_val):
        """ Dealing with None is done in function.py """
        if type(return_val) == str:
            return "\"{}\"".format(return_val.replace('"', '\"'))
        else:
            return return_val


import sys

input = sys.stdin.readline

def dfs(graph, src):
    if visited[src]: return
    visited[src] = True
    print(src)

    for next_node in graph[src]:
        if visited[next_node]: continue
        parent_table[next_node] = src
        dfs(graph, next_node)

def comb(visited, position, result):
    if visited == all_visited:
        return result

    if position >= len(sales):
        return

    curr_team = None
    for team, teams in teams_of_team.items():
        if position in teams and not visited & 1 << team:
            curr_team = team
    if not curr_team: return

    temp_result = float('inf')
    sel = comb(visited | 1<<curr_team, position + 1, result + sales[position])
    if sel:
        temp_result = min(temp_result, sel)
    unsel = comb(visited, position + 1, result)
    if unsel:
        temp_result = min(temp_result, unsel)

    return temp_result

if __name__ == '__main__':
    ## 매출하락 최소화: https://school.programmers.co.kr/learn/courses/30/lessons/72416
    sales = list(map(int, input().split()))
    links = []
    for _ in range(9):
        links.append(list(map(int, input().split())))

    sales = [0] + sales

    graph = [[] for _ in range(len(sales))]
    for u, v in links:
        graph[u].append(v)

    ## graph정보로 -> dfs로 depth를 넣어서, depth_table -> parent_table 채우기
    ##            -> depth테이블은... LCA (1) 같은depth까지 이동 (2) 동시 등산을 위한 것이니 까 꼭 안해도 된다?
    parent_table = [i for i in range(len(sales))]
    ## depth테이블은 visited + dfs의 node탐색으로 밖에 못채운다.

    ## 좌표탐색은 visisted가 안필요하다? -> 1개로 다 갈라쓴다(back은 visited영향안받고 빽된다)
    ## -> 순열 같이.. 매 경우의 수마다 [다른숫자를 선택하는 상태값]이 필요할때만 파라미터로 들고 다닌다ㅣ
    ## -> 순열이 아닌 좌표탐색 dfs는 visisted를 들고다니지 않는다. 1개만 필요. board로 대신쓸 수도 있다.
    # dfs(src=1, visited=1)
    visited = [False] * len(sales)
    dfs(graph=graph, src=1)

    # print(parent_table)
    # [0, 1, 10, 1, 5, 1, 10, 9, 10, 1, 5]
    ## 1번을 제외하고, 자기자신이 아닌, 부모로 1번이라도 거론되면, 팀장자격이 있다.
    ## parent_table -> 동일 부모별(value) 자식들(index)을 hash에 매핑하면, 팀원을 만들 수 있음.
    teams_of_team = {}
    for child, team_maker in enumerate(parent_table[1:], start=1):
        if team_maker in teams_of_team:
            # teams_of_team[team_maker].append(child)
            teams_of_team[team_maker].add(child)
        else:
            # teams_of_team[team_maker] = [child]
            ## {1: [1, 3, 5, 9], 10: [2, 6, 8], 5: [4, 10], 9: [7]}
            ## => 문제: 1번을 제외하고.. 자신의 자신의 팀에 안들어가게 해놨네..
            # 초기화할때 1번으로 자신을 팀원으로 추가
            # -> 중복될 수있으니, set으로 구성
            teams_of_team[team_maker] = {team_maker, child}

    # print(teams_of_team)
    # {1: {1, 3, 5, 9}, 10: {8, 10, 2, 6}, 5: {10, 4, 5}, 9: {9, 7}}

    ## 팀원별 1명씩 뽑되, 팀장을 뽑으면 더 좋은데? 빠지면서 뽑는 조합인데..
    ## 팀별 visited를 체킹해서 조합한다?!
    teams = teams_of_team.keys()
    ## 팀별visisted를 다 쓴 것이 종착역이다.
    ## -> bit를 종착역으로 쓸려면 [all marking bit]가 미리 값으로 만들어놔야한다.
    visited = 0
    all_visited = 0
    for team in teams:
        all_visited |= 1 << team
    # print(bin(all_visited)) # 0b11000100010
    print(comb(visited=0, position=1, result=0))


from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/ping')
def ping():
    return jsonify(ping = 'pong')

@app.route('/double')
def double():
    number = request.args.get('number')
    return jsonify(double = int(number) * 2)

if __name__ == '__main__':
    app.run(port = 8080)


import sys
from collections import deque

def read():
    return sys.stdin.readline().strip()

def bfs(y, x):
    ans = [[[0, 0] for i in range(m)] for i in range(n)]
    ans[y][x][0] = 1
    q = deque([[y, x, 0]])

    while q:
        y, x, b = q.popleft()

        if y == n - 1 and x == m - 1:
            print(ans[y][x][b])
            return

        for i in range(4):
            ny = y + dy[i]
            nx = x + dx[i]

            if 0 <= ny < n and 0 <= nx < m:

                if graph[ny][nx] == 1 and b == 0:
                    q.append([ny, nx, 1])
                    ans[ny][nx][1] = ans[y][x][b] + 1

                elif graph[ny][nx] == 0 and ans[ny][nx][b] == 0:
                    q.append([ny, nx, b])
                    ans[ny][nx][b] = ans[y][x][b] + 1

    print(-1)

dy = [1, -1, 0, 0]
dx = [0, 0, 1, -1]

n, m = map(int, read().split())
graph = [list(map(int, read())) for i in range(n)]

bfs(0, 0)

2206 벽 부수고 이동하기.py


import os.path as P

def create_parser():
    from optparse import OptionParser
    p = OptionParser("usage: python %prog training.csv test.csv language [options]")
    p.add_option("-t", "--temporary-dir", type="string", dest="temporary_dir", default=None, help="Specify the directory to use for storing temporary files")
    p.add_option("-i", "--index-file", type="string", dest="index_file", default=None, help="Specify the path to index file")
    return p

def main():
    parser = create_parser()
    opts, args = parser.parse_args()
    if len(args) != 3:
        parser.error("invalid number of arguments")
    training_csv, test_csv, process_language = args

    print("Arguments: %s %s %s" % (training_csv, test_csv, process_language))

    temporary_dir = opts.temporary_dir if opts.temporary_dir else P.dirname(training_csv)
    if not P.isdir(temporary_dir):
        parser.error("error: temporary directory %s does not exist" % temporary_dir)
    print("Temporary dir: %s" % (temporary_dir))

    index_file = opts.index_file if opts.index_file else None
    if index_file is None:
        parser.error("error: index file not specified")
    if not P.exists(index_file):
        parser.error("error: index file %s does not exist" % index_file)
    print("Index file: %s" % (index_file))
    print("Test message")
    print("OK")

if __name__ == "__main__":
    import sys
    sys.exit(main())


# Solution to Exercise 3.3 from Cracking the Coding Interview, 5th Edition
# Nitin Punjabi
# nptoronto@yahoo.com
# https://github.com/nitinpunjabi

class SetOfStacks:

  def __init__(self, sub_stack_size):
    self.sub_stack_size = sub_stack_size
    self.num_of_sub_stacks = 0
    self.cur_sub_stack_len = 0
    self.stacks = []

  def push(self, item):
    if not self.stacks or self.cur_sub_stack_len is self.sub_stack_size:
      self.stacks.append([item])
      self.num_of_sub_stacks += 1
      self.cur_sub_stack_len = 0
    else:
      self.stacks[-1].append(item)

    self.cur_sub_stack_len += 1

  def pop(self):
    if not self.stacks:
      return None

    item = self.stacks[-1].pop()
    if self.stacks[-1]:
      self.cur_sub_stack_len -= 1
    else:

      # This deals with cases where pop_at may have completely emptied an
      # earlier stack.
      while self.num_of_sub_stacks is not 0 and not self.stacks[-1]:
        self.stacks.pop()
        self.num_of_sub_stacks -= 1

      if self.stacks:
        self.cur_sub_stack_len = len(self.stacks[-1])
      else:
        self.cur_sub_stack_len = 0

    return item

  def pop_at(self, idx):
    if idx < 0 or idx >= self.num_of_sub_stacks:
      raise Exception("Stack index out of range")

    if self.stacks[idx]:
      return self.stacks[idx].pop()


import sys
import typing
from typing import Any, Callable, Dict, List, Tuple, TypeVar, Union

from docutils import nodes
from docutils.parsers.rst.states import Inliner

# An entry of Directive.option_spec
DirectiveOption = Callable[[str], Any]

# Text like nodes which are initialized with text and rawsource
TextlikeNode = Union[nodes.Text, nodes.TextElement]

# type of None
NoneType = type(None)

# path matcher
PathMatcher = Callable[[str], bool]

# common role functions
RoleFunction = Callable[[str, str, str, int, Inliner, Dict[str, Any], List[str]],
                        Tuple[List[nodes.Node], List[nodes.system_message]]]

# title getter functions for enumerable nodes (see sphinx.domains.std)
TitleGetter = Callable[[nodes.Node], str]

# inventory data on memory
Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]]

def stringify(annotation: Any) -> str:
    res = _stringify(annotation)
    return res

def _stringify(annotation: Any) -> str:
    """Stringify type annotation object."""
    if isinstance(annotation, str):
        return annotation
    elif isinstance(annotation, TypeVar):  # type: ignore
        return annotation.__name__
    elif not annotation:
        return repr(annotation)
    elif annotation is NoneType:  # type: ignore
        return 'None'
    elif getattr(annotation, '__module__', None) == 'builtins':
        return annotation.__qualname__
    elif annotation is Ellipsis:
        return '...'

    if sys.version_info >= (3, 7):  # py37+
        return _stringify_py37(annotation)
    else:
        return _stringify_py36(annotation)

def _stringify_py37(annotation: Any) -> str:
    """stringify() for py37+."""
    module = getattr(annotation, '__module__', None)
    if module == 'typing':
        if getattr(annotation, '_name', None):
            qualname = annotation._name
        elif getattr(annotation, '__qualname__', None):
            qualname = annotation.__qualname__
        elif getattr(annotation, '__forward_arg__', None):
            qualname = annotation.__forward_arg__
        else:
            qualname = stringify(annotation.__origin__)  # ex. Union
    elif hasattr(annotation, '__qualname__'):
        qualname = '%s.%s' % (module, annotation.__qualname__)
    else:
        qualname = repr(annotation)

    if getattr(annotation, '__args__', None):
        if qualname == 'Union':
            if len(annotation.__args__) == 2 and annotation.__args__[1] is NoneType:  # type: ignore  # NOQA
                return 'Optional[%s]' % stringify(annotation.__args__[0])
            else:
                args = ', '.join(stringify(a) for a in annotation.__args__)
                return '%s[%s]' % (qualname, args)
        elif qualname == 'Callable':
            args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
            returns = stringify(annotation.__args__[-1])
            return '%s[[%s], %s]' % (qualname, args, returns)
        elif annotation._special:
            return qualname
        else:
            args = ', '.join(stringify(a) for a in annotation.__args__)
            return '%s[%s]' % (qualname, args)

    return qualname

def _stringify_py36(annotation: Any) -> str:
    """stringify() for py35 and py36."""
    module = getattr(annotation, '__module__', None)
    if module == 'typing':
        if getattr(annotation, '_name', None):
            qualname = annotation._name
        elif getattr(annotation, '__qualname__', None):
            qualname = annotation.__qualname__
        elif getattr(annotation, '__forward_arg__', None):
            qualname = annotation.__forward_arg__
        elif getattr(annotation, '__origin__', None):
            qualname = stringify(annotation.__origin__)  # ex. Union
        else:
            qualname = repr(annotation).replace('typing.', '')
    elif hasattr(annotation, '__qualname__'):
        qualname = '%s.%s' % (module, annotation.__qualname__)
    else:
        qualname = repr(annotation)

    if (isinstance(annotation, typing.TupleMeta) and  # type: ignore
            not hasattr(annotation, '__tuple_params__')):  # for Python 3.6
        params = annotation.__args__
        if params:
            param_str = ', '.join(stringify(p) for p in params)
            return '%s[%s]' % (qualname, param_str)
        else:
            return qualname
    elif isinstance(annotation, typing.GenericMeta):
        params = None
        if hasattr(annotation, '__args__'):
            # for Python 3.5.2+
            if annotation.__args__ is None or len(annotation.__args__) <= 2:  # type: ignore  # NOQA
                params = annotation.__args__  # type: ignore
            else:  # typing.Callable
                args = ', '.join(stringify(arg) for arg
                                 in annotation.__args__[:-1])  # type: ignore
                result = stringify(annotation.__args__[-1])  # type: ignore
                return '%s[[%s], %s]' % (qualname, args, result)
        elif hasattr(annotation, '__parameters__'):
            # for Python 3.5.0 and 3.5.1
            params = annotation.__parameters__  # type: ignore
        if params is not None:
            param_str = ', '.join(stringify(p) for p in params)
            return '%s[%s]' % (qualname, param_str)
    elif (hasattr(typing, 'UnionMeta') and
          isinstance(annotation, typing.UnionMeta) and  # type: ignore
          hasattr(annotation, '__union_params__')):  # for Python 3.5
        params = annotation.__union_params__
        if params is not None:
            if len(params) == 2 and params[1] is NoneType:  # type: ignore
                return 'Optional[%s]' % stringify(params[0])
            else:
                param_str = ', '.join(stringify(p) for p in params)
                return '%s[%s]' % (qualname, param_str)
    elif (hasattr(annotation, '__origin__') and
          annotation.__origin__ is typing.Union):  # for Python 3.5.2+
        params = annotation.__args__
        if params is not None:
            if len(params) == 2 and params[1] is NoneType:  # type: ignore
                return 'Optional[%s]' % stringify(params[0])
            else:
                param_str = ', '.join(stringify(p) for p in params)
                return 'Union[%s]' % param_str
    elif (isinstance(annotation, typing.CallableMeta) and  # type: ignore
          getattr(annotation, '__args__', None) is not None and
          hasattr(annotation, '__result__')):  # for Python 3.5
        # Skipped in the case of plain typing.Callable
        args = annotation.__args__
        if args is None:
            return qualname
        elif args is Ellipsis:
            args_str = '...'
        else:
            formatted_args = (stringify(a) for a in args)
            args_str = '[%s]' % ', '.join(formatted_args)
        return '%s[%s, %s]' % (qualname,
                               args_str,
                               stringify(annotation.__result__))
    elif (isinstance(annotation, typing.TupleMeta) and  # type: ignore
          hasattr(annotation, '__tuple_params__') and
          hasattr(annotation, '__tuple_use_ellipsis__')):  # for Python 3.5
        params = annotation.__tuple_params__
        if params is not None:
            param_strings = [stringify(p) for p in params]
            if annotation.__tuple_use_ellipsis__:
                param_strings.append('...')
            return '%s[%s]' % (qualname,
                               ', '.join(param_strings))

    return qualname


import turtle

window = turtle.Screen()
t = turtle.Turtle()

# องศา
t.showturtle()

#? Triangle
for i in range(3):
    t.forward(60)
    t.left(120)
t.up()
t.goto(180,180)
t.down()

#? Pentagon
for i in range(5):
    t.forward(80)
    t.left(72)

t.up()
t.goto(-180,180)
t.down()

#? Octagon
for i in range(8):
    t.forward(50)
    t.left(45)

window.exitonclick()


import Crypto.Cipher.AES as AES
import os
import random
import string

def randomVar():
    return ''.join(random.sample(string.ascii_lowercase, 8))

def do_Encryption(payload):
    counter = os.urandom(16)
    key = os.urandom(32)

    randkey = randomVar()
    randcounter = randomVar()
    randcipher = randomVar()
    randctypes = randomVar()

    encrypto = AES.new(key, AES.MODE_CTR, counter=lambda: counter)
    encrypted = encrypto.encrypt(payload.replace('ctypes',randctypes))

    newpayload = "# -*- coding: utf-8 -*- \n"
    newpayload += "import Crypto.Cipher.AES as AES \nimport ctypes as %s \n" % randctypes
    newpayload += "%s = '%s'.decode('hex') \n" % (randkey, key.encode('hex'))
    newpayload += "%s = '%s'.decode('hex') \n" % (randcounter, counter.encode('hex'))
    newpayload += "decrypto = AES.new(%s , AES.MODE_CTR, counter=lambda: %s )\n" % (randkey,randcounter)
    newpayload += "%s = decrypto.decrypt('%s'.decode('hex')) \n" % (randcipher, encrypted.encode('hex'))
    newpayload += "exec(%s)" % randcipher
    return newpayload


import numpy as np
import mne
import os
from sklearn.preprocessing import StandardScaler
from sklearn.utils import shuffle
from sklearn.model_selection import KFold
from torch.utils.data import TensorDataset, DataLoader
from sklearn.model_selection import train_test_split,StratifiedKFold
import torch.optim as optim
from sklearn.metrics import accuracy_score, classification_report
import torch.nn.init as init
import matplotlib.pyplot as plt
import pickle
from torch import nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Subset, TensorDataset
from sklearn.model_selection import train_test_split
import numpy as np
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
import torch
import numpy as np
import mne
import os
from sklearn.preprocessing import StandardScaler
from sklearn.utils import shuffle
from sklearn.model_selection import KFold
from torch.utils.data import TensorDataset, DataLoader
from sklearn.model_selection import train_test_split,StratifiedKFold
import torch.optim as optim
from sklearn.metrics import accuracy_score, classification_report
import torch.nn.init as init
import matplotlib.pyplot as plt
import pickle
from torch import nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
import torch
import torch.nn as nn
from torch.utils.data import Dataset
import os
import torch
from torch import nn
import torch
import torch.optim as optim
import random
import os
import pickle
import os
import re
import os
import re
from torch.utils.data import DataLoader
from sklearn.metrics import confusion_matrix, roc_auc_score, accuracy_score
import torch
import torch.nn as nn
import torch.nn.functional as F

class CBAM(nn.Module):
    def __init__(self, in_channel):
        super(CBAM, self).__init__()
        self.Cam = ChannelAttentionModul(in_channel=in_channel)  # 通道注意力模块
        self.Sam = SpatialAttentionModul(in_channel=in_channel)  # 空间注意力模块

    def forward(self, x):
        x = self.Cam(x)
        x = self.Sam(x)
        return x

class ChannelAttentionModul(nn.Module):  # 通道注意力模块
    def __init__(self, in_channel, r=0.5):  # channel为输入的维度, r为全连接层缩放比例->控制中间层个数
        super(ChannelAttentionModul, self).__init__()
        # 全局最大池化
        self.MaxPool = nn.AdaptiveMaxPool2d(1)

        self.fc_MaxPool = nn.Sequential(
            nn.Linear(in_channel, int(in_channel * r)),  # int(channel * r)取整数, 中间层神经元数至少为1, 如有必要可设为向上取整
            nn.ReLU(),
            nn.Linear(int(in_channel * r), in_channel),
            nn.Sigmoid(),
        )

        # 全局均值池化
        self.AvgPool = nn.AdaptiveAvgPool2d(1)

        self.fc_AvgPool = nn.Sequential(
            nn.Linear(in_channel, int(in_channel * r)),  # int(channel * r)取整数, 中间层神经元数至少为1, 如有必要可设为向上取整
            nn.ReLU(),
            nn.Linear(int(in_channel * r), in_channel),
            nn.Sigmoid(),
        )

        # 激活函数
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # 1.最大池化分支
        max_branch = self.MaxPool(x)
        # 送入MLP全连接神经网络, 得到权重
        max_in = max_branch.view(max_branch.size(0), -1)
        max_weight = self.fc_MaxPool(max_in)

        # 2.全局池化分支
        avg_branch = self.AvgPool(x)
        # 送入MLP全连接神经网络, 得到权重
        avg_in = avg_branch.view(avg_branch.size(0), -1)
        avg_weight = self.fc_AvgPool(avg_in)

        # MaxPool + AvgPool 激活后得到权重weight
        weight = max_weight + avg_weight
        weight = self.sigmoid(weight)

        # 将维度为b, c的weight, reshape成b, c, 1, 1 与 输入x 相乘
        h, w = weight.shape
        # 通道注意力Mc
        Mc = torch.reshape(weight, (h, w, 1, 1))

        # 乘积获得结果
        x = Mc * x

        return x

class SpatialAttentionModul(nn.Module):  # 空间注意力模块
    def __init__(self, in_channel):
        super(SpatialAttentionModul, self).__init__()
        self.conv = nn.Conv2d(2, 1, 7, padding=3)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        # x维度为 [N, C, H, W] 沿着维度C进行操作, 所以dim=1, 结果为[N, H, W]
        MaxPool = torch.max(x, dim=1).values  # torch.max 返回的是索引和value， 要用.values去访问值才行！
        AvgPool = torch.mean(x, dim=1)

        # 增加维度, 变成 [N, 1, H, W]
        MaxPool = torch.unsqueeze(MaxPool, dim=1)
        AvgPool = torch.unsqueeze(AvgPool, dim=1)

        # 维度拼接 [N, 2, H, W]
        x_cat = torch.cat((MaxPool, AvgPool), dim=1)  # 获得特征图

        # 卷积操作得到空间注意力结果
        x_out = self.conv(x_cat)
        Ms = self.sigmoid(x_out)

        # 与原图通道进行乘积
        x = Ms * x

        return x

class ResidualBlock(nn.Module):
    def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1):
        super(ResidualBlock, self).__init__()

        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size, stride, padding)
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size, 1, padding)
        self.bn2 = nn.BatchNorm2d(out_channels)

        self.shortcut = nn.Sequential()
        if in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(out_channels)
            )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

class ConvNet(nn.Module):
    def __init__(self):
        super(ConvNet, self).__init__()
        self.res_block1 = ResidualBlock(1, 16, kernel_size=5, stride=2, padding=2)
        self.res_block2 = ResidualBlock(16, 32, kernel_size=5, stride=2, padding=2)

        # Add an additional CBAM layer
        self.cbam1 = CBAM(in_channel=32)

        self.res_block3 = ResidualBlock(32, 64, kernel_size=5, stride=2, padding=2)

        # Original CBAM layer
        self.cbam2 = CBAM(in_channel=64)

        self.flatten = nn.Flatten()
        self.fc = nn.Linear(64*17*32, 256)

    def forward(self, x):
        x = self.res_block1(x)
        x = self.res_block2(x)

        # Use the additional CBAM layer
        x = self.cbam1(x)

        x = self.res_block3(x)

        # Original CBAM
        x = self.cbam2(x)

        x = self.flatten(x)
        x = self.fc(x)
        x = x.unsqueeze(1)
        return x

class MyNetwork(nn.Module):
    def __init__(self):
        super(MyNetwork, self).__init__()
        self.conv_net = ConvNet()  # 使用一个共享的ConvNet

    def forward(self, x):
        outputs = []
        for i in range(20):
            channel_data = x[:, i:i+1, :, :]
            output = self.conv_net(channel_data)
            outputs.append(output.squeeze(1))

        return torch.stack(outputs, dim=1)

class BiLSTMCell(nn.Module):
    def __init__(self, input_size, hidden_size):
        super(BiLSTMCell, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.initialize_weights()  # 初始化权重

        # 定义前向 LSTM 的权重
        self.input_weights_i = nn.Linear(input_size, hidden_size)
        self.input_weights_f = nn.Linear(input_size, hidden_size)
        self.input_weights_o = nn.Linear(input_size, hidden_size)
        self.input_weights_s = nn.Linear(input_size, hidden_size)

        self.hidden_weights_i = nn.Linear(hidden_size, hidden_size)
        self.hidden_weights_f = nn.Linear(hidden_size, hidden_size)
        self.hidden_weights_o = nn.Linear(hidden_size, hidden_size)
        self.hidden_weights_s = nn.Linear(hidden_size, hidden_size)

        self.neighbor_weights_i = nn.Linear(4, hidden_size)
        self.neighbor_weights_f = nn.Linear(4, hidden_size)
        self.neighbor_weights_o = nn.Linear(4, hidden_size)
        self.neighbor_weights_s = nn.Linear(4, hidden_size)

    def initialize_weights(self):
        for name, param in self.named_parameters():
            if 'weight' in name:
                nn.init.xavier_uniform_(param.data)
            elif 'bias' in name:
                nn.init.zeros_(param.data)

    # ... 其余代码保持不变

    def forward(self, x, hidden, cell, neighbors):
        input_gate = torch.sigmoid(self.input_weights_i(x) + self.hidden_weights_i(hidden) + self.neighbor_weights_i(neighbors))
        forget_gate = torch.sigmoid(self.input_weights_f(x) + self.hidden_weights_f(hidden) + self.neighbor_weights_f(neighbors))
        output_gate = torch.sigmoid(self.input_weights_o(x) + self.hidden_weights_o(hidden) + self.neighbor_weights_o(neighbors))

        cell_candidate = torch.tanh(self.input_weights_s(x) + self.hidden_weights_s(hidden) + self.neighbor_weights_s(neighbors))
        next_cell = forget_gate * cell + input_gate * cell_candidate
        next_hidden = output_gate * torch.tanh(next_cell)

        return next_hidden, next_cell

class DCRNN(nn.Module):
    def __init__(self, input_size, hidden_size, rows, cols, num_neurons, device, my_network):
        super(DCRNN, self).__init__()
        # Code omitted for brevity
        self.my_network = my_network
        self.rows = rows
        self.cols = cols
        self.hidden_size = hidden_size
        self.device = device  # 添加设备属性

        self.cell_fwd = BiLSTMCell(input_size, hidden_size)
        self.cell_bwd = BiLSTMCell(input_size, hidden_size)

        self.FF = nn.Linear(200, num_neurons)
        self.output = nn.Linear(num_neurons, 2)
        self.activation = nn.Softmax(dim=1)
        self.initialize_weights()  # 初始化权重

    def initialize_weights(self):
        for name, param in self.named_parameters():
            if 'FF' in name or 'output' in name:
                if 'weight' in name:
                    nn.init.xavier_uniform_(param.data)
                elif 'bias' in name:
                    nn.init.zeros_(param.data)
    def forward(self, x):
        x = self.my_network(x)
        x = x.to(self.device)
        x = torch.transpose(x, 0, 1)
        ch, bs, seq_len = x.shape
        hidden, cell = self.init_hidden_cell(bs)

        H_fwd, H_bwd = [], []

        # 转换 hidden 和 cell 为 list
        hidden = [list(h) for h in hidden]
        cell = [list(c) for c in cell]

        # 正向传播
        for t in range(seq_len):
            last_hidden_states = []  # 用于存储最后一个时间步长的所有单元的 next_hidden 值

            for i in range(self.rows * self.cols):
                channel_idx = i
                x_t = x[channel_idx, :, t].unsqueeze(-1)
                neighbors = self.get_neighbor_hidden(hidden, i, bs, direction='forward')
                next_hidden, next_cell = self.cell_fwd(x_t, hidden[i][0], cell[i][0], neighbors)

                hidden_forward, hidden_backward = hidden[i]
                hidden_forward = next_hidden
                hidden[i] = (hidden_forward, hidden_backward)

                cell_forward, cell_backward = cell[i]
                cell_forward = next_cell
                cell[i] = (cell_forward, cell_backward)

                if t == seq_len - 1:
                    last_hidden_states.append(next_hidden)  # 当时间步长为最后一个时，将当前单元的 next_hidden 添加到 last_hidden_states 列表中

            if t == seq_len - 1:
                H_fwd = torch.stack(last_hidden_states, dim=1)  # 拼接 last_hidden_states 中的张量并创建 H_fwd

        # 反向传播
        for t in reversed(range(seq_len)):
            last_hidden_states = []  # 用于存储最后一个时间步长的所有单元的 next_hidden 值

            for i in range(self.rows * self.cols):
                channel_idx = i
                x_t = x[channel_idx, :, t].unsqueeze(-1)
                neighbors = self.get_neighbor_hidden(hidden, i, bs, direction='backward')
                next_hidden, next_cell = self.cell_bwd(x_t, hidden[i][1], cell[i][1], neighbors)

                hidden_forward, hidden_backward = hidden[i]
                hidden_backward = next_hidden
                hidden[i] = (hidden_forward, hidden_backward)

                cell_forward, cell_backward = cell[i]
                cell_backward = next_cell
                cell[i] = (cell_forward, cell_backward)

                if t == 0:  # 当时间步长为第一个（反向传播中的最后一个）时
                    last_hidden_states.append(next_hidden)

            if t == 0:
                H_bwd = torch.stack(last_hidden_states, dim=1)  # 拼接 last_hidden_states 中的张量并创建 H_bwd

        # 转换 hidden 和 cell 回 tuple
        hidden = [tuple(h) for h in hidden]
        cell = [tuple(c) for c in cell]

        # 将正向和反向隐藏状态合并
        H = torch.cat((H_fwd, H_bwd), dim=2)
        H = H.view(bs, -1)  # 将 H 的形状从 (32, 20, 10) 改为 (32, 200)

        # 接下来是前馈子网络和输出层的处理
        FF_out = torch.sigmoid(self.FF(H))
        output_out = self.activation(self.output(FF_out))
        return output_out

    def init_hidden_cell(self, batch_size):
        hidden_cell = [(
            (torch.randn(batch_size, self.hidden_size).to(self.device) * 0.01,
             torch.randn(batch_size, self.hidden_size).to(self.device) * 0.01),
            (torch.randn(batch_size, self.hidden_size).to(self.device) * 0.01,
             torch.randn(batch_size, self.hidden_size).to(self.device) * 0.01)
        ) for _ in range(self.rows * self.cols)]
        return list(zip(*[zip(*hc) for hc in hidden_cell]))

    def get_neighbor_hidden(self, hidden, index, batch_size, direction):
        row = index // self.cols
        col = index % self.cols

        if direction == 'forward':
            neighbor_indices = [
                (row - 1) * self.cols + col,
                (row + 1) * self.cols + col,
                row * self.cols + col - 1,
                row * self.cols + col + 1
            ]
        elif direction == 'backward':
            neighbor_indices = [
                (row - 1) * self.cols + col,
                (row + 1) * self.cols + col,
                row * self.cols + col + 1,
                row * self.cols + col - 1
            ]
        else:
            raise ValueError("Invalid direction")

        neighbors = []
        G = self.hidden_size

        for idx in neighbor_indices:
            if 0 <= idx < self.rows * self.cols:
                if direction == 'forward':
                        hidden_forward, hidden_backward = hidden[idx]  # 提取 hidden_forward 和 hidden_backward
                        # 提取每一行的第四列
                        neighbor_output = hidden_forward[:, 3].unsqueeze(1).unsqueeze(2)  # 这将创建一个形状为[32, 1, 1]的张量
                        # 接下来，您可以将 G_values 添加到您的数据结构中或进行其他操作。

                else:
                    hidden_forward, hidden_backward = hidden[idx]  # 提取 hidden_forward 和 hidden_backward
                    # 提取每一行的第四列
                    neighbor_output = hidden_backward[:, 3].unsqueeze(1).unsqueeze(2)  # 这将创建一个形状为[32, 1, 1]的张量
                    # 接下来，您可以将 G_values 添加到您的数据结构中或进行其他操作。
                neighbors.append(neighbor_output)
            else:
                neighbors.append(torch.zeros(batch_size, 1, 1).to(self.device))

        return torch.cat(neighbors, dim=-1).squeeze(1)

class CustomMSELoss(nn.Module):
    def __init__(self):
        super(CustomMSELoss, self).__init__()
        self.mse_loss = nn.MSELoss()

    def forward(self, y_pred, y_true):
        return self.mse_loss(y_pred, y_true) / 2

class MyDataset(Dataset):
    def __init__(self, file_paths, file_list):
        self.file_paths = file_paths
        self.file_list = file_list

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

    def __getitem__(self, idx):
        # 选择对应的文件路径
        file_path = self.file_paths[idx % len(self.file_paths)]
        X, y = self.load_data(os.path.join(file_path, self.file_list[idx]))
        X = X.view(-1, *X.shape[2:])
        y = y.view(-1, *y.shape[2:])
        return X, y

    @staticmethod
    def load_data(file_name):
        try:
            with open(file_name, 'rb') as f:
                X = pickle.load(f)
            y_file_name = file_name.replace('X', 'y')
            with open(y_file_name, 'rb') as f:
                y = pickle.load(f)
        except Exception as e:
            print(f"加载文件 {file_name} 时出错：{e}")
            X, y = None, None
        return torch.tensor(X), torch.tensor(y)

import os
import re
from torch.utils.data import DataLoader

# 假设你已经在某处定义了 MyDataset 类

import re
import os

def main():
    data_dirs = [

         '/root/autodl-tmp/batches5/',
        '/root/autodl-tmp/batches4/',
        '/root/autodl-tmp/batches3/',
        '/root/autodl-tmp/batches2/',
        '/root/autodl-tmp/batches1/'

    ]

    for i in range(len(data_dirs)):
        # 使用除当前索引 i 之外的所有数据目录作为训练和验证数据
        # 执行留一法交叉验证
        train_validation_dirs = data_dirs[:i] + data_dirs[i + 1:]
        # 使用索引 i 的数据目录作为测试数据
        test_dir = data_dirs[i]

        # 在每轮循环开始时，重置列表以收集每个文件夹的数据文件和标签文件的路径
        all_train_data_files = []
        all_train_label_files = []
        all_valid_data_files = []
        all_valid_label_files = []

        # 遍历训练和验证数据目录
        for data_dir in train_validation_dirs:
            # 列出目录中的所有文件
            files = os.listdir(data_dir)
            # 按数字对文件进行排序（这里假设文件名包含数字）
            files = sorted(files, key=lambda x: int(re.search(r'\d+', x).group()))

            # 在开始新的迭代时清空 data_files 和 label_files
            data_files = []
            label_files = []

            # 遍历目录中的文件
            for file_name in files:
                # 如果文件名以 “X_batch_” 开头，将其添加到数据文件列表中
                if file_name.startswith('X_batch_'):
                    data_files.append(os.path.join(data_dir, file_name))
                # 如果文件名以 “y_batch_” 开头，将其添加到标签文件列表中
                elif file_name.startswith('y_batch_'):
                    label_files.append(os.path.join(data_dir, file_name))

            # 区分 seizure 和 non-seizure 文件
            seizure_files = data_files[:1798]
            non_seizure_files = data_files[1798:]
            seizure_labels = label_files[:1798]
            non_seizure_labels = label_files[1798:]

            # 计算 seizure 类别的训练和验证分割点
            train_split_seizure = int(0.8 * len(seizure_files))
            # 将 seizure 文件分割为训练和验证集
            train_files_seizure = seizure_files[:train_split_seizure]
            valid_files_seizure = seizure_files[train_split_seizure:]
            train_labels_seizure = seizure_labels[:train_split_seizure]
            valid_labels_seizure = seizure_labels[train_split_seizure:]

            # 计算 non-seizure 类别的训练和验证分割点
            train_split_non_seizure = int(0.8 * len(non_seizure_files))
            # 将 non-seizure 文件分割为训练和验证集
            train_files_non_seizure = non_seizure_files[:train_split_non_seizure]
            valid_files_non_seizure = non_seizure_files[train_split_non_seizure:]
            train_labels_non_seizure = non_seizure_labels[:train_split_non_seizure]
            valid_labels_non_seizure = non_seizure_labels[train_split_non_seizure:]

            # 将当前文件夹的文件路径添加到总的训练和验证文件列表中
            all_train_data_files += train_files_seizure + train_files_non_seizure
            all_train_label_files += train_labels_seizure + train_labels_non_seizure
            all_valid_data_files += valid_files_seizure + valid_files_non_seizure
            all_valid_label_files += valid_labels_seizure + valid_labels_non_seizure

        # 在这里，你可以使用 all_train_data_files、all_train_label_files、all_valid_data_files、all_valid_label_files 和 test_dir 进行训练、验证和测试

        train_dataset = MyDataset(file_paths=data_dirs, file_list=all_train_data_files)
        validation_dataset = MyDataset(file_paths=data_dirs, file_list=all_valid_data_files)
        batch_size =160
        train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
        validation_loader = DataLoader(validation_dataset, batch_size=batch_size, shuffle=True)

    # validation_loader = ...
    # test_loader = ...
        # 获取测试集文件
        test_data_files = []
        test_label_files = []
        files = os.listdir(test_dir)
        files = sorted(files, key=lambda x: int(re.search(r'\d+', x).group()))

        # 分离数据文件和标签文件
        for file_name in files:
            if file_name.startswith('X_batch_'):
                test_data_files.append(os.path.join(test_dir, file_name))
            elif file_name.startswith('y_batch_'):
                test_label_files.append(os.path.join(test_dir, file_name))

        # 创建 DataLoader 用 all_train_data_files 和 all_train_label_files 作为训练集
        # 用 all_valid_data_files 和 all_valid_label_files 作为验证集
        # 用 test_data_files 和 test_label_files 作为测试集

        test_dataset = MyDataset(file_paths=[test_dir], file_list=test_data_files)
        test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True)

        # 初始化模型、损失函数和优化器
        input_size = 1
        hidden_size = 5
        rows = 4
        cols = 5
        num_neurons = 50
        epochs = 11
        learning_rate = 0.001

        my_network = MyNetwork()
        device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        model = DCRNN(input_size, hidden_size, rows, cols, num_neurons, device, my_network)
        model = model.to(device)
        loss_fn = CustomMSELoss().to(device)
        optimizer = optim.Adam(model.parameters(), lr=learning_rate)

        best_valid_loss = float('inf')

        # 初始化列表以存储指标
        # 初始化列表以存储指标
        def sensitivity_specificity(targets, predictions):
            tn, fp, fn, tp = confusion_matrix(targets, predictions).ravel()
            sensitivity = tp / (tp + fn)
            specificity = tn / (tn + fp)
            return sensitivity, specificity

        train_accuracies = []
        train_sensitivities = []
        train_specificities = []
        train_aucs = []

        valid_accuracies = []
        valid_sensitivities = []
        valid_specificities = []
        valid_aucs = []
        optimizer = optim.Adam(model.parameters(), lr=learning_rate)
        # 训练和验证
        for epoch in range(epochs):
            # 训练
            model.train()
            total_train_loss = 0
            total_batches = 0
            y_train_true = []
            y_train_pred = []
            y_train_scores = []
            for X_batch, y_batch in train_loader:
                X_batch, y_batch = X_batch.to(device), y_batch.to(device)
                optimizer.zero_grad()
                y_hat = model(X_batch)
                loss = loss_fn(y_hat, y_batch)
                loss.backward()
                optimizer.step()
                total_train_loss += loss.item()

                # 将预测的概率转换为类别标签
                y_hat_classes = torch.argmax(y_hat, dim=1)
                y_batch_classes = torch.argmax(y_batch, dim=1)

                y_train_true.extend(y_batch_classes.tolist())
                y_train_pred.extend(y_hat_classes.tolist())
                y_train_scores.extend(y_hat[:, 1].cpu().detach().numpy())

                total_batches += 1

            train_loss = total_train_loss / total_batches

            # 计算训练评估指标
            train_accuracy = accuracy_score(y_train_true, y_train_pred)
            train_sensitivity, train_specificity = sensitivity_specificity(y_train_true, y_train_pred)
            train_auc = roc_auc_score(y_train_true, y_train_scores)
            # 记录训练评估指标
            train_accuracies.append(train_accuracy)
            train_sensitivities.append(train_sensitivity)
            train_specificities.append(train_specificity)
            train_aucs.append(train_auc)
            # 验证
            model.eval()
            total_valid_loss = 0
            total_valid_batches = 0
            y_valid_true = []
            y_valid_pred = []
            y_valid_scores = []
            with torch.no_grad():
                for X_batch, y_batch in validation_loader:
                    X_batch, y_batch = X_batch.to(device), y_batch.to(device)
                    y_hat = model(X_batch)
                    loss = loss_fn(y_hat, y_batch)
                    total_valid_loss += loss.item()

                    # 将预测的概率转换为类别标签
                    y_hat_classes = torch.argmax(y_hat, dim=1)
                    y_batch_classes = torch.argmax(y_batch, dim=1)

                    y_valid_true.extend(y_batch_classes.tolist())
                    y_valid_pred.extend(y_hat_classes.tolist())
                    y_valid_scores.extend(y_hat[:, 1].cpu().numpy())

                    # 增加批次计数
                    total_valid_batches += 1

            # 计算平均验证损失
            valid_loss = total_valid_loss / total_valid_batches

            # 如果验证损失有所降低，保存模型
            if valid_loss < best_valid_loss:
                best_valid_loss = valid_loss
                torch.save(model.state_dict(), 'best_model.pt')

             # 计算验证评估指标
            valid_accuracy = accuracy_score(y_valid_true, y_valid_pred)
            valid_sensitivity, valid_specificity = sensitivity_specificity(y_valid_true, y_valid_pred)
            valid_auc = roc_auc_score(y_valid_true, y_valid_scores)

            # 记录验证评估指标
            valid_accuracies.append(valid_accuracy)
            valid_sensitivities.append(valid_sensitivity)
            valid_specificities.append(valid_specificity)
            valid_aucs.append(valid_auc)

            print(f"Epoch: {epoch + 1}, Train Loss: {train_loss}, Valid Loss: {valid_loss}")

            print(f"Train Accuracy: {train_accuracy}, Sensitivity: {train_sensitivity}, Specificity: {train_specificity}, AUC: {train_auc}")
            print(f"Valid Accuracy: {valid_accuracy}, Sensitivity: {valid_sensitivity}, Specificity: {valid_specificity}, AUC: {valid_auc}")

            # 在测试集上评估最佳模型
        model.load_state_dict(torch.load('best_model.pt'))

        # 测试
        model.eval()
        test_loss = 0
        y_test_true = []
        y_test_pred = []
        y_test_scores = []
        with torch.no_grad():
            for X_batch, y_batch in test_loader:
                X_batch, y_batch = X_batch.to(device), y_batch.to(device)
                y_hat = model(X_batch)
                loss = loss_fn(y_hat, y_batch)
                test_loss += loss.item()

                # 将预测的概率转换为类别标签
                y_hat_classes = torch.argmax(y_hat, dim=1)
                y_batch_classes = torch.argmax(y_batch, dim=1)

                y_test_true.extend(y_batch_classes.tolist())
                y_test_pred.extend(y_hat_classes.tolist())
                y_test_scores.extend(y_hat[:, 1].cpu().numpy())

            test_loss /= len(test_loader)

        # 计算测试评估指标
        test_accuracy = accuracy_score(y_test_true, y_test_pred)
        test_sensitivity, test_specificity = sensitivity_specificity(y_test_true, y_test_pred)
        test_auc = roc_auc_score(y_test_true, y_test_scores)

        print(f"Test Loss: {test_loss}")
        print(f"Test Accuracy: {test_accuracy}, Sensitivity: {test_sensitivity}, Specificity: {test_specificity}, AUC: {test_auc}")

if __name__ == "__main__":
    main()


import json
import time

from typing import Any

from gretel_client.projects.jobs import (
    ACTIVE_STATES,
    END_STATES,
    Job,
    RunnerMode,
    Status,
)
from gretel_client.projects.models import Model, read_model_config
from gretel_client.projects.projects import Project
from gretel_trainer.benchmark.core import BenchmarkException, log

def create_evaluate_model(
    project: Project,
    data_source: str,
    ref_data: str,
    run_identifier: str,
) -> Model:
    return project.create_model_obj(
        model_config=_make_evaluate_config(run_identifier),
        data_source=data_source,
        ref_data=ref_data,
    )

def run_evaluate(
    evaluate_model: Model,
    run_identifier: str,
    wait: int,
) -> dict[str, Any]:
    # Calling this in lieu of submit_cloud() is supposed to avoid
    # artifact upload. Doesn't work for more recent client versions!
    evaluate_model.submit(runner_mode=RunnerMode.CLOUD)
    job_status = await_job(run_identifier, evaluate_model, "evaluation", wait)
    if job_status in END_STATES and job_status != Status.COMPLETED:
        raise BenchmarkException("Evaluate failed")
    with evaluate_model.get_artifact_handle("report_json") as report:
        return json.loads(report.read())

def _make_evaluate_config(run_identifier: str) -> dict:
    config_dict = read_model_config("evaluate/default")
    config_dict["name"] = f"evaluate-{run_identifier}"
    return config_dict

def await_job(run_identifier: str, job: Job, task: str, wait: int) -> Status:
    failed_refresh_attempts = 0
    status = job.status
    while not _finished(status):
        if failed_refresh_attempts >= 5:
            return Status.LOST

        time.sleep(wait)

        status, failed_refresh_attempts = _cautiously_refresh_status(
            job, failed_refresh_attempts
        )
        _log_in_progress(run_identifier, status, task)
    return status

def _finished(status: Status) -> bool:
    return status in END_STATES

def _cautiously_refresh_status(job: Job, attempts: int) -> tuple[Status, int]:
    try:
        job.refresh()
        attempts = 0
    except:
        attempts = attempts + 1

    return (job.status, attempts)

def _log_in_progress(run_identifier: str, status: Status, task: str) -> None:
    if status in ACTIVE_STATES:
        log(run_identifier, f"{task} job still in progress (status: {status})")


import constraint

problem = constraint.Problem()

# mora izmedju 2500 i 3500 kalorija
# minimum 60g proteina
# kalcijum izmedju 500mg i 1000mg
# min para da ispuni sve ove uslove

problem.addVariable('ovseno', range(5))
problem.addVariable('piletina', range(4))
problem.addVariable('jaje', range(4))
problem.addVariable('mleko', range(9))
problem.addVariable('tiramisu', range(3))

def kalorije_ogranicenje(o,p,j,m,t):
    if(o*110 + p*205 + j*160 + m*160 + t*420 <= 3500 and o*110 + p*205 + j*160 + m*160 + t*420 >= 2500):
        return True
def proteini_minimum(o,p,j,m,t):
    if(o*4+p*32+j*13+m*8+t*4) >= 60:
        return True
def kalcijum_ogranicenje(o,p,j,m,t):
    if(o*2+p*12+j*285+m*22+t*22 >= 500 and o*2+p*12+j*285+m*22+t*22 <= 1000):
        return True
problem.addConstraint(kalorije_ogranicenje,['ovseno', 'piletina', 'jaje', 'mleko', 'tiramisu'])
problem.addConstraint(proteini_minimum, ['ovseno', 'piletina', 'jaje', 'mleko', 'tiramisu'])
problem.addConstraint(kalcijum_ogranicenje, ['ovseno', 'piletina', 'jaje', 'mleko', 'tiramisu'])

resenja = problem.getSolutions()
## 80 200 20 90 190
min_cena = 99999
kalorije = 0
proteini = 0
kalcijum = 0

for resenje in resenja:
    if(resenje['ovseno']*80 + resenje['piletina']*200+ resenje['jaje']*20 + resenje['mleko']*90 + resenje['tiramisu']*190) < min_cena:
        min_cena = resenje['ovseno']*80 + resenje['piletina']*200 + resenje['jaje']*20+resenje['mleko']*90 + resenje['tiramisu']*190
        kalorije = resenje['ovseno']*110 + resenje['piletina']*205 + resenje['jaje']*160+resenje['mleko']*160 + resenje['tiramisu']*420
        proteini = resenje['ovseno']*4 + resenje['piletina']*32 + resenje['jaje']*13+resenje['mleko']*8 + resenje['tiramisu']*4
        kalcijum = resenje['ovseno']*2 + resenje['piletina']*12 + resenje['jaje']*285+resenje['mleko']*22 + resenje['tiramisu']*22

print("Najbolja kombinacija: ", min_cena, " dinara, ",kalorije," kalorija")
print("Proteina: ", proteini, ", kalcijuma: ", kalcijum)


# 1
try:
    age = int(input("ჩაწერეთ თქვენი ასაკი: "))
    if 0 <= age <= 10:
        print("ბავშვი")
    elif 11 <= age <= 19:
        print("თინეიჯერი")
    elif 20 <= age <= 30:
        print("ახალგაზრდა")
    elif 31 <= age <= 50:
        print("ზრდასრული")
    elif 51 <= age <= 80:
        print("ასაკოვანი")
    elif 81 <= age <= 100:
        print("მხცოვანი")
    else:
        print("დენიკინის დროინდელი")
except:
    print("ასაკი ჩაწერეთ მთელ რიცხვებში")

# 2
def secondassignment(number):
    sum = 0
    numb = int(number)
    for i in range(1, numb):
        if numb % i == 0:
            sum += i
        if numb == sum:
            return numb

FILE = open('perfect number assignment', 'w')
inpu = int(input("ჩაწერეთ რიცხვი: "))
for i in range(1,inpu):
    if secondassignment(i):
        FILE.write(i + "\n")

# 3
list = ['Nika',"Gio","Daviti", "Elizabeti","Joni"]
list2 = []
def fun(list):
    s = sorted(list)
    print(s)

fun(list)