"""
This is the main Cryptocurrency class. It has a the following categories and
methods:
    1. CATEGORY: Meta-Parameters
        1.1. set_metaparams - sets the metaparameters for the chain
        1.2. get_latest_block_metaparams - gets the current block's
                                           metaparameters
    2. CATEGORY: Create Block
        2.1 create_block - converts the current TXPOOL into a block and adds it
                           to the chain
    3. CATEGORY: Address Specific
        3.1 get_balance_of_addr - gets the balance of an address
        3.2 get_transactions_of_addr - gets all the transactions of an address
    4. CATEGORY: Add Transaction
        4.1 add_transaction - adds a transaction to TXPOOL without validating
                              the transaction parameters
        4.2 validate_add_transaction - validates transaction parameters, then
                                       adds to TXPOOL
    5. CATEGORY: Blockchain Specific
        5.1 get_complete_chain - gets the entire chain excluding TXPOOL
        5.2 get_TXPOOL - gets TXPOOL
    6. CATEGORY: Load Chain
        6.1 load_chain - loads the saved blockchain
"""
from math import exp
from pathlib import Path
from copy import deepcopy
from json import dump, load
from hashlib import sha3_256

from Blockchain.Block import Block, time_utc

class Cryptocurrency:
    def __init__(self, **kwargs):
        """
        If `create_genesis` is present, `address` and `amount` must be present
        as well. If `create_genesis` is not present, this just initialises a
        Cryptocurrency instance with the metaparameters

        Keyword Arguments:
            create_genesis (Bool): To create genesis or not. Default: False
            address (str): The address to send the coins to when creating
                genesis. Default: GENESIS_ADDRESS
            amount (float): The amount of coins to generate when creating
                genesis. Default: 100000
            blocks_path (Path): The directory where each block file (JSON) is
                stored. Default: Blocks/
        """
        self.index = 0 # This is the index of the latest block in the chain
        self.chain = [] # This is a list of all the blocks (check add_block for
                        # the structure of a block)

        # These are related to TXPOOL, the pool of all current transactions
        # before they are mined into the blockchain
        self.transaction_number = 0
        self.TXPOOL = {} # (check add_transaction for the structure of a
                         # transaction)

        # Metaparameters (Read Docs/How-Coins-Are-Introduced)

        # These are fixed. If they are to be altered, they must be updated in
        # the set_metaparameters method
        self._difficulty = 2
        self._percentage_of_transactions_rewarded = 0.1
        # These are changed in the set_metaparameters method
        self._mining_fees = 1
        self._reward_for_transaction = 1.5

        self.blocks_path = Path(kwargs.get("blocks_path", "Blocks"))
        self.blocks_path.mkdir(parents= True, exist_ok= True)

        create_genesis = kwargs.get("create_genesis", None)

        if create_genesis:
            address = kwargs.get("address", "GENESIS_ADDRESS")
            amount = float(kwargs.get("amount", 100000))

            # This is the genesis
            self.add_transaction(
                sendr_addr= "Genesis",
                recvr_addr= address,
                value= amount,
                mining_fee= 0.0,
                timestamp= time_utc())

            # This transaction is now made into a block
            self.create_block(mined_block_hash= "0", nonce= "0")

    ############################################################################
    ############################# META-PARAMETERS ##############################
    ############################################################################
    def set_metaparams(self):
        """
        Sets the metaparameters for the next TXPOOL of the chain right *before*
        a block is created. More of explanation can be found at
        Docs/How-Coins-Are-Introduced

        NOTE: The amount of time the TXPOOL accepts transactions before
        converting into a block is called T. For the sake of simplicity, this is
        "manually" mined by an API call. If this Cryptocurrency instance were to
        mine the block by itself, it would have to spawn a thread that mines the
        block every T units of time. This can be set up with asyncio or simple
        threading.

        Definitions:
            R: number of transactions in the previous block
            Mu: Mining Fees
            Tau: Reward for Rewarded-Transaction
            Rho: percentage of transactions rewarded (fixed = 0.1 i.e., 10%)

        Calculations:
            Mu = {
                    1                   | R <= 10
                    (e^(R-10)) / 10     | R > 10
            }

            Tau = 1.5 * Mu
            Tau = {
                    1.5                 | R <= 10
                    1.5*(e^(R-10) / 10) | R > 10
            }

        Returns:
            tuple:
                float: Mining Fees
                float: Reward for a Transaction
        """
        # Since this is called BEFORE the TXPOOL is reset,
        # self.transaction_number indicates the number of transactions in the
        # TXPOOL that is going to be added as a block to the chain; this is the
        # metaparameter R for the next TXPOOL.
        R = self.transaction_number

        print (R)

        Mu = None
        Tau = None

        if R <= 10:
            Mu = 1
        else:
            Mu = exp((R - 10)/10)

        Tau = 1.5 * Mu

        self._mining_fees = Mu
        self._reward_for_transaction = Tau

        print (self._mining_fees, self._reward_for_transaction)

        return (Mu, Tau)

    def get_latest_block_metaparams(self):
        """
        Get a dictionary of meta parameters about this block

        Returns:
            dict:
                ```
                {
                    "index"              : Index of the current block that is
                                           going to be created
                    "TXPOOL"             : All the transactions in the TXPOOL.
                                           Refer to `add_transaction()` to see
                                           the structure of each transaction
                    "prev_hash"          : The hash of the previous block
                    "difficulty"         : The difficulty of this block
                    "mining_fees"        : The mining fees of this block
                    "num_tx_TXPOOL"      : The number of transactions in the
                                           TXPOOL
                    "reward_for_tx"      : The reward for a transaction, if
                                           rewarded
                    "percent_tx_rewarded": The percentage of transactions that
                                           are rewarded
                }
                ```
        """
        meta_params = {
            "index" : self.index,
            "TXPOOL" : self.TXPOOL,
            "prev_hash" : self.chain[-1].block_hash,
            "difficulty" : self._difficulty,
            "mining_fees" : self._mining_fees,
            "num_tx_TXPOOL": self.transaction_number,
            "reward_for_tx" : self._reward_for_transaction,
            "percent_tx_rewarded" : self._percentage_of_transactions_rewarded
        }

        return meta_params

    ############################################################################
    ############################### CREATE BLOCK ###############################
    ############################################################################
    def create_block(self, mined_block_hash, nonce):
        """
        Creates a block on the blockchain (in self.chain) and writes a block
        file (JSON) to self.blocks_path.

        The format of each block is:
        ```
        {
            "block_index": index of this block,
            "previous_block_hash": the previous block's hash,
            "difficulty": the difficulty of this block,
            "nonce": the nonce used by the miner to meet difficulty,
            "mined_block_hash": the hash submitted by the miner,
            "percent_tx_rewarded": percentage of the transactions rewarded,
            "mining_fee": mining fee for all the transactions of this block,
            "reward_for_tx": reward for the rewarded transaction,
            "transactions": see `add_transaction()`,
            "timestamp": timestamp at which the block is added to the chain
        }
        ```

        Args:
            mined_block_hash (str): the hash that is submitted by the miner
            nonce (str): the nonce used by the miner to meet difficulty

        Returns:
            `block`: The newly generated block
        """

        if self.index != 0:
            previous_hash = self.chain[-1].block_hash
        else:
            previous_hash = 0

        block = Block(
            block_index = self.index,
            mined_block_hash = mined_block_hash,
            previous_block_hash = previous_hash,
            nonce = nonce,
            difficulty = self._difficulty,
            transactions = self.TXPOOL,
        )

        file_name = str(self.index) + ".json"
        output_json = self.blocks_path / Path(file_name)

        block_dict = {
            "block_index" : self.index,
            "previous_block_hash" : previous_hash,
            "difficulty" : self._difficulty,
            "nonce" : nonce,
            "mined_block_hash" : mined_block_hash,
            "percent_tx_rewarded" : self._percentage_of_transactions_rewarded,
            "mining_fee" : self._mining_fees,
            "reward_for_tx" : self._reward_for_transaction,
            "transactions" : self.TXPOOL,
            "timestamp" : block.timestamp
        }

        with open(output_json, 'w') as F:
            dump(block_dict, F, indent = 2)

        self.set_metaparams()

        self.index += 1

        # Reset TXPOOL
        self.TXPOOL = {}
        self.transaction_number = 0

        # Adding the block to the chain
        self.chain.append(block)

        return block

    ############################################################################
    ############################ ADDRESS SPECIFIC ##############################
    ############################################################################
    def get_balance_of_addr(self, address):
        """
        As is described in `validate_add_transaction()`, although most existing
        implementations involve Merkle Trees, this just does a manual search
        through all the transactions. There is little reason to believe that
        this cryptocurrency would ever cross a thousand transactions; for that
        reason alone I have not implemented Merkle Trees.

        Args:
            address (str): Address whose balance needs to be calculated

        Returns:
            tuple:
                Bool: The existence of the address
                float: Wallet balance (is 0.0 if address does not exist)
        """
        address = address.lower()
        flag_wallet_exists = False
        wallet_balance = 0.0

        # Go through blocks to check if address exists
        for block in self.chain:
            for transaction in block.transactions:

                # The address sent money, deduct value (amount + mining fees)
                if block.transactions[transaction]["sendr_addr"] == address:
                    wallet_balance -=                                          \
                        block.transactions[transaction]["value"]
                    flag_wallet_exists = True

                # The address received money, add amount (only amount)
                if block.transactions[transaction]["recvr_addr"] == address:
                    wallet_balance +=                                          \
                        block.transactions[transaction]["final_value"]
                    flag_wallet_exists = True

        # Go through transactions in TXPOOL if address exists
        for tx in self.TXPOOL:
             # The address sent money, deduct value (amount + mining fees)
            if self.TXPOOL[tx]["sendr_addr"] == address:
                wallet_balance -= self.TXPOOL[tx]["value"]
                flag_wallet_exists = True

            # The address received money, add amount (only amount)
            if self.TXPOOL[tx]["recvr_addr"] == address:
                wallet_balance += self.TXPOOL[tx]["final_value"]
                flag_wallet_exists = True

        return (flag_wallet_exists, wallet_balance)

    def get_transactions_of_addr(self, address):
        """
        Get all the transactions that an address is associated with address
        (including the transactions in TXPOOL)

        Args:
            address (str): Address whose transactions need to be found

        Returns:
            tuple:
                Bool : does the address exist
                dict : (a dictionary of dictionaries)
                    ```
                    {
                        "transaction_number" :
                        {
                            "type"        : "send" or "receive"
                            "in_txpool"   : True or False (is it in TXPOOL?)
                            "sendr_addr"  : sender address
                            "recvr_addr"  : receiver address
                            "value"       : total amount transacted
                            "mining_fee"  : mining fee
                            "final_value" : value - mining_fee
                            "skscript"    : signed message
                            "timestamp"   : timestamp
                        },
                        "transaction_number" : ...,
                        "transaction_number" : ...
                    }
                    ```
        """
        flag_address_exists = False
        transactions = {}
        tx_index = 0

        # Go through the blocks in the chain to check if any transactions
        # to/from this address were made
        for block in self.chain:
            for transaction in block.transactions:

                # The address sent money
                if block.transactions[transaction]["sendr_addr"] == address:
                    tx = deepcopy(block.transactions[transaction])
                    tx["type"] = "send"
                    tx["in_txpool"] = False

                    transactions[str(tx_index)] = tx
                    tx_index += 1

                    flag_address_exists = True

                # The address received money
                if block.transactions[transaction]["recvr_addr"] == address:
                    tx = deepcopy(block.transactions[transaction])
                    tx["type"] = "receive"
                    tx["in_txpool"] = False

                    transactions[str(tx_index)] = tx
                    tx_index += 1

                    flag_address_exists = True

        # Go through transactions in TXPOOL if any transactions to/from this
        # address were made
        for transaction in self.TXPOOL:

             # The address sent money
            if self.TXPOOL[transaction]["sendr_addr"] == address:
                tx = deepcopy(self.TXPOOL[transaction])
                tx["type"] = "send"
                tx["in_txpool"] = True

                transactions[str(tx_index)] = tx
                tx_index += 1

                flag_address_exists = True

            # The address received money
            if self.TXPOOL[transaction]["recvr_addr"] == address:
                tx = deepcopy(self.TXPOOL[transaction])
                tx["type"] = "receive"
                tx["in_txpool"] = True

                transactions[str(tx_index)] = tx
                tx_index += 1

                flag_address_exists = True

        return (flag_address_exists, transactions)

    ############################################################################
    ############################# ADD TRANSACTION ##############################
    ############################################################################
    def add_transaction(self, sendr_addr, recvr_addr, value, mining_fee,       \
        timestamp, skscript = None):
        """
        This is to be called right after `validate_add_transaction()`. This does
        not have any checks in bound and hence must be only called after a
        transaction parameters have been validated OR when the chain decides to
        create a genesis / pay mining fees / reward a transaction.

        Args:
            sendr_addr (str): sending address
            recvr_addr (str): receiving address
            value (int): amount being transferred
            mining_fee (float): mining fee for this transaction
            timestamp (str): timestamp of the transaction being first received
            skscript (str): signed script authorizing transaction

        NOTE:
            In the case of the chain creating a genesis / paying mining fee /
            rewarding a transaction, `skscript` is set to be the sender's
            address. For this, no argument has to be passed.

        Returns:
            Bool: Always returns `True` when successful
        """
        transaction = {
            "sendr_addr" : sendr_addr.lower(),
            "recvr_addr" : recvr_addr.lower(),
            "value" : value,
            "mining_fee" : mining_fee,
            "final_value" : value - mining_fee,
            "skscript" : skscript or sendr_addr,
            "timestamp" : timestamp
        }

        transaction["txhash"] = sha3_256(
            str(transaction).encode("utf-8")
        ).hexdigest()

        transaction_index = str(self.transaction_number)
        self.TXPOOL[transaction_index] = transaction
        self.transaction_number += 1

        return True

    def validate_add_transaction(self, incoming_tx, mining_fees):
        """
        Validates the transaction that is to be added and then adds the
        transaction to the blockchain.

        NOTE:
            This is actually supposed to be solved by Merkle Trees and the sorts
            and those are good for scaling up. As such, I do not believe that
            this (private) cryptocurrency will even have more than a thousand
            transactions. Thus, I believe that iterating through the chain is an
            okay solution for now. If this is ever updated to use merkle trees,
            *MAKE SURE* to update the `Block` class and almost every class
            method in this class.

            For reference: Ethereum has three merkle trees: state, transaction
            and receipt.
            (https://blog.ethereum.org/2015/11/15/merkling-in-ethereum/)

            Likewise, each block has to be updated too in order for it to comply
            with the new definition that is implemented. In other words, make
            sure to update each block stored as blocks.json before shifting over
            to a new spec of the chain.

        Args:
            incoming_tx (dict): the incoming transaction:
                ```
                {
                    "sendr_addr" (str) : sender address
                    "recvr_addr" (str) : receiver address
                    "value" (float)    : amount transferred (not including
                                         mining fees)
                    "skscript" (str)   : signature of transaction
                }
                ```

            mining_fees (float): mining fees that is required by the network to
                                 process this transaction

        Returns:
            (int):
                 1 : Transaction Successful
                -1 : Transaction Failed - Sender's address does not have enough
                                          funds to transfer value + mining fees
                -2 : Transaction Failed - Sender's address does not exist
                                          (subset of returning -1)
        """
        sendr_addr = incoming_tx["sendr_addr"].lower()
        recvr_addr = incoming_tx["recvr_addr"].lower()
        value = float(incoming_tx["value"])

        flag_sender_exists, sendr_wallet_balance = self.get_balance_of_addr(sendr_addr)

        # Sender does not exist
        if not flag_sender_exists:
            return -2

        # Sender does not have enough balance to send moneys
        if sendr_wallet_balance < (value + mining_fees):
            return -1

        self.add_transaction(sendr_addr, recvr_addr, value, mining_fees,
            time_utc(), skscript = incoming_tx["skscript"])

        return 1

    ############################################################################
    ########################### BLOCKCHAIN SPECIFIC ############################
    ############################################################################
    def get_complete_chain(self):
        """
        Get the entire blockchain. This does not include TXPOOL. Look at
        `get_txpool()` for getting TXPOOL

        Returns:
            dict: (a dictionary of dictionaries):
            ```
            {
                "block_index" :
                {
                    "block_index"         : block index
                    "mined_block_hash"    : the (mined) hash of this block. This
                                            is *NOT* equal to Block.block_hash
                    "previous_block_hash" : hash of the previous block
                    "nonce"               : nonce used to make hash meet
                                            difficulty
                    "difficulty"          : difficulty at the time the block was
                                            mined
                    "percent_tx_rewarded" : percentage of transactions rewarded
                    "mining_fee"          : mining fee of the block
                    "reward_for_tx"       : reward for rewarded transaction
                    "transactions"        :
                    {
                        "transaction_index" :
                        {
                            "sendr_addr"  : sender address
                            "recvr_addr"  : receiver address
                            "value"       : amount transacted
                            "mining_fee"  : mining fee for the transaction
                            "final_value" : value - mining_fee
                            "skscript"    : signed message from sender
                            "timestamp"   : timestamp of the transaction
                        },
                        "transaction_index" : ...,
                        "transaction_index" : ...,
                    }
                    "timestamp"           : timestamp the block was joined into
                                            the chain
                },
                "block_index" : ...,
                "block_index" : ...,
            }
            ```
        """
        complete_chain = {}
        block_files = [f for f in self.blocks_path.rglob("*.json")]

        for block_index, block_file in enumerate(block_files):
            with open(Path(block_file), 'r') as F:
                block_dict = load(F)
                complete_chain[str(block_index)] = block_dict

        return complete_chain

    def get_TXPOOL(self):
        """
        Gets all the transactions in the TXPOOL

        Returns:
            dict: (a dictionary of dictionaries)
                ```
                {
                    "transaction_number" :
                    {
                        "sendr_addr"  : sender address
                        "recvr_addr"  : receiver address
                        "value"       : value (total amount transacted)
                        "mining_fee"  : mining fee
                        "final_value" : value - mining_fee
                        "skscript"    : signed message
                        "timestamp"   : timestamp
                    }
                }
                ```
        """
        return self.TXPOOL

    ############################################################################
    ############################### LOAD CHAIN #################################
    ############################################################################
    def load_chain(self):
        """
        Go through `self.blocks_path` and read all the json block files and
        store them in chain.
        """
        # This assumes block_files has only *.json and nothing else
        # It can be made more rigourous, but eh.
        block_files = [f for f in self.blocks_path.rglob("*.json")]

        self.index = len(block_files)

        for block_file in block_files:
            with open(block_file, 'r') as F:
                block_dict = load(F)

                block = Block(
                    block_index = block_dict["block_index"],
                    mined_block_hash = block_dict["mined_block_hash"],
                    previous_block_hash = block_dict["previous_block_hash"],
                    nonce = block_dict["nonce"],
                    difficulty = block_dict["difficulty"],
                    transactions = block_dict["transactions"],
                    timestamp = block_dict["timestamp"]
                )

                self.chain.append(block)


import json
import re
from tqdm import tqdm

from textblob import Word
from typing import List
import pandas as pd
import numpy as np

import multiprocessing; cores = multiprocessing.cpu_count() # Count the number of cores in a computer

# Get business category:
def get_cat(biz_cat, yelp_cat):
    i = 0
    while (i < len(biz_cat)):
        for cat in biz_cat:
            if cat in yelp_cat:
                return cat, yelp_cat.index(cat)
        break
    else:
        return (-1, "not in list")

# Count number of words in review
def review_len(review_str):
    '''
    Count number of words in review
    '''
    res = len(review_str.split())
    return res

# Split at capitalize words and add underscore
def convert_category(cat: List):
    arr = []
    for i in cat:
        i = i.lower()
        i = re.sub(r"'s","", i)#remove 's
        # i = Word(i).singularize() #singularize plurals
        i = re.sub(r"[^a-zA-Z0-9]"," ", i) #remove all other special characters and lowercase
        i = Word(i).singularize() #singularize plurals
        i = "_".join(i.split()) #join tokens with underscore
        arr.append(i)
    return arr

# Decontract text
def decontracted(phrase):
    # specific
    phrase = re.sub(r"won't", "will not", str(phrase))
    phrase = re.sub(r"can\'t", "can not", str(phrase))

    # general
    phrase = re.sub(r"n\'t", " not", str(phrase))
    phrase = re.sub(r"\'re", " are", str(phrase))
    phrase = re.sub(r"\'s", " is", str(phrase))
    phrase = re.sub(r"\'d", " would", str(phrase))
    phrase = re.sub(r"\'ll", " will", str(phrase))
    phrase = re.sub(r"\'t", " not", str(phrase))
    phrase = re.sub(r"\'ve", " have", str(phrase))
    phrase = re.sub(r"\'m", " am", str(phrase))
    return phrase

# Preprocess text
def preprocess(text_column):
    my_list = []
    # tqdm is for printing the status bar
    for sentance in tqdm(text_column.values):
        sent = decontracted(sentance)
        sent = sent.replace('\\r', ' ')
        sent = sent.replace('\\"', ' ')
        sent = sent.replace('\\n', ' ')
        sent = sent.replace('www', '')
        sent = sent.replace('https', '')
        sent = sent.replace('http', '')
        sent = re.sub('[^A-Za-z0-9]+', ' ', sent)
        # https://gist.github.com/sebleier/554280
        sent = ' '.join(e.lower() for e in sent.split())
        my_list.append(sent.lower().strip())

    return my_list


from typing import List

def reverse(s: str) -> str:
    """
    Reverse a string.
    - Time Complexity: O(len(s))
    - Space Complexity: O(len(s))

    :param s: a string
    :return: a reversed string
    """
    length = len(s)
    rlist: List[str] = [''] * length

    for i in range(length):
        rlist[length - i - 1] = s[i]

    return ''.join(rlist)

if __name__ == '__main__':
    print(reverse('hello'))
    print(reverse(''))
    print(reverse('X'))


'''
Given a positive integer n, return the punishment number of n.

The punishment number of n is defined as the sum of the squares of all integers i such that:

1 <= i <= n
The decimal representation of i * i can be partitioned into contiguous substrings such that the sum of the integer values of these substrings equals i.

Example 1:

Input: n = 10
Output: 182
Explanation: There are exactly 3 integers i that satisfy the conditions in the statement:
- 1 since 1 * 1 = 1
- 9 since 9 * 9 = 81 and 81 can be partitioned into 8 + 1.
- 10 since 10 * 10 = 100 and 100 can be partitioned into 10 + 0.
Hence, the punishment number of 10 is 1 + 81 + 100 = 182
Example 2:

Input: n = 37
Output: 1478
Explanation: There are exactly 4 integers i that satisfy the conditions in the statement:
- 1 since 1 * 1 = 1.
- 9 since 9 * 9 = 81 and 81 can be partitioned into 8 + 1.
- 10 since 10 * 10 = 100 and 100 can be partitioned into 10 + 0.
- 36 since 36 * 36 = 1296 and 1296 can be partitioned into 1 + 29 + 6.
Hence, the punishment number of 37 is 1 + 81 + 100 + 1296 = 1478

Constraints:

1 <= n <= 1000
'''
import unittest

from typing import *

class Solution:
    def punishmentNumber(self, n: int) -> int:
        def dfs(index, s, max_len, total):
            if total < 0 or index > len(s):
                return False
            if index == len(s) and total == 0:
                return True

            for i in range(1, max_len + 1):
                if index + i > len(s):
                    continue
                if dfs(index + i, s, max_len, total - int(s[index:index+i])):
                    return True
            return False

        def is_valid(x):
            max_len = len(str(x))
            z = str(x * x)
            return dfs(0, z, max_len, x)

        res = 0
        for i in range(n + 1):
            if is_valid(i):
                res += i * i
        return res

class TestSolution(unittest.TestCase):

    def test_case(self):
        examples = (
            ((37,),1478),
            ((10,),182),
        )
        for first, second in examples:
            self.assert_function(first, second)

    def assert_function(self, first, second):
        self.assertEqual(Solution().punishmentNumber(*first), second,
                         msg="first: {}; second: {}".format(first, second))

unittest.main()

Leetcode/2698. Find the Punishment Number of an Integer.py


# Split your dataset from the PCA pre-class work into 80% training data and 20% testing data.
import Data_prep_for_LDA
from sklearn.model_selection import train_test_split
from sklearn.svm import LinearSVC

def prepare_data():
    X_train, X_test, y_train, y_test = train_test_split(Data_prep_for_LDA.mini_main()[0],
                                                        Data_prep_for_LDA.mini_main()[1],
                                                        test_size=0.2)
    return X_train, X_test, y_train, y_test

"""
Build a simple linear classifier using the original pixel data. There are several options that you can try including
a linear SVC (http://scikit-learn.org/stable/modules/generated/sklearn.svm.LinearSVC.html#examples-using-sklearn-svm-linearsvc)
"""

def linear_SVC_clf(X=prepare_data()[0], y=prepare_data()[1]):
    clf = LinearSVC(random_state=0)
    clf.fit(X, y)
    LinearSVC(C=1.0, class_weight=None, dual=True, fit_intercept=True,
              intercept_scaling=1, loss='squared_hinge', max_iter=1000,
              multi_class='ovr', penalty='l2', random_state=0, tol=0.0001,
              verbose=0)
    return clf

"""
or
a logistic classifier (http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html#examples-using-sklearn-linear-model-logisticregression)
both of which will be covered in more detail later in this course.
What is your error rate on the training data? What is your error rate on your testing data?

Train the same linear model as in question 1, but now on the reduced representation that you created using PCA.
What is your error rate on the training data? What is your error rate on your testing data?
Train the same linear model as in question 1, but now on the reduced representation that you created using LDA.
What is your error rate on the training data? What is your error rate on your testing data?

Write three paragraphs, describing and interpreting your results from questions 1, 2, and 3.
Make a recommendation on which classifier you would prefer, and why.
"""


import logging
import os

from dotenv import load_dotenv
from google_methods.get_intent import get_intent
from telegram import Update, ForceReply
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackContext

logger = logging.getLogger(__name__)

def start(update: Update, context: CallbackContext) -> None:
    user = update.effective_user
    update.message.reply_markdown_v2(fr'Здравствуйте {user.mention_markdown_v2()}\!', reply_markup=ForceReply(selective=True))

def send_through_dialog_flow(update: Update, context: CallbackContext) -> None:
    response = get_intent(project_id=context.bot_data['project_id'], session_id=update.effective_user.id, msg=update.message.text, language_code='en-EN')
    update.message.reply_text(response.fulfillment_text)

def main() -> None:
    load_dotenv()
    logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)

    updater = Updater(token=os.getenv('TG_BOT_TOKEN'))

    dispatcher = updater.dispatcher

    project_id = os.getenv('PROJECT_ID')
    dispatcher.bot_data = {
        'project_id': project_id,
    }

    dispatcher.add_handler(CommandHandler('start', start))
    dispatcher.add_handler(MessageHandler(Filters.text & ~Filters.command, send_through_dialog_flow))

    updater.start_polling()

    updater.idle()

if __name__ == '__main__':
    main()


#!/usr/bin/python3
"""Module state fetch all"""

from sqlalchemy import create_engine
from model_state import State, Base
from sqlalchemy.orm import sessionmaker
import sys

if __name__ == "__main__":
    # Create engine that connects to the core (MySQL)
    engine = create_engine(
        'mysql+mysqldb://{}:{}@localhost/{}'.
        format(sys.argv[1], sys.argv[2], sys.argv[3]),
        pool_pre_ping=True
        )
    # Creates metadata and table schemes
    Base.metadata.create_all(engine)
    # Get the class of session and bind it with the engine
    Session = sessionmaker(bind=engine)
    # Instantiate the session
    session = Session()
    # The session object manages all the transactions
    result = session.query(State).order_by(State.id).all()

    for state in result:
        print("{}: {}".format(state.id, state.name))


from bitpacker import BitPacker

def extract_binary():

    file_name = "photo_set.bin"

    res = None
    with open(file_name, "rb") as f:
        bit_packer = BitPacker(f)

        res = bit_packer.extract(41 * 2)

    return res


# coding: utf-8

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import random
import seaborn as sns
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from hyperopt import fmin, tpe, hp, STATUS_OK, Trials,rand
from time import time

# Reading the data
app_data = pd.read_csv('University project/AppleStore.csv',index_col=0)
app_description = pd.read_csv('University project/appleStore_description.csv')

app_description.head()
#  to see if there is any null data in the dataset and what type of attribute each one it is
app_data.info()
app_description.info()

#  Merging datasets
app_data = pd.merge(app_data,app_description, on=['size_bytes','id','track_name'])
app_data.head()

def random_color_generator(number_of_colors):
# generator of random colors
    color = ["#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])
                 for i in range(number_of_colors)]
    return color
def make_plot(x_variable,y_variable):
# creates plots taking the x and y variable as parameters
    y_axes = app_data[[x_variable, y_variable]].groupby(x_variable).mean()[y_variable].sort_values(ascending=False)
    ind = np.arange(len(y_axes))
    x_axes = y_axes.index
    width = 0.8 # the width of the bars
    fig, ax = plt.subplots()
    fig = matplotlib.pyplot.gcf()
    fig.set_size_inches(18.5, 10.5)
    rects = ax.bar(ind - width/2, y_axes, width, color=random_color_generator(100),align="center")
    ax.set_ylabel(y_variable)
    ax.set_title(x_variable)
    ax.set_xticks(ind)
    ax.set_xticklabels(x_axes,rotation=40, ha ='right')
    plt.show()

count_types = app_data['prime_genre'].value_counts()
ind = np.arange(len(count_types))
print(count_types)

columns = app_data.columns.values.tolist()
# Countplot for each type of Prime_genre
for i in range(len(columns)):
    print(columns[i])
    print(type(app_data[columns[i]].head(1).values[0]))
    if(isinstance(app_data[columns[i]].head(1).values[0],(np.integer,np.float))):
        print(i)
        make_plot('prime_genre',columns[i])

width = 0.8
genre_label= app_data['prime_genre'].unique()

fig, ax = plt.subplots()
fig = matplotlib.pyplot.gcf()
fig.set_size_inches(18.5, 10.5)
rects1 = ax.bar(ind - width/2, count_types, width, color=random_color_generator(100))

# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Count')
ax.set_title('Prime genre')
ax.set_xticks(ind)
ax.set_xticklabels(genre_label,rotation=40,ha = 'right')

plt.show()

# Correlation between the user rating and the other features
app_data_new = app_data.drop(['id','currency','track_name'],axis=1)

app_data_new['app_desc_len'] = app_data_new['app_desc'].str.len()
app_data_new1 = app_data_new.drop(['app_desc','prime_genre'],axis = 1)
app_data_new1.head()
fig = matplotlib.pyplot.gcf()
fig.set_size_inches(18.5, 10.5)
sns.heatmap(app_data_new1.corr(),annot=True, fmt=".2f")

# dropping some of the of the attributes that are not usefull
app_data_new2 = app_data_new1.drop(['rating_count_ver','user_rating_ver','sup_devices.num','ver','cont_rating'],axis=1)
app_data_new2.head()

# spliting user_rating into to classes

target = app_data_new2['user_rating']
bins = [-1,4,6]
classes = [0,1]
d = dict(enumerate(classes, 1))

target = pd.cut(target, bins, labels=classes)
print(target.value_counts())

# dropping some other attributes
app_data_new2 = app_data_new2.drop(['user_rating','price','size_bytes','vpp_lic'],axis=1)
# normalizing the remaining attributes
sc_X = StandardScaler()
X_train = sc_X.fit_transform(app_data_new2)
print(X_train)
X_train.shape

from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score, cross_validate
from sklearn.model_selection import KFold
from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from lightgbm import LGBMClassifier
from sklearn.metrics import accuracy_score
from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, classification_report, confusion_matrix
import warnings

clf_rf = RandomForestClassifier()
clf_lgbm = LGBMClassifier()
clf_mlpc = MLPClassifier()

models = [clf_rf,
          clf_lgbm,
          clf_mlpc
          ]

def hyperopt_train_test(params):
# this function checks the parameters and finds out which classifier fits them, then returns the cross validation results
    t = params['type']
    del params['type']
    if  t=='randomforest':
        clf = RandomForestClassifier(**params)
    elif  t=='lgbm':
        clf = LGBMClassifier(**params)
    elif  t=='mlp':
        clf = MLPClassifier(**params)
    else:
        return 0
    return cross_val_score(clf, X_train, target).mean()

space_rf = hp.choice('classifier_type', [
    {
        'type': 'randomforest',
        'max_depth': hp.choice('max_depth', range(1,20)),
        'max_features': hp.choice('max_features', range(1,5)),
        'n_estimators': hp.choice('n_estimators', range(1,20)),
        'criterion': hp.choice('criterion', ["gini", "entropy"])
    }])
space_lgbm = hp.choice('classifier_type', [
    {
        'type': 'lgbm',
        'num_leaves': hp.choice('num_leaves', np.arange(10, 200, dtype=int)),
        'min_data_in_leaf':  hp.choice ('min_data_in_leaf',np.arange(10, 200, dtype=int)),
        'feature_fraction': hp.uniform('feature_fraction', 0.75, 1.0),
        'learning_rate': hp.loguniform('learning_rate', -5.0, -2.3),
        'min_sum_hessian_in_leaf': hp.loguniform('min_sum_hessian_in_leaf', 0, 2.3),
        'max_bin': hp.choice ('max_bin', np.arange(64, 512, dtype=int)),
        'bagging_freq': hp.choice ('bagging_freq', np.arange(1, 5, dtype=int)),
        'lambda_l1': hp.uniform('lambda_l1', 0, 10 ),
        'lambda_l2': hp.uniform('lambda_l2', 0, 10 ),
    }])
space_mlp = hp.choice('classifier_type', [
    {
        'type' : 'mlp',
        'hidden_layer_sizes' : 10 + hp.randint('hidden_layer_sizes', 40),
        'alpha' : hp.loguniform('alpha', -8*np.log(10), 3*np.log(10)),
        'activation' : hp.choice('activation', ['relu', 'logistic', 'tanh']),
        'solver': hp.choice('solver', ['lbfgs', 'sgd', 'adam'])
    }])
space = [space_rf,space_lgbm,space_mlp]
best=0
def f(params):
# objective function to minimize
    global best
    acc = hyperopt_train_test(params.copy())
    if acc > best :
        print ('new best:', acc, 'using', params['type'])
        best = acc
    return {'loss':1-best,'status': STATUS_OK}

besty=[]
for i in range(len(models)):
# using hypeopt this funciton finds the best hyperparaters for the classifiers given
    warnings.filterwarnings(action='ignore', category=DeprecationWarning)
    besty.append(fmin(f,space[i],algo= tpe.suggest,return_argmin=False,max_evals=100, trials=Trials()))

from hyperopt import space_eval
best_params = besty
best_models = []
for i in range(len(models)):
# assigning the best hyperparameters for the models
    if(best_params[i]['type']=="randomforest"):
        del best_params[i]['type']
        best_models.append(RandomForestClassifier(max_features = best_params[i]['max_features'],criterion=best_params[i]['criterion'],
                                             n_estimators= best_params[i]['n_estimators'],max_depth = best_params[i]['max_depth']))
    elif(best_params[i]['type']=="lgbm"):
        del best_params[i]['type']
        best_models.append(LGBMClassifier(num_leaves= best_params[i]['num_leaves'], min_data_in_leaf= best_params[i]['min_data_in_leaf'],
                                          learning_rate= best_params[i]['learning_rate'],feature_fraction = best_params[i]['feature_fraction'],
                                          bagging_freq=best_params[i]['bagging_freq'], lambda_l1= best_params[i]['lambda_l1'],
                                          max_bin = best_params[i]['max_bin'], min_sum_hessian_in_leaf= best_params[i]['min_sum_hessian_in_leaf'],
                                          lambda_l2= best_params[i]['lambda_l2']))
    elif(best_params[i]['type']=="mlp"):
        del best_params[i]['type']
        best_models.append(MLPClassifier(hidden_layer_sizes= best_params[i]['hidden_layer_sizes'], solver= best_params[i]['solver'],
                                         alpha= best_params[i]['alpha'], activation= best_params[i]['activation']))
print(best_models)

models_table = pd.DataFrame(columns=['train_score', 'test_score'])
for i, model in enumerate(best_models):
# finding the accuracy and recall of each of the best models
    warnings.filterwarnings(action='ignore', category=DeprecationWarning)
    cv_result = cross_validate(model, X_train, target, cv=10, scoring='accuracy',return_train_score=True)
    models_table.loc[i, 'Classfier_name'] = model.__class__.__name__
    models_table.loc[i, 'train_score'] = cv_result['train_score'].mean()
    models_table.loc[i, 'test_score'] = cv_result['test_score'].mean()
    y_pred = cross_val_predict(model, X_train, target, cv=10)
    print(model.__class__.__name__)
    print(confusion_matrix(target, y_pred))
    print()
    print(model.__class__.__name__ + ' Recall')
    print(recall_score(target, y_pred, average="macro"))
    print()
models_table


from django.conf.urls import url

from Calenda.views import *
from Dashboard import views

app_name = "dashboard"

urlpatterns = [
    url(r'^$', views.dashboard, name='dash'),
#    url(r'^admin/createUser/$', views.createAdmin, name='adminSign'),
#    url(r'^admin/createHospital/$', views.createHospital, name='createHospital'),
#    url(r'^admin/$', views.adminDash, name='adminDash'),
#    url(r'^doctor/$', views.doctorDash, name='doctorDash'),
#    url(r'^doctor/(?P<view_id>[0-9]+)/', views.doctorView, name="doctorView" ),
    url(r'^help/$', views.dashHelp, name='dashHelp'),
    url(r'^calendar/$', views.calendar, name='calendar'),
    url(r'^appvalidate/$', views.appValidate, name='appValidate'),
    url(r'^appointment/new/$', views.appCreate, name='appCreate'),
    url(r'^appointment/(?P<app_id>[0-9]+)/$', views.appUpdate, name='appUpdate'),
    url(r'^appointment/(?P<app_id>[0-9]+)/delete/$', views.appDelete, name='appDelete'),
    url(r'^admit/$', views.admit, name='admit'),
    url(r'^discharge/$', views.discharge, name='discharge'),
    url(r'^transfer/$', views.transfer, name='transfer'),
#    url(r'^basic/', views.basic, name='basic'),
#    url(r'^tests/', views.tests, name='test'),
    url(r'^create/$', views.createEntity, name='create'),
    url(r'^log/$', views.log, name='log'),
#    url(r'^hospital/', views.hospital, name='hospital'),
#    url(r'^prescriptions/', views.pres, name='pres'),
#    url(r'^messages/', views.messages, name='messages'),
#    url(r'^edit/', views.editProfile, name='editInfo'),
]


import argparse
import random

SOUND_DICT = {
    # Non-harmonizing consonants
    'C': [
        'b', 'p', 't', 'j', 'x', 'd', 'r', 'z', 's', 'f', 'l', 'm', 'n', 'h',
        'w', 'y', 'ng', 'sh', 'zh', 'ch'
    ],

    # Legal non-harmonizing word-initial consonants
    'O': [
        'b', 'p', 't', 'j', 'x', 'd', 'r', 'z', 's', 'f', 'l', 'm', 'n', 'h',
        'w', 'y', 'sh', 'zh', 'ch'
    ],

    # Non-harmonizing vowels
    'V': [
        'i', 'é'
    ],

    # /i/
    'I': [
        'i'
    ],

    # /é/
    'E': [
        'é'
    ],

    # Front vowels
    'F': [
        'e', 'ö', 'ü'
    ],

    # Back vowels
    'B': [
        'a', 'o', 'u'
    ],

    # Front dorsals
    'K': [
        'k', 'g',
    ],

    # Back dorsals
    'Q': [
        'q', 'gh'
    ]
}

def create_wugs(template, number):
    wugs = ['# {}'.format(template)]
    for _ in range(int(number)):
        wug = ''
        for c in template:
            wug += random.choice(SOUND_DICT[c])
        wugs.append(wug)
    wugs.append("")
    return wugs

def generate_wugs(input_file, output_file):
    wugs = []
    with open(input_file, 'r') as f:
        for line in f:
            if line and not line[0] == '#':
                template, number = line.rstrip().split('\t')
                wugs.extend(create_wugs(template, number))

    with open(output_file, 'w') as f:
        for wug in wugs:
            f.write(wug + '\n')

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Generate Uyghur wug words from a template file"
    )
    parser.add_argument(
        "infile", type=str, help="The file to read the templates from"
    )
    parser.add_argument(
        "outfile", type=str, help="The file to write wugs to"
    )
    args = parser.parse_args()
    generate_wugs(args.infile, args.outfile)


'''
TO DO:
Taking in input (which will be classes x y h w)
Check if there are same number of objects
Check that class is in predefined_classes.txt
***Sort by class number
Check that bounding box pixel values are less than 3 pixels apart

Written by: Eric, Rosy, Francine
'''
import os
import time
from averageText import *
from count import *
class image_labels:
    def __init__(self, file_name):
        self.file_name = file_name
        self.box_file = open(file_name, "r")
        self.box_list = sorted([x.strip('\n') for x in self.box_file.readlines()])
        self.length = len(self.box_list)
        self.DIFFERENCE = 3
        self.IMGWIDTH = 640
        self.IMGHEIGHT = 480

    def difference(self, coordinates1, coordinates2):
        """
        | Checks if the difference between two bounding boxes are greater than a set difference limit.
        |
        | Inputs:   self
        |           coordinates1: a list of lists in the format [[left_x, top_y], [right_x, bottom_y]]
        |           coordinates2: a list of lists in the format [[left_x, top_y], [right_x, bottom_y]]
        | Output:   a boolean indicating if any of the coordinate pairs are >= DIFFERENCE apart
        """
        for i in range(2): # just compare x y of coordinates
            point1 = coordinates1[i]
            point2 = coordinates2[i]
            x_difference = abs(point1[0] - point2[0])
            y_difference = abs(point1[1] - point2[1])
            if x_difference >= self.DIFFERENCE or y_difference >= self.DIFFERENCE:
                return False
        return True

    def coordinates(self, coordslist):
        x = float(coordslist[1])
        y = float(coordslist[2])
        w = float(coordslist[3])
        h = float(coordslist[4])

        topleft = ((x - w/2) * self.IMGWIDTH, (y + h/2) * self.IMGHEIGHT)
        botright = ((x + w/2) * self.IMGWIDTH, (y - h/2) * self.IMGHEIGHT)
        coordinates = [topleft, botright]
        return coordinates

    def verify_labels(self, image2):
        """
        | Check if the number of boxes in the images equal, if so continue to verify if the difference
        | between the coordinates are smaller than 3.
        |
        | Input:   self
        |          image2: another instance of the image_labels class
        | Output:  If the number of boxes are the same and the coordinate differences is smaller than 3, return True;
        |          If not, return False
        """
        # check the lengths are equal, effectively making sure they have the same number of boxes
        if self.length == image2.length:

            # for each box in the list box_list

            for i in range(self.length): # iterating thru the boxes in the files

                # split strings into lists
                i1list = self.box_list[i].split(" ")
                i2list = image2.box_list[i].split(" ")

                if i1list[0] == i2list[0]: #check if the classes are the same first
                    if(i1list == '' and i2list == ''):
                        return True
                    elif(i1list == '' and i2list != '') or (i2list == '' and i1list != ''):
                        return False
                    coordinates1 = self.coordinates(i1list)
                    coordinates2 = self.coordinates(i2list)
                    print("coordinates1: ", coordinates1)
                    print("coordinates2: ", coordinates2)

                    result = self.difference(coordinates1, coordinates2)

                    if result == False:
                        return False
                else:
                    return False
            return True
        else:
            return False

def verify(labeled_files1, labeled_files2, verified_text_files, basepath, labelers):
    for bbox_file2 in labeled_files2:
        if bbox_file2 in verified_text_files:
            continue
        for bbox_file1 in labeled_files1:
            if bbox_file2 == bbox_file1:
                file2 = basepath + labelers[1] + "/" + bbox_file2
                file1 = basepath + labelers[0] + "/" + bbox_file1
                print("file being checked rn is: ", file2)
                box2 = image_labels(file2)
                box1 = image_labels(file1)
                if box1.verify_labels(box2) == True:
                    print("passed verification")
                    passed = open("passed_verification.txt", "a")
                    print("passed ", file1, " ", file2, file = passed)
                    passed.close()
                    average_files(file1, file2, "./output/labels/")
                    os.replace(basepath + bbox_file1[:-4] + '.jpg', "./output/images/" +bbox_file1[:-4]+'.jpg')
                else:
                    print("failed verification")
                    failed = open("failed_verification.txt", "a")
                    print("failed ", file1, " ", file2, file = failed)
                    failed.close()
                    os.replace(basepath + bbox_file1[:-4] + '.jpg', "./compData/admin/" +bbox_file1[:-4]+'.jpg')
                verified_text_files.append(bbox_file1)

def start_verifying(basepath, verified_text_files=[]):
    print('Verification started')
    print('Basepath is ', basepath)
    group_names = [name for name in os.listdir(basepath) if os.path.isdir(os.path.join(basepath, name))]
    if('admin' in group_names):
        group_names.remove('admin')
    else:
        os.makedirs(basepath + "admin/")
    while(True):
        time.sleep(10)
        for group in group_names:
            print("curently checking", group)
            new_basepath = basepath +group+"/"
            labelers = [ name for name in os.listdir(new_basepath) if os.path.isdir(os.path.join(new_basepath, name))]
            if(len(labelers) == 2):
                labeled_files1 = os.listdir(new_basepath + labelers[0]+"/")
                labeled_files2 = os.listdir(new_basepath + labelers[1]+"/")
                if(len(labeled_files1)>0 and len(labeled_files2)>0):
                    if(len(labeled_files1)<=len(labeled_files2)):
                        verify(labeled_files1, labeled_files2, verified_text_files, new_basepath, labelers)
                    else:
                        verify(labeled_files2, labeled_files1, verified_text_files, new_basepath, labelers)


"""
StorageTask module
"""

import os
import re

# Conditional import
try:
    from libcloud.storage.providers import get_driver

    LIBCLOUD = True
except ImportError:
    LIBCLOUD = False

from .base import Task

class StorageTask(Task):
    """
    Task that processes object storage buckets. Supports local and cloud providers in Apache libcloud.
    """

    # URL prefix
    PREFIX = r"(\w+):\/\/.*"
    PATH = r"\w+:\/\/(.*)"

    def register(self, key=None, secret=None, host=None, port=None, token=None, region=None):
        """
        Checks if required dependencies are installed. Reads in cloud storage parameters.

        Args:
            key: provider-specific access key
            secret: provider-specific access secret
            host: server host name
            port: server port
            token: temporary session token
            region: storage region
        """

        if not LIBCLOUD:
            raise ImportError('StorageTask is not available - install "workflow" extra to enable')

        # pylint: disable=W0201
        self.key = key
        self.secret = secret
        self.host = host
        self.port = port
        self.token = token
        self.region = region

    def __call__(self, elements, executor=None):
        # Create aggregated directory listing for all elements
        outputs = []
        for element in elements:
            if self.matches(element):
                # Get directory listing and run actions
                outputs.extend(super().__call__(self.list(element), executor))
            else:
                outputs.append(element)

        return outputs

    def matches(self, element):
        """
        Determines if this element is a storage element.

        Args:
            element: input storage element

        Returns:
            True if this is a storage element
        """

        # Only accept file URLs
        return re.match(StorageTask.PREFIX, self.upack(element, True).lower())

    def list(self, element):
        """
        Gets a list of urls for a object container.

        Args:
            element: object container

        Returns:
            list of urls
        """

        provider = re.sub(StorageTask.PREFIX, r"\1", element.lower())
        path = re.sub(StorageTask.PATH, r"\1", element)

        # Load key and secret, if applicable
        key = self.key if self.key is not None else os.environ.get("ACCESS_KEY")
        secret = self.secret if self.secret is not None else os.environ.get("ACCESS_SECRET")

        # Parse key and container
        key, container = (os.path.dirname(path), os.path.basename(path)) if key is None else (key, path)

        # Parse optional prefix from container
        parts = container.split("/", 1)
        container, prefix = (parts[0], parts[1]) if len(parts) > 1 else (container, None)

        # Get driver for provider
        driver = get_driver(provider)

        # Get client connection
        client = driver(
            key,
            secret,
            host=self.host,
            port=self.port,
            token=self.token,
            region=self.region,
        )

        container = client.get_container(container_name=container)
        return [client.get_object_cdn_url(obj) for obj in client.list_container_objects(container=container, prefix=prefix)]


from models import Selection
import random,math

def elite_selection(population):
    #Keep the most fit half of individuals
    population.sort(key=lambda individual: individual.fitness, reverse=True)
    del population[(len(population)//2):]
    return population

def truncation_selection(population):
    #Choose at random having first removed the lowest fitness k individuals
    k = Selection.truncation_k
    desired_size = len(population)//2

    population.sort(key=lambda individual: individual.fitness, reverse=True)
    del population[(len(population)-k):]

    random.shuffle(population)
    del population[desired_size:]
    return population

def roulette_selection(population):
    #Probability of being selected is proportional to fitness
    new_pop = []

    #Get total fitness
    total_fitness = 0
    lowest_fitness = population[0].fitness

    for individual in population:
        if(individual.fitness < lowest_fitness):
            lowest_fitness = individual.fitness
    for individual in population:
        total_fitness += individual.fitness - lowest_fitness

    #Fill probabilities
    relative_fitness = []
    probabilities_array = []
    population.sort(key=lambda individual: individual.fitness, reverse=True) #One full sort so as to lower the overall passes over probabilities array (the first ones are the most likely to be chosen if this is done)
    for index, individual in enumerate(population):
        fitness = individual.fitness - lowest_fitness
        relative_fitness.append(fitness/total_fitness)
        probabilities_array.append(sum(relative_fitness[:index+1]))

    #Draw winners
    for i in range(len(population)//2):
        rand = random.random()
        for index, probability in enumerate(probabilities_array):
            if(rand < probability):
                new_pop.append(population[index])
                break

    return new_pop

def rank_selection(population):
    #Probability of being selected is proportional to relative fitness
    new_pop = []

    #Fill probabilities
    population.sort(key=lambda individual: individual.fitness, reverse=True) #Sort to order individuals for rank selection
    probabilities_array = []
    indexes_sum = (len(population) * (len(population)+1))//2
    probabilities_array.append(len(population)/indexes_sum)
    for i in range(1, len(population)):
        new_value = ((len(population) - i)/indexes_sum) + probabilities_array[i-1]
        probabilities_array.append(new_value)

    #Draw winners
    for i in range(len(population)//2):
        rand = random.random()
        for index, probability in enumerate(probabilities_array):
            if(rand < probability):
                new_pop.append(population[index])
                break

    return new_pop

def clash(fighter1, fighter2):
    fitter_chance = Selection.tournament_threshold
    outcome = random.random()
    if(fighter1.fitness > fighter2.fitness):
        if(outcome < fitter_chance):    #Fittest wins
            return fighter1
        else:
            return fighter2
    else:
        if(outcome < fitter_chance):    #Fittest wins
            return fighter2
        else:
            return fighter1

def tournament_selection_nr(population):    #No replacement
    #Take a pair at random and make them fight for a spot, with the fittest having more chance of success
    random.shuffle(population)
    new_pop = []
    for i in range(0, len(population)-1, 2):
        fighter1 = population[i]
        fighter2 = population[i+1]
        new_pop.append(clash(fighter1, fighter2))

    return new_pop

def tournament_selection_wr(population):        #With replacement
    #Winner of 4, with replacement
    new_pop = []
    while (len(new_pop) < len(population)/2):
        fighter1 = random.choice(population)
        fighter2 = random.choice(population)
        fighter3 = random.choice(population)
        fighter4 = random.choice(population)
        winner1 = clash(fighter1, fighter2)
        winner2 = clash(fighter3, fighter4)
        new_pop.append(clash(winner1, winner2))

    return new_pop

def T(t,Tc,T0,k):
    return Tc + (T0-Tc) * math.exp(-k*t)

def boltzmann_aptitude(individual,t,Tc,T0,k):
    return (math.exp(individual.fitness) / T(t,Tc,T0,k))

def boltzmann_selection(population):
    Tc = Selection.boltzmann_tc
    T0 = Selection.boltzmann_t0
    k = Selection.boltzmann_k
    t = Selection.boltzmann_t

    #Probability of being selected is proportional to fitness
    new_pop = []

    #Get total fitness
    total_fitness = 0
    for individual in population:
        total_fitness += boltzmann_aptitude(individual,t,Tc,T0,k)

    #Fill probabilities
    relative_fitness = []
    probabilities_array = []

    population.sort(key=lambda individual: individual.fitness, reverse=True) #One full sort so as to lower the overall passes over probabilities array (the first ones are the most likely to be chosen if this is done)
    for index, individual in enumerate(population):
        relative_fitness.append(boltzmann_aptitude(individual,t,Tc,T0,k) / total_fitness)
        probabilities_array.append(sum(relative_fitness[:index+1]))

    #Draw winners
    for i in range(len(population)//2):
        rand = random.random()
        for index, probability in enumerate(probabilities_array):
            if(rand < probability):
                new_pop.append(population[index])
                break

    Selection.boltzmann_t = t+1

    return new_pop

def selection_chooser(selection, population):

    if selection == None or selection.get("method") == None:
        print("Selection method required")
        exit(-1)

    method = selection.get("method")
    if(method == "elite"):
        return Selection(method, elite_selection)
    if(method == "roulette"):
        return Selection(method, roulette_selection)
    if(method == "rank"):
        return Selection(method, rank_selection)
    if(method == "tournament_nr" or method == "tournament_wr"):
        threshold = selection.get("tournament_threshold")
        if threshold == None or threshold <= 0:
            print("Specify a positive threshold for tournament method")
            exit(-1)
        Selection.tournament_threshold = selection.get("tournament_threshold")
        if(method == "tournament_nr"):
            return Selection(method, tournament_selection_nr)
        else:
            return Selection(method, tournament_selection_wr)
    if(method == "truncation"):
        truncation_k = selection.get("truncation_k")
        if truncation_k == None or truncation_k <= 0 or truncation_k > population:
            print("Specify a valid k for truncation method")
            exit(-1)
        Selection.truncation_k = selection.get("truncation_k")
        return Selection(method, truncation_selection)
    if(method == "boltzmann"):
        boltzmann_k = selection.get("boltzmann_k")
        boltzmann_tc = selection.get("boltzmann_tc")
        boltzmann_t0 = selection.get("boltzmann_t0")
        if boltzmann_k == None or boltzmann_k <= 0:
            print("Specify a valid k for bolztmann method")
            exit(-1)
        if boltzmann_tc == None or boltzmann_tc <= 0:
            print("Specify a valid tc for bolztmann method")
            exit(-1)
        if boltzmann_t0 == None or boltzmann_t0 <= 0:
            print("Specify a valid t0 for bolztmann method")
            exit(-1)
        Selection.boltzmann_k = selection.get("boltzmann_k")
        Selection.boltzmann_tc = selection.get("boltzmann_tc")
        Selection.boltzmann_t0 = selection.get("boltzmann_t0")
        return Selection(method,boltzmann_selection)
    else:
        print("Incorrect selection algorithm")
        exit(-1)


import copy
import json
import sys
from queue import PriorityQueue
from typing import List

from DiGraph import DiGraph
from GraphAlgoInterface import GraphAlgoInterface
from NodeData import NodeData
from src import GraphInterface

def clean(g: DiGraph) -> None:
    """
    Cleaning the graph be doing any algorithm.
    """
    if g is not None:
        for key in g.get_all_v():
            g.get_node(key).set_tag(0)
            g.get_node(key).set_weight(sys.float_info.max)
            g.get_node(key).set_info('')

def dfs(g: DiGraph, starting_node: NodeData):
    """
    famous greedy algorithm which find if from starting_node the node is connected to all others node
    param: g, starting_node
    """
    clean(g)
    st = [starting_node]
    while len(st) > 0:
        curr_node = st.pop()
        curr_node.set_tag(1)
        for neigh in g.all_out_edges_of_node(curr_node.get_key()):
            neigh_node = g.get_node(neigh)
            if neigh_node.get_tag() == 0:
                st.append(neigh_node)

def check_graph_connectivity(g: DiGraph) -> bool:
    """
    Private function which help to connect function
    """

    for node_data in g.get_all_v():
        curr = g.get_node(node_data)
        if curr.get_tag() == 0:
            return False
    return True

class GraphAlgo(GraphAlgoInterface):
    """
    Graph algo class which represent all the algorithm that can do on graph
    *TSP: to find the shortest path between a bunch of cities.
    *The Shortest Path: to check the shortest path from source node to end node
    *Connected: to see if the graph is strongly connected
    *CenterPoint: to find the center of the graph
    *Load: load graph from json
    *Save: save graph to json
    """

    def __init__(self, graph: DiGraph = None):
        """
        Regular graph_algo constructor
        @param DiGraph to initialize graph in it
        """
        if graph is not None:
            self._g = graph
        else:
            self._g = DiGraph()
        self._parent = {}

    def reset(self):
        """
        Useful only for load function to load new graph
        """
        if self._g.get_all_v():
            self._g.get_all_v().clear()
            self._g.get_edge_out().clear()
            self._g.get_edge_in().clear()

    def graph_transposer(self) -> DiGraph:
        """
         To check connectivity of directed graph need to transpose the graph
         and run to each direction from the same node dfs.
         """
        graph_trans = DiGraph()
        for node_id in self._g.get_all_v():
            graph_trans.add_node(node_id)
            for edge_id in self._g.all_out_edges_of_node(node_id):
                graph_trans.add_node(edge_id)
                graph_trans.add_edge(edge_id, node_id, self._g.all_out_edges_of_node(node_id).get(edge_id))
        return graph_trans

    def dijkstra(self, start, end=None):
        """
        Dijkstra's original algorithm found the shortest path between two given nodes, but a more common variant
        fixes a single node as the "source" node and finds shortest paths from the source to all other nodes in the
        graph, producing a shortest-path tree.
        @param: start,end optional
        """
        clean(self._g)
        p = {}
        q = PriorityQueue()
        node = self._g.get_node(start)
        node.set_weight(0)
        q.put((node.get_weight(), node.get_key()))
        while not q.empty():
            curr_node = q.get()
            curr_key = curr_node[1]
            if end is not None and curr_key == end:
                break
            for neigh in self._g.all_out_edges_of_node(curr_key):
                if self._g.get_node(neigh).get_tag() == 0:
                    neigh_node = self._g.get_node(neigh)
                    weight = self._g.get_node(curr_key).get_weight() + self._g.all_out_edges_of_node(curr_key).get(
                        neigh)
                    if weight < neigh_node.get_weight():
                        neigh_node.set_weight(weight)
                        p[neigh] = curr_key
                        q.put((neigh_node.get_weight(), neigh_node.get_key()))
                self._g.get_node(curr_key).set_tag(1)
        if end is not None:
            return p, self._g.get_node(end).get_weight()

    def connected(self) -> bool:
        """
        Directed graph called connected if and only if from each u from V there is a path
        to each w from V.
        @return True if the graph is strongly connected
        """
        if self._g.get_all_v() is not None:
            g_trans = self.graph_transposer()
            for i in range(2):
                if i == 0:
                    starting_node = next(iter(self._g.get_all_v()))
                    node = self._g.get_node(starting_node)
                    dfs(self._g, node)
                    b = check_graph_connectivity(self._g)
                    if not b:
                        return False
                if i == 1:
                    starting_node = next(iter(g_trans.get_all_v()))
                    node = g_trans.get_node(starting_node)
                    dfs(g_trans, node)
                    b = check_graph_connectivity(g_trans)
                    if not b:
                        return False
        return True

    def get_graph(self) -> GraphInterface:
        """
        :return: the directed graph on which the algorithm works on.
        """
        return self._g

    def centerPoint(self) -> (int, float):
        """
         Finds the node that has the shortest distance to it's farthest node.
         return: The nodes id, min-maximum distance
         """
        if self._g.get_all_v() is not None:
            clean(self._g)
            if self.connected():
                center = None
                highest_dist = sys.float_info.max
                for curr in self._g.get_all_v():
                    dist = sys.float_info.min
                    self.dijkstra(curr)
                    for dist_curr in self._g.get_all_v():
                        temp_dist = self._g.get_node(dist_curr).get_weight()
                        if temp_dist > dist:
                            dist = temp_dist
                    if dist < highest_dist:
                        highest_dist = dist
                        center = curr
                self._g.get_node(center).set_info('Paint')
                return center, highest_dist
        return -1, float('inf')

    def TSP(self, node_lst: List[int]) -> (List[int], float):
        """
        Finds the shortest path that visits all the nodes in the list
        param: node_lst: A list of nodes id's
        return: A list of the nodes id's in the path, and the overall distance
        """

        if self._g.get_all_v() is not None and self.valid_list(node_lst):
            clean(self._g)
            if node_lst is None or len(node_lst) == 1:
                return [], -1
            if len(node_lst) == 2 and (self.shortest_path(node_lst[0], node_lst[1])[0] != float('inf') or
                                       self.shortest_path(node_lst[1], node_lst[0])[0] != float('inf')):
                if self.shortest_path(node_lst[0], node_lst[1])[0] > self.shortest_path(node_lst[1], node_lst[0])[0]:
                    return self.shortest_path(node_lst[1], node_lst[0])
                else:
                    return self.shortest_path(node_lst[0], node_lst[1])
            if self._g.get_with_pos():

                ans1 = list()
                ans2 = list()

                shortest_path_dist_1 = 0
                shortest_path_dist_2 = 0

                right = self.find_the_rightest_node(node_lst)
                left = self.find_the_leftest_node(node_lst)

                for i in range(2):
                    if i == 0:
                        n = right
                    else:
                        n = left
                    temp_list = copy.deepcopy(node_lst)
                    while len(temp_list) > 1:
                        temp_list.remove(n)
                        next_elem = self.search_for_closest_node(n, temp_list)
                        if next_elem[0] != -1:
                            if i == 0:
                                shortest_path_dist_1 += self.shortest_path(n, next_elem[0])[0]
                            else:
                                shortest_path_dist_2 += self.shortest_path(n, next_elem[0])[0]
                        else:
                            return [], -1
                        temp_ans1 = self.shortest_path(n, next_elem[0])[1]
                        for elem in temp_ans1:
                            if i == 0:
                                ans1.append(elem)
                            else:
                                ans2.append(elem)
                        if len(temp_list) > 1:
                            if i == 0:
                                ans1.pop()
                            else:
                                ans2.pop()
                        n = next_elem[0]
                    temp_list.clear()

                if shortest_path_dist_1 < shortest_path_dist_2:
                    for node in ans1:
                        self._g.get_node(node).set_info('Paint')
                    return ans1, shortest_path_dist_1
                for node in ans2:
                    self._g.get_node(node).set_info('Paint')
                return ans2, shortest_path_dist_2
            else:
                temp_ans = list()
                ans = list()
                max_dist = sys.maxsize
                for node in node_lst:
                    temp_ans.clear()
                    dist = 0
                    temp_lst = copy.deepcopy(node_lst)
                    curr = node
                    while len(temp_lst) > 1:
                        temp_lst.remove(curr)
                        next_node, next_dist = self.search_for_closest_node(curr, temp_lst)
                        if next_node == -1:
                            dist = sys.maxsize
                            break
                        dist += next_dist
                        temp_ans1 = self.shortest_path(curr, next_node)[1]
                        for elem in temp_ans1:
                            temp_ans.append(elem)
                        if len(temp_lst) > 1:
                            temp_ans.pop()
                        curr = next_node
                    if dist < max_dist:
                        max_dist = dist
                        ans = copy.deepcopy(temp_ans)
                for node in ans:
                    self._g.get_node(node).set_info('Paint')
                return ans, max_dist
        return [], -1

    def load_from_json(self, file_name: str) -> bool:
        """
        Loads a graph from a json file.
        @param file_name: The path to the json file
        @returns True if the loading was successful, False o.w.
        """
        self.reset()
        try:
            with open(file_name, 'r+') as f:
                json_graph = json.load(f)
                for value in json_graph['Nodes']:
                    if len(value) > 1:
                        pos_str = str(value['pos'])
                        pos_arr = pos_str.split(',')
                        self._g.add_node(value['id'], (pos_arr[0], pos_arr[1], pos_arr[2]))
                    else:
                        self._g.add_node(value['id'])
                for edge in json_graph['Edges']:
                    self._g.add_edge(int(edge['src']), int(edge['dest']), float(edge['w']))
            return True
        except IOError as e:
            print(e)
            return False

    def save_to_json(self, file_name: str) -> bool:
        """
           Saves the graph in JSON format to a file
           @param file_name: The path to the out file
           @return: True if the save was successful, False o.w.
           """
        json_graph = {'Edges': [], 'Nodes': []}
        for node_data in self._g.get_all_v():
            node = self._g.get_node(node_data)
            if node.get_pos() is None:
                node_dict = {'id': node.get_key()}
                json_graph['Nodes'].append(node_dict)
            else:
                pos = str(node.get_pos()[0]) + ',' + str(node.get_pos()[1]) + ',' + str(node.get_pos()[2])
                node_dict = {'id': node.get_key(), 'pos': pos}
                json_graph['Nodes'].append(node_dict)

        for node in self._g.get_all_v():
            for edge in self._g.all_out_edges_of_node(node):
                edge_dict = {'src': node, 'w': self._g.all_out_edges_of_node(node).get(edge), 'dest': edge}
                json_graph['Edges'].append(edge_dict)

        graph = json.dumps(json_graph, indent=2)
        try:
            with open(file_name, 'w') as f:
                f.write(graph)
                return True
        except IOError as e:
            print(e)
            return False

    def shortest_path(self, id1: int, id2: int) -> (float, list):
        """
        Returns the shortest path from node id1 to node id2 using Dijkstra's Algorithm
        @param id1: The start node id
        @param id2: The end node id
        @return: The distance of the path, a list of the nodes ids that the path goes through
        """
        if self._g.get_all_v() is None or len(self._g.get_all_v()) == 1 or id1 == id2:
            return float('inf'), []
        if not id1 in self._g.get_all_v() or id2 not in self._g.get_all_v():
            return float('inf'), []
        p, d = self.dijkstra(id1, id2)
        path = []
        if d != sys.float_info.max:
            while True:
                path.append(id2)
                if id1 == id2:
                    break
                id2 = p[id2]
            path.reverse()
            for node in path:
                self._g.get_node(node).set_info('Paint')
            return d, path
        return float('inf'), path

    def plot_graph(self) -> None:
        """
        Plots the graph.
        If the nodes have a position, the nodes will be placed there.
        Otherwise, they will be placed in a random but elegant manner.
        @return: None
        """

        from src.Gui.DrawGraph import GraphDraw
        gd = GraphDraw(self)
        gd.run_gui()

    def find_the_rightest_node(self, node_lst):
        """
        Private function to TSP finding the rightest node pos_x value
        param:node_ls
        """
        rightest_index = 0
        rightest_value = 0
        for city in node_lst:
            x = self._g.get_node(city).get_pos()[0]
            if x > rightest_value:
                rightest_value = x
                rightest_index = city
        return rightest_index

    def find_the_leftest_node(self, node_lst):
        """
        Private function to TSP finding the leftest node pos_x value
        param:node_ls
        """
        leftest_index = 0
        leftest_value = sys.float_info.max
        for city in node_lst:
            x = self._g.get_node(city).get_pos()[0]
            if x < leftest_value:
                leftest_value = x
                leftest_index = city
        return leftest_index

    def search_for_closest_node(self, src, temp_list):
        """
         Private function to TSP finding the closest node to the src from the temp_list(which contains nodes)
         param:src,node_ls
         """
        min_dist = sys.float_info.max
        closest_node = -1
        for curr in temp_list:
            temp_dist = self.shortest_path(src, curr)[0]
            if min_dist > temp_dist:
                min_dist = temp_dist
                closest_node = curr
        return closest_node, min_dist

    def valid_list(self, node_lst) -> bool:
        for node in node_lst:
            if node not in self._g.get_all_v():
                return False
        return True


import subprocess
from pynput import keyboard
import P1_script
import ffmpeg
import struct
import numpy as np
from scipy.fftpack import dct, idct
import matplotlib.pyplot as plt
from PIL import Image

def convert_to_mp2(input_file, output_file):
    # Convert the video to .mp2 (this will create an audio-only .mp2 file)
    conversion_command = [
        "ffmpeg",
        "-i", input_file,
        "-vn",
        "-c:a", "mp2",
        output_file
    ]
    subprocess.run(conversion_command)
def parse_video_info(input_file):
    # Use ffprobe to get video information
    info_command = [
        "ffprobe",
        input_file
    ]
    video_info = subprocess.check_output(info_command, stderr=subprocess.STDOUT, text=True)

    # Print the video information
    print("Video Information:")
    print(video_info)

def modify_resolution(input_file, output_file, new_width, new_height):
    # Modify the resolution of the video
    resolution_command = [
        "ffmpeg",
        "-i", input_file,
        "-vf", f"scale={new_width}:{new_height}",
        output_file
    ]
    subprocess.run(resolution_command)

def change_chroma_subsampling(input_file, output_file, chroma_subsampling):
    # Change the chroma subsampling of the video
    subsampling_command = [
        "ffmpeg",
        "-i", input_file,
        "-vf", f"format=yuv420p{chroma_subsampling}",
        output_file
    ]
    subprocess.run(subsampling_command)

def read_video_info(input_file):
    # Read and print relevant video information
    info_command = [
        "ffprobe",
        input_file
    ]
    video_info = subprocess.check_output(info_command, stderr=subprocess.STDOUT, text=True)

    print("Video Information:")
    lines = video_info.splitlines()
    for line in lines:
        if "Duration" in line or "Stream" in line or "Video" in line:
            print(line)

def task1():
    input_file = "/home/ubuntunil/PycharmProjects/P2/BBB.mp4"
    # Output audio-only .mp2 file
    output_file = "/home/ubuntunil/PycharmProjects/P2/BBB.mp2"
    convert_to_mp2(input_file, output_file)
    # Parse video information using ffprobe from the original .mp4 file
    parse_video_info(input_file)

def task2():
    # Prompt the user to choose a resolution
    input_file = "/home/ubuntunil/PycharmProjects/P2/BBB.mp4"
    print("Choose a resolution:")
    print("a. 480p (854x480)")
    print("b. 720p (1280x720)")
    print("c. 1080p (1920x1080)")

    resolution_choice = input("Enter the letter of your choice and press enter: ")

    if resolution_choice == "a":
        new_width, new_height = 854, 480
    elif resolution_choice == "b":
        new_width, new_height = 1280, 720
    elif resolution_choice == "c":
        new_width, new_height = 1920, 1080
    else:
        print("Invalid choice. Using default resolution (854x480).")
        new_width, new_height = 854, 480

    # Output modified resolution video
    output_file_modified_resolution = f"BBB_{new_width}x{new_height}.mp4"

    modify_resolution(input_file, output_file_modified_resolution, new_width, new_height)
    print("Modification of resolution completed")

def task3():
    input_file = "/home/ubuntunil/PycharmProjects/P2/BBB.mp4"
    # Change the chroma subsampling
    print("Choose a chroma subsampling:")
    print("1. 4:2:0 (default)")
    print("2. 4:2:2")
    print("3. 4:4:4")

    subsampling_choice = input("Enter the letter of your choice and press enter: ")

    if subsampling_choice == "e":
        chroma_subsampling = ",format=yuv422p"
    elif subsampling_choice == "f":
        chroma_subsampling = ",format=yuv444p"
    else:
        chroma_subsampling = ""

    # Output video with modified chroma subsampling
    output_file_modified_subsampling = f"BBB_subsampling{chroma_subsampling}.mp4"
    change_chroma_subsampling(input_file, output_file_modified_subsampling, chroma_subsampling)
    print("Modification of chroma subsampling completed")

def task4():
    input_file = "/home/ubuntunil/PycharmProjects/P2/BBB.mp4"
    read_video_info(input_file)

def task5():
    input_image = '/home/ubuntunil/PycharmProjects/P2/OnePiece.jpg'
    output_image = '/home/ubuntunil/PycharmProjects/P2/outputBWOnePiece.jpg'
    P1_script.transform_to_bw_and_compress(input_image, output_image)

task_mapping = {
    '1': task1,
    '2': task2,
    '3': task3,
    '4': task4,
    '5': task5,
    }

def on_key_release(key):
    try:
        # Obtén el nombre de la tecla presionada
        key_name = key.char
        # Verifica si la tecla presionada está en el mapeo
        if key_name in task_mapping:
            # Ejecuta la tarea correspondiente
            task_mapping[key_name]()
    except AttributeError:
        # Si no es una tecla imprimible, no hacemos nada
        pass

def main():
    # Configura el listener de teclado
    with keyboard.Listener(on_release=on_key_release) as listener:
        print("Press keys 1-5 to execute the tasks. Press Ctrl+C to exit")
        listener.join()

if __name__ == "__main__":
    main()


# https://leetcode.com/problems/minimum-operations-to-make-a-uni-value-grid/
from typing import List

def minOperations(grid: List[List[int]], x: int) -> int:
    nums=list([]) # convert into vector
    for i in range(len(grid)):
        nums.extend(grid[i])
    n=len(nums)
    if n==1: return 0

    nums.sort() # sort it

    # check if there's a solution
    rem=nums[0]%x
    for i in range(1, n):
        if nums[i]%x!=rem:
            return -1 # impossible

    # find median and make it target, it makes one 0 operation
    target=nums[int(n/2)]
    return sum([abs(int((target-elem)/x)) for elem in nums])


class Solution(object):

    def romanToInt(self, s):
        """
        :type s: str
        :rtype: int
        """
        romanToInt = {"I": 1, "V":5, "X":10, "L":50, "C":100, "D": 500, "M":1000}
        answer = 0;
        previousValue = 0;
        for char in s:
            value = romanToInt[char]
            if value > previousValue:
                answer = answer + (value - 2*previousValue);
            else:
                answer = answer + value;
            previousValue = value;
        return answer


#! /usr/bin/env python
# -*- coding: utf-8 -*-

from downward import suites
import common_setup

REVS = ["issue67-v1-base", "issue67-v1"]
SUITE = suites.suite_optimal_with_ipc11()

CONFIGS = {
    "astar_blind": [
        "--search",
        "astar(blind())"],
    "astar_lmcut": [
        "--search",
        "astar(lmcut())"],
    "astar_lm_zg": [
        "--search",
        "astar(lmcount(lm_zg(), admissible=true, optimal=true))"],
}

exp = common_setup.IssueExperiment(
    search_revisions=REVS,
    configs=CONFIGS,
    suite=SUITE,
    )
exp.add_comparison_table_step()

exp()


from util import make_language_files
import numpy as np

for domain in ["EMEA", "JRC", "GNOME"]:
    docs, _ = make_language_files(domain)
    c = 0
    lens = [len(doc) for doc in docs]
    q25, q75 = np.percentile(lens, [25,75])
    ran = q75-q25
    max_len = q75 + (1.5*ran)
    min_len = q25 - (1.5*ran)
    lens_new = []
    for doc in docs:
        x = len(doc)
        if min_len <= x <= max_len:
            lens_new.append(x)
    print(f"Average without removing outliers for {domain}: {sum(lens)/len(docs)}")
    print(f"Average after removing outliers for {domain}: {sum(lens_new)/len(docs)}")


from Common.computer import Computer
from functools import reduce

TILES = {
    0: ' ',
    1: '#',
    2: 'x',
    3: '_',
    4: 'o'
}

def split_list(list, n):
    for i in range(0, len(list), n):
        yield list[i:i + n]

def show_screen(tiles):
    max_x = 0
    max_y = 0
    for tile in tiles.items():
        if tile[0][0] > max_x:
            max_x = tile[0][0]
        if tile[0][1] > max_y:
            max_y = tile[0][1]
    screen_list = []
    for i in range(0, max_y + 1):
        row = []
        for j in range(0, max_x + 1):
            tile = tiles.get((j, i), ' ')
            row.append(tile)
        screen_list.append(row)

    for i, row in enumerate(screen_list):
        screen_list[i] = ''.join(row)
    return '\n'.join(screen_list)

input_path = './input.txt'

computer = Computer(input_path)
output = computer.calculate()[1]
instructions = split_list(output, 3)
tiles = dict()
for instr in instructions:
    tiles[(instr[0], instr[1])] = TILES[instr[2]]

part_one = len(list(filter(lambda tile: tile[1] == 'x', tiles.items())))
print('answer for part1 is: ', part_one)
# 284
print(show_screen(tiles))

computer.i = 0
computer.set(0, 2)
# computer.input()
score = 0
ball_x,paddle_x=0,0
while True:
    output = computer.calculate(arcade=True)
    if output is None:
        break
    if output == 'input':
        print(show_screen(tiles))
        move = 1 if ball_x > paddle_x else 0 if ball_x == paddle_x else -1
        computer.input(move)
        continue
    if output[0] == -1 and output[1] == 0:
        score = output[2]
    else:
        tiles[(output[0], output[1])] = TILES[output[2]]
        if TILES[output[2]] == 'o':
            ball_x, ball_y = output[:2]
        elif TILES[output[2]] == '_':
            paddle_x = output[0]
print('game is finished with SCORE:', score)


import numpy as np
import matplotlib.pyplot as plt

data = [[66.3, 63.5, 60.6], [83.5, 85.2, 81.5]]

plt.rcParams.update({'font.size': 32})

X = np.arange(3)
plt.figure(facecolor=(1, 1, 1))
plt
labels_pos = np.arange(3) + 0.25
labels = ['video 0007', 'video 0036', 'video 0072']
plt.bar(X + 0.00, data[0], color = 'b', width = 0.25)
plt.bar(X + 0.25, data[1], color = 'g', width = 0.25)
plt.xticks(labels_pos, labels)
plt.legend(['Single Frame', "Ours"], fontsize = 24)
plt.ylim(0, 100)
plt.ylabel('IoU (%)')

for i in range(len(data[0])):
    plt.text(x = X[i]+0.05, y = data[0][i]+1, s = str(data[0][i]))

for i in range(len(data[1])):
    plt.text(x = X[i]+0.3, y = data[1][i]+1, s = str(data[1][i]))

plt.show()


"""\
In this .py file we will define the following functions to call from the Data_Modelling script.
Each function has a pre-processing or data wrangling function.

    1- remove_special_characters: a generic function that removes special characters from an input string
    2 - cleaning: a specialised function for the dataset that performs the pre-processing for modelling on an input
            DataFrame and returns an ouput, processed DataFrame
    3 - process_test:  a generic function that applies the major hyperparameters used on the test dataset to the
            output dataset
"""

# Import Packages ####
import pandas as pd
import numpy as np

# Remove special characters ####
def remove_special_characters(input_string):
    # A list of special_characters to be removed
    special_characters = ['@', '#', '$', '*', '&', '[', ']', '(', ')']

    for i in special_characters:
        # Replace the special character with an empty string
        input_string = str(input_string).replace(i, "")

    return input_string

# Define Function ####
def cleaning(input_df):

    # Fill Missing Observations ####
    df_processed = input_df.copy()

    # Replace Age with median split by Pclass and then Sex
    replacement_ages = pd.DataFrame(df_processed.groupby(['Pclass', 'Sex'])['Age'].median()).reset_index()

    for Pclass in df_processed['Pclass'].unique():
        for Sex in df_processed['Sex'].unique():
            df_processed.loc[(df_processed['Pclass'] == Pclass) &
                             (df_processed['Sex'] == Sex) &
                             (df_processed['Age'].isnull()), 'Age'] = \
                replacement_ages.loc[(replacement_ages['Pclass'] == Pclass) &
                                     (replacement_ages['Sex'] == Sex), 'Age'].values[0]

    # Replace all missing Embarked with 'S'
    df_processed.loc[df_processed['Embarked'].isnull(), 'Embarked'] = 'S'

    # Replace Age with median split by Pclass and then Sex
    replacement_fares = pd.DataFrame(df_processed.groupby(['Pclass', 'Sex'])['Fare'].median()).reset_index()

    for Pclass in df_processed['Pclass'].unique():
        for Sex in df_processed['Sex'].unique():
            df_processed.loc[(df_processed['Pclass'] == Pclass) &
                             (df_processed['Sex'] == Sex) &
                             (df_processed['Fare'].isnull()), 'Fare'] = \
                replacement_fares.loc[(replacement_fares['Pclass'] == Pclass) &
                                      (replacement_fares['Sex'] == Sex), 'Fare'].values[0]

    # I cannot find cabin data online, so it will have to be dropped
    df_processed.drop(labels='Cabin', axis='columns', inplace=True)

    # Data_Processing ####
    # Change sex into numerical representation
    df_processed[['Sex']] = df_processed[['Sex']].replace("male", 1)  # change male to 1
    df_processed[['Sex']] = df_processed[['Sex']].replace("female", 0)  # change female to 0

    # Feature Engineering ####
    # Add total family size column
    df_processed['Family_Size'] = df_processed['SibSp'] + df_processed['Parch']

    # Add travelling alone column
    df_processed['Travelling_Alone'] = 'ERROR'
    df_processed.loc[df_processed['Family_Size'] == 0, 'Travelling_Alone'] = 1
    df_processed.loc[df_processed['Family_Size'] != 0, 'Travelling_Alone'] = 0

    # Add a title column and populate with title based on title appearing in name
    df_processed.insert(3, 'Title', 'ERROR')

    titles_list = ['Mr.', 'Miss.', 'Mrs.', 'Master.', 'Dr.', 'Rev.']  # list to iterate through

    for title in titles_list:  # for loop to replace Title where appropriate
        df_processed.loc[np.where(df_processed['Name'].str.contains(title))[0], 'Title'] = title

    # Exceptions
    # Pool all Military titles into one label
    df_processed.loc[np.where(df_processed['Name'].str.contains('Capt. | Major. | Col.'))[0], 'Title'] = 'Military'

    # Pull Men without titles in the Mr. title
    df_processed.loc[(df_processed['Sex'] == 1) &
                     (df_processed['Title'] == 'ERROR'), 'Title'] = 'Mr.'

    # Pull any females without any spouses or siblings into the Miss. title
    df_processed.loc[(df_processed['Sex'] == 0) &
                     (df_processed['SibSp'] == 0) &
                     (df_processed['Title'] == 'ERROR'), 'Title'] = 'Miss.'

    # The  Mr. and Master Titles don't match their ages.
    # Make any Master. >18 Mr.
    df_processed.loc[(df_processed['Age'] > 18) &
                     (df_processed['Title'] == 'Master.'), 'Title'] = 'Mr.'

    # Make any Mr. < 18 Masters
    df_processed.loc[(df_processed['Age'] < 18) &
                     (df_processed['Title'] == 'Mr.'), 'Title'] = 'Master.'

    # Feature Binning ####
    # Bin Age into Age_Range
    df_processed['Age_Range'] = pd.cut(df_processed['Age'],
                                       bins=[0, 12, 18, 30, 60, 80],
                                       labels=['Children', 'Teenage', 'Young Adult', 'Adult', 'Elder'])

    # Bin Fare into Age_Range
    df_processed['Fare_Range'] = pd.qcut(df_processed['Fare'], q=4,
                                         labels=['Lowest', 'Lower-Median', 'Upper-Median', 'Upper'])

    # Sort out dtypes
    df_processed['Title'] = df_processed['Title'].astype('category')
    df_processed['Embarked'] = df_processed['Embarked'].astype('category')

    # Drop Unwanted Columns ####
    # Drop the PassengerID as the heatmap shows it was a bad predictor of survived
    df_processed.drop(labels='PassengerId', axis='columns', inplace=True)

    # Drop the Ticket as I think it has no value for the analysis
    df_processed.drop(labels='Ticket', axis='columns', inplace=True)

    return df_processed

# Define a function that processes the test dataset with the same parameters as the training dataset ####
def process_test(input_df, multicolinearity_to_drop, transformation_method, features_sel):
    # One-hot encoding ####
    input_df = pd.get_dummies(input_df,
                              columns=['Embarked', 'Title', 'Age_Range', 'Fare_Range'],
                              prefix=['Embarked', 'Title', 'Age_Range', 'Fare_Range'])

    # Drop any columns that shows Multi-colinearity
    input_df.drop(multicolinearity_to_drop, axis='columns', inplace=True)

    # Drop the unnecessary columns for the model that cannot be transformed ####
    input_df.drop(labels=['Name'], axis='columns', inplace=True)

    # Apply Feature Selection
    input_df = input_df.loc[:, features_sel]

    # Apply Transformation
    transformation = transformation_method
    input_df = transformation.fit(input_df).transform(input_df)

    return input_df


from flask import Blueprint,jsonify,send_file,request

from baidu_ai import text2audio
from setting import MONGO_DB,COVER,MUSIC,CHATS,RET
import os,time
from bson import ObjectId
from ChatRedis import get_chat

chats = Blueprint("chats",__name__)

@chats.route("/chat_list",methods=["POST"])
def chat_list():
    chat_id = request.form.get("chat_id")
    to_user = request.form.get("to_user")
    from_user = request.form.get("from_user")
    chat_window = MONGO_DB.chats.find_one({"_id":ObjectId(chat_id)})
    # chat_list = chat_window.get("chat_list") # ["12313213214134","435278346587263475"]
    get_chat(to_user,from_user)

    RET["code"] = 0
    RET["msg"] = "查询聊天记录"
    RET["data"] = chat_window.get("chat_list")[-20:]

    return jsonify(RET)

@chats.route("/recv_message",methods=["POST"])
def recv_message():
    chat_info = request.form.to_dict()
    # {from_user,to_user}
    chat = MONGO_DB.chats.find_one({"user_list":{"$all":[chat_info.get("from_user"),chat_info.get("to_user")]}})
    chat_list = reversed(chat.get("chat_list"))

    count = get_chat(chat_info.get("to_user"),chat_info.get("from_user"))
    print(count)
    if not count:
        # 没有收到消息
        res = text2audio("没有新的消息")
        return jsonify([{"from_user":chat_info.get("from_user"),"message":res}])

    new_chat_list = []
    for chat in chat_list:
        if chat.get("from_user") == chat_info.get("to_user"):
            continue
        new_chat_list.append(chat)
        if len(new_chat_list) == count:
            break

    # chat_last = chat_list[-1]
    # 1.如果很多条未读？
    # 2.直接回复消息，收取自己的消息
    print(new_chat_list)
    return jsonify(new_chat_list)