import numpy as np
from copy import deepcopy
from src.utils import transform_df_to_np_array
from src.utils import vprint
import pandas as pd
from sklearn.metrics import recall_score, precision_score, accuracy_score

class Hyperrectangle():

    def __init__(self, bounds=None, init_around_random_point=None, X=None, min_size=10, nb_steps=100, alpha=0.1,
                 metric="concentration", epsilon=0.1, beta=1, margin=0.5, compute_min_max_dataset=False, verbose=False):
        if bounds:
            self.bounds = bounds
        elif init_around_random_point:
            self.bounds = Hyperrectangle.init_around_random_point(X, margin=margin)
        else:
            ValueError("Either bounds of init_around_random_point must be provided")
        self.min_size = min_size
        self.nb_steps = nb_steps
        self.metric = metric
        self.epsilon = epsilon
        self.beta = beta
        self.alpha = alpha
        self.verbose = verbose

        self.bounds_history = [deepcopy(self.bounds)]
        self.metric_history = []
        self.exploration_steps = []

        # For prediction
        self.concentration = None
        self.proba = None

        if compute_min_max_dataset:
            self.info_min_max = [[min(X[:, i]), max(X[:, i])] for i in range(X.shape[1])]
            if isinstance(X, pd.DataFrame):
                self.var_names = X.columns
            else:
                self.var_names = None
        else:
            self.info_min_max = None

    def fit(self, X, y):
        current_size = self.compute_size(X, y)
        nb_steps = 0

        best_metric = self.compute_metric(X, y, self.metric)
        self.metric_history.append(best_metric)

        v = self.verbose
        while (current_size > self.min_size) and (nb_steps < self.nb_steps):
            original_metric = best_metric
            current_bounds = deepcopy(self.bounds)
            var_opt, bound_opt, trend_opt = None, None, None
            # Explore around the hyperrectangle
            vprint(v, "step - current best metric -- bound - trend - metric")
            for var in range(X.shape[1]):
                for bound in ["left", "right"]:
                    for k in ["pos", "neg"]:
                        if k == "pos":
                            trend = self.epsilon
                        else:
                            trend = - self.epsilon
                        self.change_bound(var, bound, trend)
                        new_metric = self.compute_metric(X, y, self.metric)
                        vprint(v, "%d - %.3f -- %s - %.2f - %.3f"%(nb_steps, original_metric, bound, trend, new_metric))
                        # Record history
                        self.bounds_history.append(deepcopy(self.bounds))
                        self.metric_history.append(new_metric)

                        if new_metric > best_metric:
                            var_opt, bound_opt, trend_opt = var, bound, trend
                            best_metric = new_metric
                        self.bounds = deepcopy(current_bounds)
            # Update the bounds if necessary
            if best_metric > original_metric:
                grad = (best_metric - original_metric)/trend_opt
                self.change_bound(var_opt, bound_opt, self.alpha*grad)
                best_metric = self.compute_metric(X, y, self.metric)
                if v:
                    bound_to_print = [list(map(lambda x: round(x, 2), b)) for b in self.bounds]
                    curr_bounds_to_print = [list(map(lambda x: round(x, 2), b)) for b in current_bounds]
                    vprint(v, "---update bounds--- alpha*grad = %.3f (%.3f, %.3f) - %s -> %s"%(self.alpha*grad, self.alpha,
                                                                                grad, curr_bounds_to_print,
                                                                                bound_to_print))
                # Record history
                self.bounds_history.append(deepcopy(self.bounds))
                self.metric_history.append(best_metric)
            else:
                vprint(v, "---no bounds update---")
                # Record history
                self.bounds_history.append(deepcopy(self.bounds))
                self.metric_history.append(best_metric)

            # Calcul stop criteria
            current_size = self.compute_size(X, y)
            nb_steps += 1
        # Record concentration for predictions
        self.concentration = self.compute_concentration(X, y)

    def predict(self, X):
        if isinstance(X, pd.DataFrame):
            X = X.values
        return self.get_mask(X).astype(int)

    def get_proba(self, X):
        if isinstance(X, pd.DataFrame):
            X = X.values
        mask = self.get_mask(X).astype(int)
        return self.concentration*mask

    def change_bound(self, dim, bound, epsilon):
        """
        Change the boundaries of the dim-th variable by extending the left or right bound by epsilon (might be negative)\\
        For instance :
        >>> hr = Hyperrectangle(bounds=[[1, 10], [-2, 2], [3, 9], [-7, -1]])
        >>> hr.change_bound(1, "left", 1)
        >>> hr.bounds
        [[1, 10], [-1, 2], [3, 9], [-7, -1]]
        The changes ensure that left bound is always lower than or equal to the right bound. If the hyperrectangle\\
         has min/max dataset information, the function ensure that lower bound is always greater or equal to the\\
         minimum observed in the variable the dataset and the right bound lower or equal than the maximum observed in\\
         the variable on the dataset
        :param dim: int, the boundary number to change
        :param bound: string, must be 'left' or 'right'. Indicates which side to change
        :param epsilon: float, the amount of change to apply.
        :return: None
        """
        new_bounds = self.bounds
        if bound == "left":
            if (epsilon > 0):
                new_bounds[dim][0] = min(new_bounds[dim][0] + epsilon, new_bounds[dim][1])
            elif (epsilon < 0) and (self.info_min_max is not None):
                new_bounds[dim][0] = max(new_bounds[dim][0] + epsilon, self.info_min_max[dim][0])
            else:
                new_bounds[dim][0] = new_bounds[dim][0] + epsilon
        elif bound == "right":
            if (epsilon < 0):
                new_bounds[dim][1] = max(new_bounds[dim][1] + epsilon, new_bounds[dim][0])
            elif (epsilon > 0) and (self.info_min_max is not None):
                new_bounds[dim][1] = min(new_bounds[dim][1] + epsilon, self.info_min_max[dim][1])
            else:
                new_bounds[dim][1] = new_bounds[dim][1] + epsilon
        else:
            raise ValueError("bound must take either 'left' or 'right'")

    def compute_metric(self, X, y):
        """
        Compute metric on the dataset X,y provided. Metric available are 'size', 'concentration', 'z_score', 'f_beta' \\
        based on size and concentration, 'lift'. A user defined function can also be provided. It must take a numpy array \\
        representing X and one representing y after they have been filtered by the hyperrectangle.
        :param X: numpy array or datafram pandas
        :param y: numpy array or pandas series
        :param metric: string or function
        :return: float
        """
        if isinstance(X, pd.DataFrame):
            X = transform_df_to_np_array(X)

        if isinstance(y, pd.Series):
            y = y.values

        y_pred = self.predict(X)

        if isinstance(metric, str):
            if metric == "size":
                m = self.compute_size(X, y)
            elif metric == "concentration":
                m = self.compute_concentration(X, y)
            elif metric == "z_score":
                m = self.compute_z_score(X, y)
            elif metric == "f_beta":
                m = self.compute_f_beta_score(X, y)
            elif metric == "lift": #TODO ajouter ce test aux tests unitaires
                m = self.compute_lift(X, y)
            #TODO ajouter recall, precision, accuracy
            else:
                raise NotImplementedError("Please provide a string metric among those available")
        elif callable(metric):
            _, y_filter = self.filter_dataset(X, y)
            m = metric(X, y_filter)
        else:
            raise ValueError("metric takes either string or function")
        return m

    def compute_size(self, X, y):
        _, y_filter = self.filter_dataset(X, y)
        return len(y_filter)

    def compute_concentration(self, X, y):
        _, y_filter = self.filter_dataset(X, y)
        if len(y_filter) > 0:
            return y_filter.sum()/len(y_filter)
        else:
            return 0

    def compute_z_score(self, X, y):
        original_concentration = y.sum()/len(y)
        _, y_filter = self.filter_dataset(X, y)
        if len(y_filter) > 0:
            current_concentration = y_filter.sum()/len(y_filter)
            return (current_concentration - original_concentration)/len(y_filter)
        else:
            return None

    def compute_f_beta_score(self, X, y):
        c = self.compute_concentration(X, y)
        s = self.compute_size(X, y)
        d = (self.beta**2)*s + c
        if d != 0:
            return (1+self.beta**2)*c*s/d
        else:
            return 0

    def compute_lift(self, X, y):
        original_concentration = y.sum()/len(y)
        c = self.compute_concentration(X, y)
        if original_concentration != 0:
            return c/original_concentration
        else:
            raise Exception("There is no 1 in the y vector")

    def compute_recall(self, X, y):#todo tu à faire
        X_filter, y_filter = self.filter_dataset(X, y)
        y_pred = self.predict(X_filter)
        return recall_score(y_filter, y_pred)

    def compute_precision(self, X, y):#todo tu à faire
        X_filter, y_filter = self.filter_dataset(X, y)
        y_pred = self.predict(X_filter)
        return precision_score(y_filter, y_pred)

    def compute_accuracy(self, X, y):#todo tu à faire
        X_filter, y_filter = self.filter_dataset(X, y)
        y_pred = self.predict(X_filter)
        return accuracy_score(y_filter, y_pred)

    def filter_dataset(self, X, y):
        mask = self.get_mask(X).reshape(-1)
        return X[mask, :], y[mask].reshape(mask.sum(), 1)

    def get_mask(self, X):
        mask = True
        for i in range(len(self.bounds)):
            mask = mask * ((X[:,i]>=self.bounds[i][0])*(X[:,i]<=self.bounds[i][1]))
        return mask.reshape(X.shape[0], 1)

    @staticmethod
    def init_around_random_point(X, margin=0.5, seed=None):
        """
        Choose one point (one line) within the dataset and create a hypercube that as this point as center and \
        vertices of length 2*margin
        :param X: array of 1 line per point and variables in columns. Can be a pandas dataframe to
        :param margin: distance between the center and one face of the hypercube
        :param seed: random seed
        :return: list of list describing bounds of the hypercube
        """
        if seed:
            np.random.seed(seed)
        if isinstance(X, pd.DataFrame):
            X = transform_df_to_np_array(X)
        point = X[np.random.randint(0, X.shape[0]),:]
        bounds = []
        for i in range(len(point)):
            low = point[i] - margin
            high = point[i] + margin
            bounds.append([low, high])
        return bounds


from unittest.mock import Mock, call

import pytest

from uttut import NOT_ENTITY
from uttut.elements import Datum, Intent, Entity
from ..ordinal_label import OrdinalLabel

TRANSFORMATION_PAIRS = [
    (
        {
            'utterance': '你好',
            'intent': {'names': ['HI']},
        },
        Datum(utterance='你好', intents=[Intent(0)]),
    ),
    (
        {
            "utterance": "我想喝珍奶半糖",
            "intent": {
                "names": ["ORDER"],
            },
            "entities": [
                {
                    "name": "ITEM",
                    "start": 3,
                    "end": 5,
                    "replacements": ["拿鐵", "多多綠", "紅茶"],
                },
                {
                    "name": "SUGAR",
                    "start": 5,
                    "end": 7,
                    "replacements": ["無糖", "全糖"],
                },
            ],
        },
        Datum(
            utterance='我想喝珍奶半糖',
            intents=[Intent(2)],
            entities=[
                Entity(1, '珍奶', 3, 5, ["拿鐵", "多多綠", "紅茶"]),
                Entity(2, '半糖', 5, 7, ["無糖", "全糖"]),
            ],
        ),
    ),
]

@pytest.fixture(scope='function')
def tx():
    intent2index = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    entity2index = {'ITEM': 1, 'SUGAR': 2}
    not_entity_index = 0
    tx = OrdinalLabel(intent2index, entity2index, not_entity_index)
    return tx

@pytest.yield_fixture
def mocked_OrdinalLabel():
    orig_checker = OrdinalLabel.is_valid_mapping
    OrdinalLabel.is_valid_mapping = Mock(return_value=True)
    yield OrdinalLabel
    OrdinalLabel.is_valid_mapping = orig_checker

def test_ordinal_label_init(mocked_OrdinalLabel):
    intent2index = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    entity2index = {'DRINK': 0, 'SIZE': 1}
    not_entity_index = 2
    tx = mocked_OrdinalLabel(intent2index, entity2index, not_entity_index)
    assert tx._intent2index == intent2index
    assert tx._entity2index == entity2index
    assert tx._not_entity_index == not_entity_index

    assert tx.not_entity_index == not_entity_index

    actual_calls = mocked_OrdinalLabel.is_valid_mapping.call_args_list
    expected_calls = [
        call(intent2index),
        call({**entity2index, NOT_ENTITY: 2}),
    ]
    assert expected_calls == actual_calls

def test_ordinal_label_init_raise_if_intent2index_not_valid():
    # setup
    orig_checker = OrdinalLabel.is_valid_mapping
    OrdinalLabel.is_valid_mapping = Mock(side_effect=[False, True])

    intent2index = {}
    entity2index = {}
    with pytest.raises(ValueError):
        OrdinalLabel(intent2index, entity2index)

    # tear down
    OrdinalLabel.is_valid_mapping = orig_checker

def test_ordinal_label_init_raise_if_entity2index_not_valid():
    # setup
    orig_checker = OrdinalLabel.is_valid_mapping
    OrdinalLabel.is_valid_mapping = Mock(side_effect=[True, False])

    intent2index = {}
    entity2index = {}
    with pytest.raises(ValueError):
        OrdinalLabel(intent2index, entity2index)

    # tear down
    OrdinalLabel.is_valid_mapping = orig_checker

def test_ordinal_label_is_valid_mapping():
    correct_mapping = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    duplicate_mapping = {'HI': 0, 'BYE': 1, 'ORDER': 1}
    non_ordinal_mapping = {'HI': 0, 'BYE': 1, 'ORDER': 3}
    mapping_with_negative = {'HI': -1, 'BYE': 0, 'ORDER': 1}
    assert OrdinalLabel.is_valid_mapping(correct_mapping)
    assert not OrdinalLabel.is_valid_mapping(duplicate_mapping)
    assert not OrdinalLabel.is_valid_mapping(non_ordinal_mapping)
    assert not OrdinalLabel.is_valid_mapping(mapping_with_negative)

@pytest.mark.parametrize('expected, datum', TRANSFORMATION_PAIRS)
def test_ordinal_label_humanize(expected, datum, tx):
    actual = tx.humanize(datum)
    assert set(actual.keys()) == set(expected.keys())
    assert actual['utterance'] == expected['utterance']
    assert set(actual['intent']['names']) == set(expected['intent']['names'])
    assert len(actual.get('entities', [])) == len(expected.get('entities', []))

    if len(actual.get('entities', [])) > 0:
        for actual_entity, expected_entity in zip(actual['entities'], expected['entities']):
            assert actual_entity['name'] == expected_entity['name']
            assert actual_entity['start'] == expected_entity['start']
            assert actual_entity['end'] == expected_entity['end']
            actual_replacements = set(actual_entity.get('replacements', []))
            expected_replacements = set(expected_entity.get('replacements', []))
            assert actual_replacements == expected_replacements

@pytest.mark.parametrize('raw_dict, datum', TRANSFORMATION_PAIRS)
def test_ordinal_label_machanize(raw_dict, datum, tx):
    assert tx.machanize(raw_dict) == datum

def test_ordinal_label_serialization():
    intent2index = {'HI': 0, 'BYE': 1, 'ORDER': 2}
    entity2index = {'DRINK': 1, 'SIZE': 2}
    tx = OrdinalLabel(intent2index, entity2index)

    serialized = tx.serialize()
    loaded_tx = OrdinalLabel.deserialize(serialized)

    assert tx._intent2index == loaded_tx._intent2index
    assert tx._entity2index == loaded_tx._entity2index

@pytest.fixture(scope='function')
def raw_data():
    return {
        "data": [
            {
                "utterance": "你好",
                "intent": {
                    "names": ["GREETINGS"],
                },
            },
            {
                "utterance": "我想喝珍奶半糖",
                "intent": {
                    "names": ["ORDER"],
                },
                "entities": [
                    {
                        "name": "ITEM",
                        "start": 3,
                        "end": 5,
                        "replacements": ["拿鐵", "多多綠", "紅茶"],
                    },
                    {
                        "name": "SUGAR",
                        "start": 5,
                        "end": 7,
                        "replacements": ["無糖", "全糖"],
                    },
                ],
            },
        ],
    }

def test_ordinal_from_raw_dictionary(raw_data):
    tx = OrdinalLabel.from_raw_dictionary(raw_data)
    # indexes are based on the appearance order
    assert tx._intent2index == {"GREETINGS": 0, "ORDER": 1}
    assert tx._entity2index == {"ITEM": 1, "SUGAR": 2}

def test_ordinal_from_raw_dictionary_with_custom_not_entity_index(raw_data):
    tx = OrdinalLabel.from_raw_dictionary(raw_data, not_entity_index=1)
    # indexes are based on the appearance order
    assert tx._intent2index == {"GREETINGS": 0, "ORDER": 1}
    assert tx._entity2index == {"ITEM": 0, "SUGAR": 2}

def test_ordinal_from_raw_dictionary_with_custom_not_entity_index_2(raw_data):
    tx = OrdinalLabel.from_raw_dictionary(raw_data, not_entity_index=2)
    # indexes are based on the appearance order
    assert tx._intent2index == {"GREETINGS": 0, "ORDER": 1}
    assert tx._entity2index == {"ITEM": 0, "SUGAR": 1}

@pytest.mark.parametrize('wrong_entity_index', [0., 1., 100., 0.1, '0', '0.1'])
def test_ordinal_from_raw_dictionary_raise_with_wrong_type(wrong_entity_index):
    raw_data = {'data': []}
    with pytest.raises(TypeError):
        OrdinalLabel.from_raw_dictionary(raw_data, not_entity_index=wrong_entity_index)


import collections

participant = ["mislav", "stanko", "mislav", "ana"]
completion = ["stanko", "ana", "mislav"]

def solution(participant, completion):
    dic = {}

    for i in participant:
        if i in dic.keys():
            dic[i] += 1
        else:
            dic[i] = 1
    # {'mislav': 2, 'stanko': 1, 'ana': 1}

    for i in completion:
        if i in dic.keys():
            dic[i] -= 1
    # {'mislav': 1, 'stanko': 0, 'ana': 0}

    for key, value in dic.items():
        if value == 1:
            return key
    # mislav

print(solution(participant, completion))

# Counter 라이브러리 활용한 풀이
def solution2(participant, completion):
    answer = collections.Counter(participant) - collections.Counter(completion)
    return list(answer.keys())[0]

# hash 활용한 풀이
def solution3(participant, completion):
    answer = ''
    temp = 0
    dic = {}
    for part in participant:
        dic[hash(part)] = part
        temp += int(hash(part))
    for com in completion:
        temp -= hash(com)
    answer = dic[temp]

    return answer

# sort 후 비교하는 풀이

def solution4(participant, completion):
    participant.sort()
    completion.sort()
    for i in range(len(completion)):
        if participant[i] != completion[i]:
            return participant[i]
    return participant[len(participant)-1]


from xml.sax.handler import ContentHandler
from xml.sax import make_parser
import sys
import string

class RedditHandler(ContentHandler):

    def __init__ (self):
        # Item
        self.inEntry = False
        self.inContent = False
        self.content = ""
        self.title = " "
        self.link = " "
        self.description = " "
        self.id = " "
        self.news = []
        # Alimentador
        self.nameAlim = " "
        self.uriAlim = " "

    def startElement (self, name, attrs):
        if name == 'entry':
            self.inEntry = True
        elif self.inEntry:
            if name == 'content':
                self.inContent = True
            elif name == 'link':
                self.link = attrs.get('href')
            elif name == 'title':
                self.inContent = True
            elif name == 'id':
                self.inContent = True
        elif not self.inEntry:
            if name == 'title':
                self.inContent = True
            elif (name == 'link') and (attrs.get('rel') == "alternate"):
                self.uriAlim = attrs.get('href')

    def endElement (self, name):
        global news

        if name == 'entry':
            self.inEntry = False
            self.news.append({'link': self.link,
                                'title': self.title,
                                'description': self.description,
                                'id':self.id})
        elif self.inEntry:
            if name == 'title':
                self.title = self.content
                self.content = ""
                self.inContent = False
            elif name == 'content':
                self.description = self.content
                self.content = ""
                self.inContent = False
            elif name == 'id':
                self.id = self.content
                self.content = ""
                self.inContent = False
    # Iformacion relacionada con el canal
        elif not self.inEntry:
            if name == 'title':
                self.nameAlim = self.content
                self.content = ""
                self.inContent = False

    def characters (self, chars):
        if self.inContent:
            self.content = self.content + chars

class RedditNews:

    def __init__(self, stream):
        self.parser = make_parser()
        self.handler = RedditHandler()
        self.parser.setContentHandler(self.handler)
        self.parser.parse(stream)

    def news (self):
        return self.handler.news

    def nameAlim (self):
        return self.handler.nameAlim

    def uriAlim (self):
        return self.handler.uriAlim


import sqlite3
# Вы создаете БД для учета задач в команде разработки.
conn = sqlite3.connect('Tasks.db')
cursor = conn.cursor()
# Вам необходимо создать базу данных для хранения информации о задачах и их статусе.
# Каждая задача должна иметь уникальный идентификатор,
# название,
# описание и
# статус (выполнена или невыполнена).
cursor.execute(
    '''CREATE TABLE IF NOT EXISTS Task_execution_control(
     ID INTEGER PRIMARY KEY AUTOINCREMENT,
     Name TEXT,
     Description TEXT,
     Status TEXT
     )'''
 )

#
# Напишите программу на языке Python, которая создает базу данных SQLite,
# добавляет в нее несколько задач и позволяет пользователю получать информацию о задачах.
cursor.execute(
    '''INSERT INTO Task_execution_control(
    Name,
    Description,
    Status)
    VALUES(
    'Домашнее задание',
    'по математике',
    'не выполненно'
    )'''
)
cursor.execute(
    '''INSERT INTO Task_execution_control(
    Name,
    Description,
    Status)
    VALUES(
    'Домашнее задание',
    'по физике',
    'выполненно'
    )'''
)
cursor.execute(
    '''INSERT INTO Task_execution_control(
    Name,
    Description,
    Status)
    VALUES(
    'Контрольная работа',
    'по химии',
    'выполненно'
    )'''
)
cursor.execute(
    '''INSERT INTO Task_execution_control(
    Name,
    Description,
    Status)
    VALUES(
    'Зачет',
    'по иностранному языку',
    'выполненно'
    )'''
)
conn.commit()

cursor.execute('''SELECT Name, Description, Status FROM Task_execution_control WHERE Status = 'выполненно' ''')
print(*cursor)

Table=cursor.execute('''SELECT * FROM Task_execution_control ORDER BY ID''')
print(*Table)

cursor.execute('''SELECT * FROM Task_execution_control''')
List = cursor.fetchall()

for i in List:
    print(*i)


from ultralytics import YOLO
import streamlit as st
import os

#######################################################################################################################
## Ъј» The aim of this script is to create an Rock/Paper/Scissors application based on a trained model (from YOLOv8). ##
## ЪЈъ The uploaded snapshots are stored, by default, in /workspace/attendee/.                                        ##
## ЪДа The train model is stored, by default, in /workspace/attendee/                                                 ##
## Рё╣№ИЈ Note on the environments variables:                                                                            ##
##      - WORK_PATH (default value: '/workspace/attendee/') is the path where get the model and store de snapshots   ##
#######################################################################################################################

# ЪЏа Get configuration from environment variables
WORK_PATH = os.getenv('WORK_PATH', '/workspace/attendee/')

# Save uploaded photo
def save_photo(photo):

    photoAbsolutePath = WORK_PATH + photo.name

    with open(photoAbsolutePath,'wb') as f:
         f.write(photo.getbuffer())

    return photoAbsolutePath

# main
if __name__ == '__main__':

    st.write("## Welcome on the Ъфе ЪЊё Рюѓ№ИЈ game!")
    # ЪДа Load the model
    model = YOLO(WORK_PATH + "best.torchscript")

    # ЪЊИ Camera input
    img_file_buffer = st.camera_input("Take your picture in real time:")
    if img_file_buffer is not None:
      photoPath = save_photo(img_file_buffer)

      # Ъћј Prediction
      results = model.predict(photoPath, verbose=True, save=True, conf=0.5)

      # ЪЊѕ Display results
      for r in results:
        for c in r.boxes.cls:
          st.write(r.names[int(c)])
      #st.write(results)


from __future__ import division

import torch
import numpy as np
import cv2

def unique(tensor):
    """
    Returns all unique values in tensor
    """

    tensor_np = tensor.cpu().numpy()
    unique_np = np.unique(tensor_np)
    unique_tensor = torch.from_numpy(unique_np)

    tensor_res = tensor.new(unique_tensor.shape)
    tensor_res.copy_(unique_tensor)
    return tensor_res

def bbox_iou(box1, box2):
    """
    Returns the IoU of two bounding boxes
    """

    # get bbox coords
    b1_x1, b1_y1, b1_x2, b1_y2 = box1[:, 0], box1[:, 1], box1[:, 2], box1[:, 3]
    b2_x1, b2_y1, b2_x2, b2_y2 = box2[:, 0], box2[:, 1], box2[:, 2], box2[:, 3]

    # get coordinates of intersection rectangle
    inter_rect_x1 = torch.max(b1_x1, b2_x1)
    inter_rect_y1 = torch.max(b1_y1, b2_y1)
    inter_rect_x2 = torch.min(b1_x2, b2_x2)
    inter_rect_y2 = torch.min(b1_y2, b2_y2)

    # calculate intersection area
    inter_area = torch.clamp(
        inter_rect_x2 - inter_rect_x1 + 1,
        min=0
    ) * torch.clamp(
        inter_rect_y2 - inter_rect_y1 + 1,
        min=0
    )

    # calculate union area
    b1_area = (b1_x2 - b1_x1 + 1) * (b1_y2 - b1_y1 + 1)
    b2_area = (b2_x2 - b2_x1 + 1) * (b2_y2 - b2_y1 + 1)
    union_area = b1_area + b2_area - inter_area

    # return IoU
    return (inter_area / union_area)

def predict_transform(prediction, inp_dim, anchors, num_classes, CUDA=False):
    """
    Transforms all predictions across the 3 scales into a consistent siz
    """
    batch_size = prediction.size(0)
    stride = inp_dim // prediction.size(2)
    grid_size = inp_dim // stride
    bbox_attrs = 5 + num_classes
    num_anchors = len(anchors)

    # transform predictions to 2d
    prediction = prediction.view(
        batch_size, bbox_attrs * num_anchors, grid_size*grid_size)
    prediction = prediction.transpose(1, 2).contiguous()
    prediction = prediction.view(
        batch_size, grid_size*grid_size*num_anchors, bbox_attrs)

    # adjust anchor box sizes for detection layer sizes
    anchors = [(a[0]/stride, a[1]/stride) for a in anchors]

    # Sigmoid the coords and confidence scores
    prediction[:, :, 0] = torch.sigmoid(prediction[:, :, 0])
    prediction[:, :, 1] = torch.sigmoid(prediction[:, :, 1])
    prediction[:, :, 4] = torch.sigmoid(prediction[:, :, 4])

    # Add grid offsets to center coords prediction
    grid = np.arange(grid_size)
    a, b = np.meshgrid(grid, grid)

    x_offset = torch.FloatTensor(a).view(-1, 1)
    y_offset = torch.FloatTensor(b).view(-1, 1)

    if CUDA:
        x_offset = x_offset.cuda()
        y_offset = y_offset.cuda()

    x_y_offset = torch.cat((x_offset, y_offset), 1).repeat(
        1, num_anchors).view(-1, 2).unsqueeze(0)

    prediction[:, :, :2] += x_y_offset

    # apply anchors to dimensions of bounding box
    anchors = torch.FloatTensor(anchors)

    if CUDA:
        anchors = anchors.cuda()

    anchors = anchors.repeat(grid_size*grid_size, 1).unsqueeze(0)
    prediction[:, :, 2:4] = torch.exp(
        prediction[:, :, 2:4])*anchors  # log space transforms

    # apply sigmoid to class scores
    prediction[:, :, 5:5 +
               num_classes] = torch.sigmoid((prediction[:, :, 5:5+num_classes]))

    # resize detectoin map to the size of the input image
    prediction[:, :, :4] *= stride

    return prediction

def write_results(prediction, confidence, num_classes, nms_conf=0.4):
    """
    Take predictions, prepare them, and then perform NMS on them
    """

    conf_mask = (prediction[:, :, 4] > confidence).float().unsqueeze(2)
    prediction = prediction * conf_mask  # if below confidence threshold, set to 0

    # create new tensor of same shape and value as prediction
    box_corner = prediction.new(prediction.shape)
    box_corner[:, :, 0] = (prediction[:, :, 0] - prediction[:, :, 2]/2)
    box_corner[:, :, 1] = (prediction[:, :, 1] - prediction[:, :, 3]/2)
    box_corner[:, :, 2] = (prediction[:, :, 0] + prediction[:, :, 2]/2)
    box_corner[:, :, 3] = (prediction[:, :, 1] + prediction[:, :, 3]/2)
    prediction[:, :, :4] = box_corner[:, :, :4]

    batch_size = prediction.size(0)
    write = False

    for ind in range(batch_size):
        image_pred = prediction[ind]  # individual image tensor

        # reduce to max class only instead of all classes
        # index and value, max along  dim 1 of (0,1) (max class)
        max_conf, max_conf_score = torch.max(image_pred[:, 5:5+num_classes], 1)
        max_conf = max_conf.float().unsqueeze(1)
        max_conf_score = max_conf_score.float().unsqueeze(1)
        # bounding box, class, class confidence
        seq = (image_pred[:, :5], max_conf, max_conf_score)
        image_pred = torch.cat(seq, 1)

        # remove zeroed bounding boxes from before
        non_zero_index = (torch.nonzero(image_pred[:, 4]))
        try:
            image_pred_ = image_pred[non_zero_index.squeeze(), :].view(-1, 7)
        except:
            continue  # if no detections happen

        if image_pred_.shape[0] == 0:
            continue  # if no detections happen

        # get various classes that were detected
        # -1 holds class index, find all unique class indexes
        img_classes = unique(image_pred_[:, -1])

        # perform NMS classwise
        for c in img_classes:  # for every class present in the detections

            # get the detections of the current class
            cls_mask = image_pred_ * \
                (image_pred_[:, -1] == c).float().unsqueeze(1)
            class_mask_index = torch.nonzero(cls_mask[:, -2]).squeeze()
            image_pred_class = image_pred_[class_mask_index].view(-1, 7)

            # sort detections such that the max objectness score is at the top
            conf_sort_index = torch.sort(
                image_pred_class[:, 4], descending=True)[1]
            image_pred_class = image_pred_class[conf_sort_index]
            idx = image_pred_class.size(0)  # num of detections

            # NMS
            for i in range(idx):
                # get all IoUs of the boxes after the current one in the loop
                # since we sorted bboxes by class confidence, any box with an index after the current
                # will have a smaller class confidence score
                try:
                    ious = bbox_iou(image_pred_class[i].unsqueeze(
                        0), image_pred_class[i+1:])
                except ValueError:
                    break
                except IndexError:
                    break

                # zero all detections with IoU > threshold
                iou_mask = (ious < nms_conf).float().unsqueeze(1)
                image_pred_class[i+1:] *= iou_mask

                # remove zero entries
                # here a zero entry is a bounding box that has too high an IoU with the current
                # bbox and since it is atleast i+1 indexed, it will have a lower confidence score
                non_zero_ind = torch.nonzero(image_pred_class[:, 4]).squeeze()
                image_pred_class = image_pred_class[non_zero_ind].view(-1, 7)

            # write results
            batch_ind = image_pred_class.new(
                image_pred_class.size(0), 1).fill_(ind)
            seq = batch_ind, image_pred_class

            if not write:
                output = torch.cat(seq, 1)
                write = True
            else:
                out = torch.cat(seq, 1)
                output = torch.cat((output, out))

    # try to return something, otherwise return a 0
    try:
        return output
    except Exception:
        return 0

def load_classes(namesFile):
    """
    Return a list of names from a class file (classnames separated by \n)
    """

    fp = open(namesFile, "r")
    names = fp.read().split("\n")[:-1]
    return names

def letterbox_image(img, inp_dim):
    """
    Resize image with unchanged aspect ratio using padding
    """

    img_w, img_h = img.shape[1], img.shape[0]
    w, h = inp_dim
    new_w = int(img_w * min(w/img_w, h/img_h))
    new_h = int(img_h * min(w/img_w, h/img_h))
    resized_img = cv2.resize(
        img, (new_w, new_h), interpolation=cv2.INTER_CUBIC)

    # fill a new array of size x, y, 3 with 128 values
    canvas = np.full((inp_dim[1], inp_dim[0], 3), 128)
    canvas[
        (h-new_h)//2:(h-new_h)//2 + new_h,
        (w-new_w)//2:(w-new_w)//2 + new_w,
        :
    ] = resized_img

    return canvas

def prep_image(img, inp_dim):
    """
    Convert image from openCV format to Pytorch format
    """

    img = (letterbox_image(img, (inp_dim, inp_dim)))
    img = img[:, :, ::-1].transpose(2, 0, 1).copy()
    img = torch.from_numpy(img).float().div(
        255.0).unsqueeze(0)  # convert to torch and normalise
    return img

def setController(buzz, pos, ser):
    """
    Sets servo angle to pos and buzzer state to buzz on serial port ser
    """
    s = "{0:03d}{1}".format(int(pos), buzz) # Add trailing zeros to make 3 digit number
    print(s)
    b = bytes(s, 'utf-8')

    ser.write(b) # Convert the decimal number to ASCII then send it to the Arduino
    #ser.readline() # block until arduino responds
    ser.flushInput()
    ser.flushOutput()


import json
import requests
import urllib
import config
import fractions
import logging
import time
from CFDCamperLogo import LOGO

url = config.URL
headers = config.HEADERS
auth = config.AUTH
UNIT = 10**8
FEE_PER_KB=20000 #Increase our chance of getting into the block on time
FEED_ADDRESS = '1E7qexXyupzYqx9R6rbUt2PciyTZ35MBDf' #Feed Address to Camp
MIN_TARGET_SIZE = 8 * UNIT #Minimum wager amount of target bet
MIN_ATTACK_DISTANCE = 900 #in seconds, time before next broadcast to start attack
MIN_ATTACK_SIZE = .75 #minimum normalized difference between broadcasts to attack for
SAFETY_CONSTANT= .9 #Adjust for how much the price might change between the matching attack and the broadcast block for small differences
SAFETY_RATIO = .75 #percent based adjustment for large movements
SAFETY_THRESHOLD = 6 #Threshold for switching between safety_constant and safety_ratio
MY_ADDRESSES = ['19r2xo2iZ8UzxxgvjVcXkVFw7GhypQLnxF','1A6pex2MMEqJtRdwGPwZmEQLJ7Jw65QCBz', '1GwusE6uPNpjNUxV3LoZCwEsdY93mbyoxp', '1FvrgmfKAr3Uc7KeACMk6DFwbJggXR8FxV', '1CFD35UfrKn92rnHQZj213Be9qjBssEEr3']#['mhtp9Fpt1Citcfhua34DrhZQMTwMF2qS7V', 'moSG49871W5D1CmscFJjJgYyH2BnFwLpJD', 'mtAMogTagoRwJkcDxduBY8iMnxxehofqrX', 'n4No9RNhAGmkJT54LmcsenagfrGXbvm8r6', 'msRd43qcfz7vfmRAJks2f58caiSzS94Hoa', 'mrwDQYWweppb7siEDm7w2sbjpBDWX7gEJm']
LAST_BROADCAST = None
LAST_BROADCAST_TIMESTAMP= None
CAST_INTERVAL = 7200 #Either gotten from enhanced info or established manually
BET_TYPE_NAME = {0: 'Bull', 1: 'Bear'}
USE_LOCAL_TIMESTAMP = True #use Timestamp from when the broadcast is received, or the one contained in the broadcast

F = fractions.Fraction

logging.basicConfig(
    level = logging.INFO,
    format = '%(asctime)s %(levelname)s %(message)s',
)
class LoopyAccessList(list):
    def __init__(self, *args, **kwargs):
        list.__init__(self, *args)
        self.counter = 0
    def get(self, ):
        self.counter += 1
        key = self.counter % len(self)
        return self[key - 1]

MY_ADDRESSES = LoopyAccessList(MY_ADDRESSES)

def qNum(num_items):
   return ','.join('?' for i in range(num_items))

    #Don't attack you're own open wagers that didn't match
def getOpenBets():
    bindings = [FEED_ADDRESS] + MY_ADDRESSES
    query = 'SELECT * FROM bets WHERE (status == "open") AND (feed_address == ?) AND (source NOT IN (%s))' %(qNum(len(MY_ADDRESSES)))
    payload = {'method': 'sql', 'params': {'query': query, 'bindings': bindings}, 'jsonrpc': '2.0', 'id': 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response['result']
def trim(x):
    return round(x / 10**8, 4)

def getLastBroadcast():
    bindings = [FEED_ADDRESS]
    query= 'SELECT * FROM broadcasts WHERE (status = "valid" AND source = ?) ORDER BY tx_index DESC'
    payload = {'method': 'sql', 'params': {'query': query, 'bindings': bindings}, 'jsonrpc': '2.0', 'id': 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response['result'][0]

#for Estimating the value of the next broadcast #Must be tailored to the specific Feed
def getRealTimePrice():
    price, timestamp = bitstampTicker()  #ticker to Use
    return price

def btcTicker():
    data = json.loads(urllib.request.urlopen('https://api.coindesk.com/v1/bpi/currentprice.json').read().decode('utf-8'))
    dataPrice= float(data['bpi']['USD']['rate'])
    dataTime = data['time']['updated']
    return dataPrice, dataTime

def bitstampTicker():
    data = json.loads(urllib.request.urlopen('https://www.bitstamp.net/api/ticker/').read().decode('utf-8'))
    dataPrice = float(data['last'])
    dataTime = data['timestamp']
    return dataPrice, dataTime

def doBet(params):
    default_params = {'allow_unconfirmed_inputs': True, 'target_value':0}
    params.update(default_params)
    payload = { "method": "do_bet", "params": params, "jsonrpc": "2.0", "id": 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response

def getRunningInfo():
    payload = { "method": "get_running_info", "params": {}, "jsonrpc": "2.0", "id": 0}
    response = requests.post(url, data=json.dumps(payload), headers=headers, auth=auth).json()
    return response['result']

print(LOGO)
STARTUP = True
target_tracked = {}
while True:
    if not STARTUP:
        logging.info("sleeping for 120 seconds")
        time.sleep(120)
    STARTUP= False
    try:
        current_block = getRunningInfo()['last_block']['block_index']
    except:
        logging.info("Could not connect to Counterpartyd RPC")
        continue
    last_broadcast = getLastBroadcast()
    if not LAST_BROADCAST or (last_broadcast['tx_hash'] != LAST_BROADCAST['tx_hash']):
        LAST_BROADCAST = last_broadcast
        initial_value = LAST_BROADCAST['value']
        if USE_LOCAL_TIMESTAMP and LAST_BROADCAST_TIMESTAMP:
            LAST_BROADCAST_TIMESTAMP = time.time()
        else:
            LAST_BROADCAST_TIMESTAMP = last_broadcast['timestamp']
        logging.info("Added New Broadcast, Value: {0}, Time: {1}".format(initial_value, time.ctime(LAST_BROADCAST_TIMESTAMP)))
    current_time = time.time()
    estimate_till_next_broadcast = (CAST_INTERVAL - (current_time - LAST_BROADCAST_TIMESTAMP ))
    logging.info("Estimated time till next broadcast: {}".format(estimate_till_next_broadcast))
    if estimate_till_next_broadcast > MIN_ATTACK_DISTANCE: continue
    open_bets  = getOpenBets()
    logging.info("Found {0} open bets at Feed {1}".format(len(open_bets), FEED_ADDRESS))
    if not open_bets: continue
    target_bets = {}
    for bet in open_bets:
        if bet['wager_remaining'] < MIN_TARGET_SIZE: continue
        if not bet['bet_type'] in [0,1]: continue
        if (bet['counterwager_quantity'] / bet['wager_quantity']) > 5: continue
        bet_expire_remaining = bet['expiration'] - (current_block - bet['block_index'])
        if (bet_expire_remaining - 1) <  (estimate_till_next_broadcast / (60 * 10)): continue
        target_bets[bet['tx_hash']] = bet
    if not target_bets: continue
    logging.info("Found {0} targets at Feed {1}".format(len(open_bets), FEED_ADDRESS))
    current_price = getRealTimePrice()
    price_difference = current_price - initial_value
    if SAFETY_CONSTANT< price_difference < SAFETY_THRESHOLD:
        safe_difference = (price_difference - SAFETY_CONSTANT)
    elif -SAFETY_THRESHOLD < price_difference < -SAFETY_CONSTANT:
        safe_difference = (price_difference + SAFETY_CONSTANT)
    elif -SAFETY_CONSTANT< price_difference < SAFETY_CONSTANT:
        safe_difference = 0
    else:
        safe_difference = (price_difference * SAFETY_RATIO)
    logging.info("Initital Value {0}, Real time price {1}, Price difference {2}, Estimated safe difference {3}".format(initial_value, current_price, price_difference, safe_difference))
    if abs(safe_difference) < MIN_ATTACK_SIZE: continue
    for target in target_bets.values():
        if (target['bet_type'] == 0 and safe_difference > 0) or (target['bet_type'] == 1 and safe_difference < 0):
            logging.info("Target type {0}, Estimated Safe Difference {1} --Skipping".format(BET_TYPE_NAME[target['bet_type']], safe_difference))
            continue
        else:
            logging.info("Target type {0}, Estimated Safe Difference {1} --Targetting".format(BET_TYPE_NAME[target['bet_type']], safe_difference))
        try:
            if (time.time() - target_tracked[target['tx_hash']]['timestamp']) < 20*60: pass
            else: raise Exception
        except:
            target_tracked[target['tx_hash']] = {'timestamp': time.time(), 'estimated_wager_remaining': target['wager_remaining']}
        print("target_wager_remaining %s" %target_tracked[target['tx_hash']]['estimated_wager_remaining'])
        while target_tracked[target['tx_hash']]['estimated_wager_remaining'] >= 0:
            time.sleep(2)
            target_inverse_odds = F(target['counterwager_quantity']/ target['wager_quantity'])
            normalized_leverage = target['leverage']/ 5040.0
            if target_tracked[target['tx_hash']]['estimated_wager_remaining'] > abs(safe_difference * UNIT * normalized_leverage):
                wager_quantity = abs(round((safe_difference * UNIT) * target_inverse_odds * normalized_leverage))
                counterwager_quantity = abs(round((safe_difference * UNIT * normalized_leverage)))
            else:
                wager_quantity = abs(round(target_tracked[target['tx_hash']]['estimated_wager_remaining'] * target_inverse_odds * normalized_leverage))
                counterwager_quantity = abs(round(target_tracked[target['tx_hash']]['estimated_wager_remaining'] * normalized_leverage))
            leverage = target['leverage']
            deadline = target['deadline']
            bet_type = 0 if target['bet_type'] == 1 else 1
            expiration = 2
            source= MY_ADDRESSES.get()
            params = {'fee_per_kb': FEE_PER_KB, 'source': source, 'expiration': expiration, 'bet_type': bet_type, 'deadline': deadline, 'leverage': leverage, 'counterwager_quantity': counterwager_quantity, 'wager_quantity': wager_quantity, 'feed_address': FEED_ADDRESS}
            print(params)
            try:
                print('trying')
                tx_hash = doBet(params)['result']
            except:
                print('EXCEPT')
                continue
            if tx_hash:
                target_tracked[target['tx_hash']]['estimated_wager_remaining'] = target_tracked[target['tx_hash']]['estimated_wager_remaining'] - wager_quantity
                logging.info("ATTACK: Target Address {0}, Estimated wager_remaining {1}, Actual wager_remaining {2}, Wager_quantity {3}".format(target['source'], trim(target_tracked[target['tx_hash']]['estimated_wager_remaining']), trim(target['wager_remaining']), trim(wager_quantity)))
                logging.info("ATTACK: HASH {}".format(tx_hash))


from mrjob.job import MRJob
from mrjob.step import MRStep
import numpy as np
import pickle
from sklearn.neighbors import KNeighborsClassifier as KNC
import csv
import re

# Helper function to read SKLearn KNN model objects
def open_pickle(filename):
    f = open(filename, 'rb')
    ob = pickle.load(f)
    f.close()
    return ob

KNN = [("five", open_pickle("5_neighbors_model.obj")),
    ("ten", open_pickle("10_neighbors_model.obj")),
    ("fifteen", open_pickle("15_neighbors_model.obj")),
    ("twenty", open_pickle("20_neighbors_model.obj"))]

class KNNSuccesses(MRJob):

    def mapper(self, _, line):
        try:
            var_list = [float(x) for x in line.strip().split(',')][1:]
            true_val = var_list[-1]
            if (0 > true_val > -1): # Unstandardizes data
                true_val = 1
            elif (2 > true_val > 1):
                true_val = 2
            elif (4 > true_val > 3):
                true_val = 3
            elif (5 > true_val > 6):
                true_val = 4
            else:
                true_val = 1
            for model in KNN:
                yield model[0], model[1].score(np.array([var_list[0:-1]]), np.array([true_val]))
            yield "lines", 1
        except:
            yield 'h', 1

    def reducer(self, model, count):
        yield model, sum(count)

if __name__ == '__main__':
    KNNSuccesses.run()


import os
import sys
from pathlib import Path
from typing import Callable, Literal

from typing_extensions import ParamSpec

WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")

def user_cache_dir(appname: str) -> Path:
    r"""
    Return full path to the user-specific cache dir for this application.
        "appname" is the name of application.
    Typical user cache directories are:
        macOS:      ~/Library/Caches/<AppName>
        Unix:       ~/.cache/<AppName> (XDG default)
        Windows:    C:\Users\<username>\AppData\Local\<AppName>\Cache
    On Windows the only suggestion in the MSDN docs is that local settings go
    in the `CSIDL_LOCAL_APPDATA` directory. This is identical to the
    non-roaming app data dir (the default returned by `user_data_dir`). Apps
    typically put cache data somewhere *under* the given dir here. Some
    examples:
        ...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
        ...\Acme\SuperApp\Cache\1.0
    OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
    """
    if WINDOWS:
        return _get_win_folder("CSIDL_LOCAL_APPDATA") / appname / "Cache"
    elif sys.platform == "darwin":
        return Path("~/Library/Caches").expanduser() / appname
    else:
        return Path(os.getenv("XDG_CACHE_HOME", "~/.cache")).expanduser() / appname

def user_data_dir(appname: str, roaming: bool = False) -> Path:
    r"""
    Return full path to the user-specific data dir for this application.
        "appname" is the name of application.
            If None, just the system directory is returned.
        "roaming" (boolean, default False) can be set True to use the Windows
            roaming appdata directory. That means that for users on a Windows
            network setup for roaming profiles, this user data will be
            sync'd on login. See
            <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
            for a discussion of issues.
    Typical user data directories are:
        macOS:                  ~/Library/Application Support/<AppName>
        Unix:                   ~/.local/share/<AppName>    # or in
                                $XDG_DATA_HOME, if defined
        Win XP (not roaming):   C:\Documents and Settings\<username>\ ...
                                ...Application Data\<AppName>
        Win XP (roaming):       C:\Documents and Settings\<username>\Local ...
                                ...Settings\Application Data\<AppName>
        Win 7  (not roaming):   C:\Users\<username>\AppData\Local\<AppName>
        Win 7  (roaming):       C:\Users\<username>\AppData\Roaming\<AppName>
    For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
    That means, by default "~/.local/share/<AppName>".
    """
    if WINDOWS:
        const = "CSIDL_APPDATA" if roaming else "CSIDL_LOCAL_APPDATA"
        return Path(_get_win_folder(const)) / appname
    elif sys.platform == "darwin":
        return Path("~/Library/Application Support/").expanduser() / appname
    else:
        return Path(os.getenv("XDG_DATA_HOME", "~/.local/share")).expanduser() / appname

def user_config_dir(appname: str, roaming: bool = True) -> Path:
    """Return full path to the user-specific config dir for this application.
        "appname" is the name of application.
            If None, just the system directory is returned.
        "roaming" (boolean, default True) can be set False to not use the
            Windows roaming appdata directory. That means that for users on a
            Windows network setup for roaming profiles, this user data will be
            sync'd on login. See
            <http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
            for a discussion of issues.
    Typical user data directories are:
        macOS:                  same as user_data_dir
        Unix:                   ~/.config/<AppName>
        Win *:                  same as user_data_dir
    For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
    That means, by default "~/.config/<AppName>".
    """
    if WINDOWS:
        return user_data_dir(appname, roaming=roaming)
    elif sys.platform == "darwin":
        return user_data_dir(appname)
    else:
        return Path(os.getenv("XDG_CONFIG_HOME", "~/.config")).expanduser() / appname

# -- Windows support functions --
def _get_win_folder_from_registry(
    csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"]
) -> Path:
    """
    This is a fallback technique at best. I'm not sure if using the
    registry for this guarantees us the correct answer for all CSIDL_*
    names.
    """
    import winreg

    shell_folder_name = {
        "CSIDL_APPDATA": "AppData",
        "CSIDL_COMMON_APPDATA": "Common AppData",
        "CSIDL_LOCAL_APPDATA": "Local AppData",
    }[csidl_name]

    key = winreg.OpenKey(
        winreg.HKEY_CURRENT_USER,
        r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders",
    )
    directory, _type = winreg.QueryValueEx(key, shell_folder_name)
    return Path(directory)

def _get_win_folder_with_ctypes(
    csidl_name: Literal["CSIDL_APPDATA", "CSIDL_COMMON_APPDATA", "CSIDL_LOCAL_APPDATA"]
) -> Path:
    csidl_const = {
        "CSIDL_APPDATA": 26,
        "CSIDL_COMMON_APPDATA": 35,
        "CSIDL_LOCAL_APPDATA": 28,
    }[csidl_name]

    buf = ctypes.create_unicode_buffer(1024)
    ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)

    # Downgrade to short path name if have highbit chars. See
    # <http://bugs.activestate.com/show_bug.cgi?id=85099>.
    has_high_char = any(ord(c) > 255 for c in buf)
    if has_high_char:
        buf2 = ctypes.create_unicode_buffer(1024)
        if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
            buf = buf2

    return Path(buf.value)

if WINDOWS:
    try:
        import ctypes

        _get_win_folder = _get_win_folder_with_ctypes
    except ImportError:
        _get_win_folder = _get_win_folder_from_registry

P = ParamSpec("P")

APP_NAME = "meme_generator"
BASE_CACHE_DIR = user_cache_dir(APP_NAME).resolve()
BASE_CONFIG_DIR = user_config_dir(APP_NAME).resolve()
BASE_DATA_DIR = user_data_dir(APP_NAME).resolve()

def _ensure_dir(path: Path) -> None:
    if not path.exists():
        path.mkdir(parents=True, exist_ok=True)
    elif not path.is_dir():
        raise RuntimeError(f"{path} is not a directory")

def _auto_create_dir(func: Callable[P, Path]) -> Callable[P, Path]:
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> Path:
        path = func(*args, **kwargs)
        _ensure_dir(path)
        return path

    return wrapper

@_auto_create_dir
def get_cache_dir() -> Path:
    return BASE_CACHE_DIR

def get_cache_file(filename: str) -> Path:
    return get_cache_dir() / filename

@_auto_create_dir
def get_config_dir() -> Path:
    return BASE_CONFIG_DIR

def get_config_file(filename: str) -> Path:
    return get_config_dir() / filename

@_auto_create_dir
def get_data_dir() -> Path:
    return BASE_DATA_DIR

def get_data_file(filename: str) -> Path:
    return get_data_dir() / filename


"""
Diagonal traversal
"""
from typing import List
import collections

class Solution:
    def findDiagonalOrder_i(self, matrix: [List[List[int]]]) -> List[int]:
        entries = [(i + j, (j, i)[(i ^ j) & 1], val)
                   for i, row in enumerate(matrix)
                   for j, val in enumerate(row)]
        return [e[2] for e in sorted(entries)]

    def findDiagonalOrder(self, matrix: [List[List[int]]]) -> List[int]:
        def sorter(xy):
            x, y = xy
            return (x + y, x if (x + y) % 2 else y)

        m, n = len(matrix), len(matrix[0])
        indices = sorted(((i, j) for i in range(m) for j in range(n)),
                         key=sorter)
        return [matrix[i][j] for i, j in indices]

    def findDiagonalOrder_dd(self, matrix: [List[List[int]]]) -> List[int]:
        result = []
        dd = collections.defaultdict(list)
        for i, row in enumerate(matrix):
            for j, val in enumerate(row):
                dd[i + j].append(val)
        for k in sorted(dd.keys()):
            diag = dd[k] if k % 2 else reversed(dd[k])
            result.extend(diag)
        return result

def test():
    sol = Solution()
    cases = [
        [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    ]
    for case in cases:
        print(sol.findDiagonalOrder(case))

if __name__ == '__main__':
    test()


import warnings

from openpyxl import load_workbook

# dobim količino iz columna b

def array_kolicina():
    warnings.simplefilter("ignore")
    wb = load_workbook(filename="C:/Podatki/newZlozenke.xlsx", read_only=True)
    ws = wb['Podatki']
    myArray = []
    for row in ws.iter_rows('C2:C20477'):
        for cell in row:
            myArray.append(cell.value)
    return myArray


import streamlit as st
import leafmap.foliumap as leafmap

st.set_page_config(layout="wide")

# Customize page title
st.title("Welcome to Waterpix")
st.subheader("Let's inspect your water in every pixel!")

st.header("How to check water quality in your area of interest?")

markdown = """
1. Check if all areas are added to your account,
2. Get familiar with meaning of each index,
3. Check what are the newest values and how they change within your area,
4. If you need it, use timeline to see how water quality changed in time.
"""

st.markdown(markdown)

st.subheader("Your Areas of Interest")
m = leafmap.Map(minimap_control=True, center=[49.73907, 20.68443], zoom=12)
m.add_geojson(
    "https://magisterkacog.blob.core.windows.net/aois/jezioro_roznowskie.json"
)
m.to_streamlit(height=500)


__author__ = 'Chris'
import serial
from time import sleep
import logging
import math
#######
# Object handles requesting and parsing weather info from an Arduino
# Init with serial port (IE: /dev/ttyACM0), baud rate, and timeout
#

class Arduino(object):

    def __init__(self, port, speed, timeout):
        self.port = port
        self.speed = speed
        self.timeout = timeout
        self.serial = serial.Serial()
        self.serial.port = self.port
        self.serial.baudrate = self.speed
        self.serial.timeout = self.timeout
        self.logger = logging.getLogger(__name__)
        self.logger.info("Arduino Instantiated.")

        # Nodes
        self.node1 = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.node2 = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.node3 = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.lacrosse = {'stationid': '', 'temp': '', 'rh': '', 'windspeed': '', 'winddir': 0, 'rainfall': 0,
                         'dewpoint': 0, 'humidex': 0}

        # store previous value
        self.node1last = dict(self.node1)
        self.node2last = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.node3last = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}
        self.nodelast = {'address': '', 'uptime': '', 'temp': '', 'vin': '', 'vbat': '', 'age': '', 'error': True}

    def validate(self, node):
        # allow 30% difference between readings
        allowedVariance = .3
        low = 1 - allowedVariance
        high = 1 + allowedVariance

        # minimum and maximum values - anything outside these values is an error
        vMin = 0
        vMax = 1000
        tempMin = -50
        tempMax = 50
        ageMax = 700

        # check for allowed min/max range
        self.logger.debug("Checking min/maxes (temp/vin/vbat/age): "+str(node['temp']) +
                          "/"+str(node['vin'])+"/"+str(node['vbat'])+"/"+str(node['age']))
        if (node['temp'] < tempMin) or (node['temp'] > tempMax):
            self.logger.debug("Temp out of range")
            return False

        if (node['vin'] < vMin or node['vin'] > vMax or node['vbat'] < vMin or node['vbat'] > vMax):
            self.logger.debug("vBat or vIn out of range")
            return False

        if node['age'] > ageMax:
            self.logger.debug("Age out of range")
            return False

        # check for allowed variances
        self.logger.debug("validating address: " + str(node['address']))
        address = node['address']
        if (address == 1):
            self.logger.debug("Node1last")
            nodelast = dict(self.node1last)
        elif (address == 2):
            self.logger.debug("Node2last")
            nodelast = dict(self.node2last)
        elif (address == 3):
            self.logger.debug("Node3last")
            nodelast = dict(self.node3last)
        else:
            return False

        # on first run, nodelast will be empty - we will assume that it's valid.
        self.logger.debug("Nodelast: " + str(nodelast['temp']))
        if (nodelast['address'] == ''):
            self.logger.debug("First run, return true")
            if (address == 1):
                self.node1last = dict(node)
            elif (address == 2):
                self.node2last = dict(node)
            elif (address == 3):
                self.node3last = dict(node)
            return True

        oldTemp = nodelast['temp']
        oldVcc = nodelast['vin']
        oldVbat = nodelast['vbat']
        newTemp = node['temp']
        newVcc = node['vin']
        newVbat = node['vbat']

        self.logger.debug("Checking allowed variance range of: " + str(oldTemp*high)+"<"+str(newTemp)+"<"+str(oldTemp*low))
        if (newTemp < oldTemp*low or newTemp > oldTemp*high):
            self.logger.debug("Temp failure: " + str(oldTemp*high)+"<"+str(newTemp)+"<"+str(oldTemp*low))
            return False

        if (newVbat < oldVbat*low or newVbat > oldVbat*high):
            self.logger.debug("vbat failure: " + str(oldVbat*high)+"<"+str(newVbat)+"<"+str(oldVbat*low))
            return False

        if (newVcc < oldVcc*low or newVcc > oldVcc*high):
            self.logger.debug("vcc failure: " + str(oldVcc*high)+"<"+str(newVcc)+"<"+str(oldVcc*low))
            return False

        self.logger.debug("Variance ranges passed")
        # all conditions passed - update nodelast and return true
        if (address == 1):
            self.node1last = dict(node)
        elif (address == 2):
            self.node2last = dict(node)
        elif (address == 3):
            self.node3last = dict(node)

        self.logger.debug("All tests passed!")
        return True

    def open(self):
        if not self.serial.isOpen():
            self.logger.debug("Opening serial connection")
            self.serial.open()

            # when opening a connection, arduino returns garbage first
            # flush it, and ignore the output
            self.serial.write(b'|')
            self.serial.readline()
            self.serial.flushInput()
            self.logger.debug("Opened")

    def isOpen(self):
        return self.serial.isOpen()

    def close(self):
        if self.serial.isOpen():
            self.serial.close()

    def writeSerial(self, value):
        # make sure port is open
        if not self.isOpen():
            self.logger.debug("Port closed")
            self.open()

        #flush buffer
        self.serial.write(b'|')
        self.serial.readline()

        sleep(0.1)
        self.serial.flushInput()

        self.logger.debug("Writing value: " + value)
        self.serial.write(bytearray(value,'utf-8'))
        self.serial.write(b'|')

        result = self.serial.readline()
        self.logger.debug("Result: \n\t" + result)
        self.close()
        return result

    def calcDewPoint(self, RH, temp):
        dewpoint = math.pow((RH/100.0), 0.125)
        self.logger.debug("Dewpoint step 1: " + str(dewpoint))
        dewpoint = dewpoint * (112+0.9*temp) + (0.1 * temp) - 112
        self.logger.debug("Dewpoint step 2: " + str(dewpoint))
        return round(dewpoint)

    def calcHumidex(self, dewpoint, temp):
        dewK = dewpoint + 273.15
        exponent = 5417.7530 * (1/273.16 - 1/dewK)
        humidex = temp + 0.5555 * (6.11 * math.pow(math.e, exponent) - 10)
        return round(humidex,1)

    def readValue(self, node):
        if (node == "node1"):
            result = self.writeSerial("node1")

            self.logger.info("Node 1 Received.")

            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.node1['address'] = int(resultList[0])
                self.node1['uptime'] = int(resultList[1])
                if resultList[2] == "ovf":
                        # temp probe error
                    self.node1['temp'] = -100
                else:
                    self.node1['temp'] = float(resultList[2])
                self.node1['vin'] = int(resultList[3])
                self.node1['vbat'] = int(resultList[4])
                self.node1['age'] = int(resultList[5])

                if self.validate(self.node1) is False:
                    self.logger.error("Node 1 out of range - data old or invalid")
                    self.logger.error("Value:\n" + str(self.node1))
                    self.node1['error'] = True
                else:
                    self.node1["error"] = False
                self.logger.debug("Node1: \n" + str(self.node1) + "\n\n")
            except:
                self.node1["error"] = True
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("node1")

        elif (node == "node2"):
            result = self.writeSerial("node2")

            self.logger.info("Node 2 Received.")

            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.node2['address'] = int(resultList[0])
                self.node2['uptime'] = int(resultList[1])
                if resultList[2] == "ovf":
                    # temp probe error
                    self.node2['temp'] = -100
                else:
                    self.node2['temp'] = float(resultList[2])
                self.node2['vin'] = int(resultList[3])
                self.node2['vbat'] = int(resultList[4])/2
                self.node2['age'] = int(resultList[5])

                if self.validate(self.node2) is False:
                    self.logger.error("Node 2 out of range - data old or invalid")
                    self.logger.error("Value:\n" + str(self.node2))
                    self.node2['error'] = True
                else:
                    self.node2["error"] = False
                self.logger.debug("node2: \n" + str(self.node2) + "\n\n")
            except:
                self.node2["error"] = True
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("node2")

        elif (node == "node3"):
            result = self.writeSerial("node3")

            self.logger.info("Node 3 Received.")

            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.node3['address'] = int(resultList[0])
                self.node3['uptime'] = int(resultList[1])
                if resultList[2] == "ovf":
                    # temp probe error
                    self.node3['temp'] = -100
                else:
                    self.node3['temp'] = float(resultList[2])
                self.node3['vin'] = int(resultList[3])
                self.node3['vbat'] = int(resultList[4])
                self.node3['age'] = int(resultList[5])

                if self.validate(self.node3) is False:
                    self.logger.error("Node 3 out of range - data old or invalid")
                    self.logger.error("Value:\n" + str(self.node3))
                    self.node3['error'] = True
                else:
                    self.node3["error"] = False
                self.logger.debug("node3: \n" + str(self.node3) + "\n\n")
            except:
                self.node3["error"] = True
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("node3")

        elif (node == "lacrosse"):
            result = self.writeSerial("lacrosse")

            self.logger.info("LaCrosse Received.")
            try:
                result.strip('\n\r')
                resultList = result.split(',')
                self.lacrosse["stationid"] = int(resultList[0])
                self.lacrosse["temp"] = float(resultList[1])/10.0
                self.lacrosse["rh"] = int(resultList[2])
                self.lacrosse["windspeed"] = float(resultList[3])/10.0
                self.lacrosse["winddir"] = int(resultList[4])
                self.lacrosse["rainfall"] = int(resultList[5])
                self.logger.debug("calculating dewpoint")
                self.lacrosse["dewpoint"] = self.calcDewPoint(self.lacrosse["rh"], self.lacrosse["temp"])
                self.logger.debug("calculating humidex")

                # only calculate humidex is RH is above 30%
                self.lacrosse["humidex"] = self.calcHumidex(self.lacrosse["dewpoint"], self.lacrosse["temp"])
                self.logger.debug("lacrosse: \n" + str(self.lacrosse) + "\n\n")
            except:
                self.logger.error("Invalid Response Received")
                self.logger.error("Response: \n" + result)
                # send | and try again
                self.logger.error("Trying again")
                self.writeSerial(b'|')
                self.readValue("lacrosse")

    def updateAll(self):
        self.logger.debug("Updating all nodes")

        self.readValue("node1")
        sleep(0.1)
        self.readValue("node2")
        sleep(0.1)
        self.readValue("node3")
        sleep(0.1)
        self.readValue("lacrosse")

    def printCurrent(self):
        self.writeSerial('|')
        print(self.writeSerial('current'))
        print(self.serial.readlines())

class Pressure(object):

    def __init__(self, port, speed, timeout):
        self.port = port
        self.speed = speed
        self.timeout = timeout
        self.serial = serial.Serial()
        self.serial.port = self.port
        self.serial.baudrate = self.speed
        self.serial.timeout = self.timeout
        self.logger = logging.getLogger(__name__)
        self.logger.info("Pressure Instantiated.")

        self.pressure = ''

    def open(self):
        if not self.serial.isOpen():
            self.logger.debug("Opening serial connection")
            self.serial.open()
            self.logger.debug("Opened")

    def close(self):
        if self.serial.isOpen():
            self.serial.close()

    def writeSerial(self, value):
        # make sure port is open
        if not self.serial.isOpen():
            self.logger.debug("Port closed")
            self.open()

        self.serial.flushInput()

        self.logger.debug("Writing value: " + value)
        self.serial.write(bytearray(value, 'utf-8'))

        sleep(0.1)
        self.serial.readline()

        sleep(0.1)
        result = self.serial.readline()
        self.logger.debug("Result: \n\t" + result)
        self.close()
        return result

    def getPressure(self):
        self.pressure = float(self.writeSerial("pres"))
        return self.pressure


from os import listdir
import re
from skimage.io import imread
import numpy as np
import hashlib
def searchKey(name):
    number = int(re.search(r'[0-9]{1,10}',name).group(0))
    return number

def searchKeyM(line):
    number = int(re.search(r'[0-9]{1,10}',line[0]).group(0))
    return number

foldername = './test_b'
filenames = listdir(foldername)
bigDict = {}
for name in filenames:
    if name[len(name) - 3:len(name)] == 'png':
        histogram = [0] * 3 * 256
        im = imread(foldername+'/'+name)
        for i in range(im.shape[0]):
            for j in range(im.shape[1]):
                if (im[i,j] != np.array([255,255,255], dtype = np.uint8)).any():
                    histogram[int(im[i,j,0])] = histogram[int(im[i,j,0])] + 1
                    histogram[int(im[i,j,1]) + 256] = histogram[int(im[i,j,1]) + 256] + 1
                    histogram[int(im[i,j,2]) + 512] = histogram[int(im[i,j,2]) + 512] + 1
        keyTmp = ''.join(str(e) for e in histogram)
        bigDict.setdefault(keyTmp,[]).append(name)

result = []
tmp = list(bigDict.values())
for i in range(len(tmp)):
    result.append(sorted(tmp[i],key = searchKey))
result = sorted(result,key = searchKeyM)
for i in range(len(result)):
    result[i] = ' '.join(e for e in result[i])
result = '\n'.join(e for e in result)
result = result + '\n'
m = hashlib.sha256()
m.update(result.encode('utf-8'))
res = m.hexdigest()
print(res)


import asyncio
from dataclasses import dataclass
from typing import List

from clients.tg.api import TgClient
from clients.fapi.tg import TgClientWithFile
from clients.fapi.s3 import S3Client
from clients.tg.dcs import UpdateObj, File
from bot.utils import log_exceptions

@dataclass
class WorkerConfig:
    endpoint_url: str
    aws_secret_access_key: str
    aws_access_key_id: str
    bucket: str
    concurrent_workers: int = 1

class Worker:
    def __init__(self, token: str, queue: asyncio.Queue, config: WorkerConfig):
        self.queue = queue
        self.config = config
        self.run = True
        # обязательный параметр, в него нужно сохранить запущенные корутины воркера
        self._tasks: List[asyncio.Task] = []
        # обязательный параметр, выполнять работу с s3 нужно через объект класса self.s3
        # для загрузки файла нужно использовать функцию fetch_and_upload или stream_upload
        self.s3 = S3Client(
            endpoint_url=config.endpoint_url,
            aws_secret_access_key=config.aws_secret_access_key,
            aws_access_key_id=config.aws_access_key_id
        )
        self.tg = TgClient(token)
        self.ftg = TgClientWithFile(token)
        self.is_first_msg = True

    @log_exceptions
    async def handle_update(self, upd: UpdateObj):
        """
        в этом методе должна происходить обработка сообщений и реализация бизнес-логики
        бизнес-логика бота тестируется с помощью этого метода, файл с тестами tests.bot.test_worker::TestHandler
        """
        if self.is_first_msg:
            self.is_first_msg = False
            await self.tg.send_message(upd.message.chat.id, '[greeting]')
        else:
            if not upd.message.document:
                await self.tg.send_message(upd.message.chat.id, '[document is required]')
            else:
                await self.tg.send_message(upd.message.chat.id, '[document]')
                raise Exception('a;a;a')
                file: File = await self.ftg.get_file(upd.message.document.file_id)
                await self.ftg.download_file(file.file_path, file.file_unique_id)
                await self.s3.stream_file(self.config.bucket, upd.message.document.file_name, file.file_unique_id)

                await self.tg.send_message(upd.message.chat.id, '[document has been saved]')

    @log_exceptions
    async def _worker(self):
        """
        должен получать сообщения из очереди и вызывать handle_update
        """
        while self.run:
            msg = await self.queue.get()
            await self.handle_update(msg)

    def start(self):
        """
        должен запустить столько воркеров, сколько указано в config.concurrent_workers
        запущенные задачи нужно положить в _tasks
        """
        self.run = True
        self._tasks = [asyncio.create_task(self._worker()) for _ in range(self.config.concurrent_workers)]

    @log_exceptions
    async def stop(self):
        """
        нужно дождаться пока очередь не станет пустой (метод join у очереди), а потом отменить все воркеры
        """
        self.run = False
        if not self.queue.empty():
            await self.queue.join()

        for task in self._tasks:
            task.cancel()

        await asyncio.sleep(0.1)


import os
from pathlib import Path

if os.environ.get("ROOT_DIR"):
    ROOT_PATH = "/root/insurance_bot"
else:
    ROOT_PATH = os.path.normpath(os.path.join(os.path.abspath(os.path.dirname(__file__)), ".."))

data_dir = os.path.join(ROOT_PATH, "data")

""" bert """
bert_data_path = Path.home()/'.pytorch_pretrained_bert'
bert_vocab_path = bert_data_path / 'bert-base-chinese-vocab.txt'
bert_model_path = bert_data_path / 'bert-base-chinese.tar.gz'

tencent_w2v_path = Path.home()/'.word2vec'


import rpyc
import os
import json
import sys
import signal

from rpyc.utils.server import ThreadedServer

file_name = "backup_json"

def int_handler(signal, frame):
    file_table = PrimaryBackUpService.exposed_BackUpServer.file_table

    # Putting the file_table on disk
    backup_json = open(file_name, 'w')
    json.dump(file_table, backup_json, ensure_ascii=False)

    sys.exit(0)

def read(fname):
    mapping = PrimaryBackUpService.exposed_BackUpServer.file_table[fname]
    return mapping

def loadFromFile():
    if os.path.isfile(file_name):
        print("local backup file found")
        PrimaryBackUpService.exposed_BackUpServer.file_table = json.load(open(file_name))

class PrimaryBackUpService(rpyc.Service):

    class exposed_BackUpServer():
        file_table = {}
        allChunkServers = {} #e.g. {"1":("127.0.0.1","8888"), "2":("127.0.0.1","8887")}
        primary_secondary_table = {}    # primary_secondary_table[primary_id] = [secondary_id_1, secondary_id_2]
                                        # e.g. {"1":["2","4"], "5":["3","6"]}

        def exposed_getFileTable(self):
            file_table_string = json.dumps(self.file_table)
            print("File table requested by MasterServer")
            return file_table_string
        def exposed_getAllChunkServers(self):
            allChunkServers_string = json.dumps(self.allChunkServers)
            print("allChunkServers requested by MasterServer")
            return allChunkServers_string
        def exposed_getPrimarySecondary(self):
            primary_secondary_table_string = json.dumps(self.primary_secondary_table)
            print("primary_secondary_table requested by MasterServer")
            return primary_secondary_table_string

        def exposed_updateFileTable(self, __file_table__):
            self.__class__.file_table = json.loads(__file_table__)
            print("File table is updated")
        def exposed_updateAllChunkServers(self, __allChunkServers__):
            self.__class__.allChunkServers = json.loads(__allChunkServers__)
            print("allChunkServers is updated")
        def exposed_updatePrimarySecondaryTable(self, __primary_secondary_table__):
            self.__class__.primary_secondary_table = json.loads(__primary_secondary_table__)
            print("primary_secondary_table is updated")

        ## master functions

        def exposed_get_file_table_entry(self, fname):
            # print("file_table in master:", self.__class__.file_table)
            if fname in self.__class__.file_table:
                return self.__class__.file_table[fname]
            else:
                return None

        def exposed_get_list_of_files(self):
            return list(self.__class__.file_table.keys())

        def exposed_get_primaryServers(self):
            primaryServers = {}
            for primary_id in list(self.__class__.primary_secondary_table.keys()):
                primaryServers[primary_id] = self.__class__.allChunkServers[primary_id]
            return primaryServers

        def exposed_get_secondaryServers(self, primary_id):
            secondaryServers = {}
            for secondary_id in self.__class__.primary_secondary_table[primary_id]:
                secondaryServers[secondary_id] = self.__class__.allChunkServers[secondary_id]
            return secondaryServers

        def exposed_get_chunkServers(self):
            return self.__class__.allChunkServers

if __name__ == "__main__":
    port = 8100 # master is told to connect to this number in master_b.py
    loadFromFile()
    signal.signal(signal.SIGINT, int_handler)
    print("Shadow Master is running on port", port)
    t = ThreadedServer(PrimaryBackUpService, port=port)
    t.start()


import re
import string
from nltk.corpus import stopwords
import random
import unittest
import os

"""Test cases for my code, use: python -m unittest test_file_name.py in terminal"""

class ClosedHashtable:

    def __init__(self):
        self.hashsize = 100
        self.table = [None]*self.hashsize
        self.stop_words = set(stopwords.words('english'))
        self.words = []

        # max of 50 words being searched for in the part 2 section due to k <= 50, hence hashtablesize of 50
        # Yes, it doesnt include all the words within the textfile, but it only includes the exact amount words necessary

        self.p2_hashsize = 50
        self.p2_table = [None]*self.p2_hashsize

    def fileloader(self, filepath):
        self.root = None
        words = self.parse_file(filepath)
        for word in words:
            self.words.append(word)

    def parse_file(self, filepath):
        with open(filepath, 'r') as f:
            textfile = f.readlines()
            words = []
            for line in textfile:
                # textfile is now a string of the text file
                line = line.strip().lower()
                # seperated each line via periods
                line = line.translate(
                    str.maketrans('', '', string.punctuation))
                # eliminated periods
                line = self.remove_stopwords(line)
                # got rid of redudent terms such as the, this, is ETC

                line = re.sub('\d+', '', line)
                line = re.sub(' +', ' ', line)
                # now put into list form
                if line:
                    words.extend(line.split(" "))
            return words

    def remove_stopwords(self, text):
        return " ".join([word for word in str(text).split() if word not in self.stop_words])

    def hashfunc(self, word):
        total = 0

        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.hashsize
        return total

    def add(self, words):
        for word in words:
            slot = self.hashfunc(word)
            start = slot
            insert = False
            while insert == False:

                if self.table[slot] is None:
                    self.table[slot] = [word, 1]
                    insert = True
                    break
                elif word == self.table[slot][0]:
                    self.table[slot][1] += 1
                    insert = True
                    break
                slot = (slot+1) % self.hashsize
                if slot == start:
                    insert = True
                    break
        print("CLOSED HASH TABLE p1 of size: ", self.hashsize)
        print("")
        print(self.table)
        print("")
        "---part2------------------------------------------------------"

    def p2_hashfunc(self, word):
        total = 0

        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.p2_hashsize
        return total

    def p2_add(self, words):
        for word in words:
            slot = self.p2_hashfunc(word)
            start = slot
            insert = False
            while insert == False:
                if self.p2_table[slot] is None:
                    self.p2_table[slot] = [word, 1]
                    insert = True
                    break
                elif word == self.p2_table[slot][0]:
                    self.p2_table[slot][1] += 1
                    insert = True
                    break
                slot = (slot+1) % self.p2_hashsize
                if slot == start:
                    insert = True
                    break
        print("---------------CLOSED HASH TABLE p2 of size: ", self.p2_hashsize)
        print("")
        print(self.p2_table)

    def p2_searchtest(self):
        # searching function for closded hash table
        # starts at step = 1, because im counting going to the hashing index as a step
        for k in range(5, 30, 5):
            print("")
            print("k =", k*2)
            print("")
            # list of words we will look for
            list = []
            # add k*2 words to list since in my loop k is half of the actual k
            for i in range(k):
                # allows me to add one word in the table and one word not in the table
                x = random.randint(0, len(self.p2_table)-1)
                list.append(self.p2_table[x][0])
                x = "notinlist"+str(i)
                list.append(x)
            # iterate through words in our list to find
            for word in list:
                slot = self.p2_hashfunc(word)
                print("searching for: ", word, ", starting at index: ", slot)
                start = slot
                found = False
                steps = 1
                while found == False:
                    if word == self.p2_table[slot][0]:
                        found = True
                        print(word, "is in the table, after", steps, "steps")
                        print("")
                        break
                    slot = (slot+1) % self.p2_hashsize
                    steps += 1
                    # if done full loop and not found, then not in table
                    if slot == start:
                        found = True
                        print(word, "is not in the table -1")
                        print("")
                        break

obje = ClosedHashtable()
obje.fileloader('D:/A3test.txt')
obje.add(obje.words)
obje.p2_add(obje.words)
obje.p2_searchtest()

"-----OPEN HASH TABLE----------------"

class Node:

    def __init__(self, data, freq):
        self.data = data
        self.next = None
        self.freq = freq

class OpenHashtable:

    def __init__(self,):
        # NOTE: while coding i changed some things to cater towards pt2, hence the seperate functions for p1 and p2
        # but im realizing now alot of it wasnt necessary, regardless it still follows the instructions correctly
        # just harder to read
        self.hashsize = 13
        self.hashtable = []
        self.stop_words = set(stopwords.words('english'))
        self.words = []

        # max amount of values being stored is 50, so having around 4 per bucket on avg seems optimum to avoid any values having their own bucket
        self.p2_hashsize = 13
        self.p2_hashtable = []
        self.p2_words = []

    def fileloader(self, filepath):
        self.root = None
        words = self.parse_file(filepath)
        for word in words:
            self.words.append(word)

    def parse_file(self, filepath):
        with open(filepath, 'r') as f:
            textfile = f.readlines()
            words = []
            for line in textfile:
                # textfile is now a string of the text file
                line = line.strip().lower()
                # seperated each line via periods
                line = line.translate(
                    str.maketrans('', '', string.punctuation))
                # eliminated periods
                line = self.remove_stopwords(line)
                # got rid of redudent terms such as the, this, is ETC

                line = re.sub('\d+', '', line)
                line = re.sub(' +', ' ', line)
                # now put into list form
                if line:
                    words.extend(line.split(" "))
            return words

    def remove_stopwords(self, text):
        return " ".join([word for word in str(text).split() if word not in self.stop_words])

    def hashfunc(self, word):
        # using same hash function through entirety of code as introduced by the slides in class
        total = 0
        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.hashsize

        return total

    def construct(self):
        for i in range(self.hashsize):
            add = Node('Head', 0)
            self.hashtable.append([add])
            # print(self.hashtable[i][0].data)

    # adds to hash table
    def add(self):
        for word in self.words:
            new_node = Node(word, 1)
            # get correct slot
            slot = self.hashfunc(word)
            # set current to the first node in the slot
            curr = self.hashtable[slot][0]
            done = False
            # if the first node is empty, add the new node
            while done == False:
                if curr.next == None and curr.data != new_node.data:
                    curr.next = new_node
                    done = True
                # add to word frequency if the word is already in the hashtable
                elif curr.data == new_node.data:
                    curr.freq += 1
                    done = True
                # traverse
                curr = curr.next

    # prints the hashtable

    def print(self):
        print("--------------OPEN HASHTABLE Using LinkedList Part 1-------")
        for i in range(self.hashsize):
            print("index", i)
            # set current to the first node in the slot
            curr = self.hashtable[i][0]
            # traverse
            list = []
            while curr != None:
                x = curr.data, curr.freq
                list.append(x)
                # print(curr.data, curr.freq)
                curr = curr.next
            print(list)

    """ -------------------- PART 2 for open ------------------------------------------------------------"""

    def construct2(self):
        for i in range(self.p2_hashsize):
            add = Node('Head', 0)
            self.p2_hashtable.append([add])

    def add2(self):

        # gets 50 random words from the list of all the words in the text file
        for i in range(50):
            x = random.randint(0, len(self.words)-1)
            self.p2_words.append(self.words[x])
        # iterates through the chosen words and adds them to the hashtable
        for word in self.p2_words:
            new_node = Node(word, 1)
            # get correct slot
            slot = self.hashfunc2(word)
            # set current to the first node in the slot
            curr = self.p2_hashtable[slot][0]
            done = False
            # if the first node is empty, add the new node
            while done == False:
                if curr.next == None and curr.data != new_node.data:
                    curr.next = new_node
                    done = True
                # add to word frequency if the word is already in the hashtable
                elif curr.data == new_node.data:
                    curr.freq += 1
                    done = True
                # traverse
                curr = curr.next

    # conducts search for pt 2 w/ helper function
    def search2(self):

        for k in range(5, 25, 5):
            print(" going to index counts as a step, lowest steps possible as per my definition is 3 due to head node")
            print("k =", k*2)
            print("")
            list = []
            # adds words to list following assignment instructions
            for i in range(k):
                x = random.choice(self.p2_words)
                list.append(x)
                x = "notinlist"
                list.append(x)
                # iterates through each word we are looking for and sends it to be searched
            for i in list:
                self.searchhelper2(i)

    # takes word from search list as input and searches for it in the hashtable
    def searchhelper2(self, word):
        # start steps at 1 because im counting going to correct bucket as a step
        # an item cannot have less then 3 steps due to first step being going to correct bucket, and first item in bucket being head placeholder
        # therefore requiring 3 steps to get to the first possible node of any of the buckets
        steps = 1
        found = False
        slot = self.hashfunc2(word)
        curr = self.p2_hashtable[slot][0]
        while curr != None:
            if curr.data == word:
                steps += 1
                print("found", word, "in", steps, "steps")
                print("")
                found = True
                break
            else:
                steps += 1
                curr = curr.next
                if curr == None:
                    print("not found", word, "-1")
                    print("")
                    break

    def hashfunc2(self, word):
        total = 0

        for i in range(len(word)):
            total = total + (i+1) * ord(word[i])

        total = total % self.p2_hashsize

        return total

    # prints out the hashtable
    def print2(self):
        print("")
        print("----------OPEN Hashtable for part 2------")
        print(" going to index counts as a step, lowest steps possible as per my definition is 3 due to head node")
        for i in range(self.p2_hashsize):
            print("")
            print("index", i)
            print("")
            # set current to the first node in the slot
            curr = self.p2_hashtable[i][0]
            # traverse
            list = []
            while curr != None:
                x = curr.data, curr.freq
                list.append(x)

                curr = curr.next
            print(list)

    def main(self):
        obj.fileloader('D:/A3test.txt')
        obj.construct()
        obj.add()
        obj.print()

    def main2(self):
        obj2.fileloader('D:/A3test.txt')
        obj2.construct2()
        obj2.add2()
        obj2.print2()
        obj2.search2()

obj = OpenHashtable()
obj.main()

obj2 = OpenHashtable()
obj2.main2()


# BFS
from collections import deque

N, D = map(int, input().split())
human = [tuple(map(int, input().split())) for _ in range(N)]
infected = [False] * N
infected[0] = True
queue = deque([0])
while len(queue) != 0:
    now = queue.popleft()
    for i in range(N):
        if not infected[i]:
            dist_sq = (human[i][0] - human[now][0]) ** 2 + (human[i][1] - human[now][1]) ** 2
            if dist_sq <= D ** 2:
                infected[i] = True
                queue.append(i)
for i in range(N):
    print('Yes' if infected[i] else 'No')

# DFS
N, D = map(int, input().split())
human = [tuple(map(int, input().split())) for _ in range(N)]
infected = [False] * N
infected[0] = True
stack = [0]
while len(stack) != 0:
    now = stack.pop()
    for i in range(N):
        if not infected[i]:
            dist_sq = (human[i][0] - human[now][0]) ** 2 + (human[i][1] - human[now][1]) ** 2
            if dist_sq <= D ** 2:
                infected[i] = True
                stack.append(i)
for i in range(N):
    print('Yes' if infected[i] else 'No')

# Union-Find
class UnionFind():
    # 初期化
    def __init__(self, n):
        self.par = [-1] * n
        self.rank = [0] * n
        self.siz = [1] * n

    # 根を求める
    def root(self, x):
        if self.par[x] == -1:
            return x # x が根の場合は x を返す
        else:
            self.par[x] = self.root(self.par[x]) # 経路圧縮
            return self.par[x]

    # x と y が同じグループに属するか (根が一致するか)
    def issame(self, x, y):
        return self.root(x) == self.root(y)

    # x を含むグループと y を含むグループを併合する
    def unite(self, x, y):
        # x 側と y 側の根を取得する
        rx = self.root(x)
        ry = self.root(y)
        if rx == ry:
            return False # すでに同じグループのときは何もしない
        # union by rank
        if self.rank[rx] < self.rank[ry]: # ry 側の rank が小さくなるようにする
            rx, ry = ry, rx
        self.par[ry] = rx # ry を rx の子とする
        if self.rank[rx] == self.rank[ry]: # rx 側の rank を調整する
            self.rank[rx] += 1
        self.siz[rx] += self.siz[ry] # rx 側の siz を調整する
        return True

    # x を含む根付き木のサイズを求める
    def size(self, x):
        return self.siz[self.root(x)]

def is_connected(i: int, j: int) -> bool:
    dist = (X[i] - X[j]) ** 2 + (Y[i] - Y[j]) ** 2
    return dist <= D ** 2

N, D = map(int, input().split())
X = [0] * N; Y = [0] * N
for i in range(N): X[i], Y[i] = map(int, input().split())
uf = UnionFind(N)
for i in range(N):
    for j in range(N):
        if is_connected(i, j): uf.unite(i, j)
for i in range(N):
    print('Yes' if uf.issame(0, i) else 'No')


#!/usr/bin/env python

import sys
import http.server
import traceback
from http.server import SimpleHTTPRequestHandler
import json

import time

class MyHandler(SimpleHTTPRequestHandler):
    """
    Mock shoutbox HTTP server to test
    shoutbox API requests.
    """
    def do_HEAD(self):
        self.send_response(200)
        self.send_header("Content-type", "application/json")
        self.end_headers()

    def do_GET(self):
        """
        Respond to a GET with a JSON array containing one object
        that contains the following:

        test user: test message
        """

        self.send_response(200)
        self.send_header("Content-type", "application/json")
        self.end_headers()

        timestamp = str(round(time.time()))
        body = json.dumps([{
            "user": "test user",
            "text": "test message",
            "timestamp": timestamp,
            "id": timestamp,
            "ip": "1.2.3.4"
        }])
        self.wfile.write(body.encode())

    def do_POST(self):
        """
        Respond to a POST by printing the message contents to console
        """
        content_len = int(self.headers.get('Content-Length'))
        post_data = self.rfile.read(content_len)

        try:
            message = json.loads(post_data.decode('utf-8'))
            print("{}: {}".format(message["user"], message["text"]))
            self.send_response(200)

        except:
            traceback.print_exc()
            self.send_response(400)

        self.end_headers()

        return

HandlerClass = MyHandler
ServerClass = http.server.HTTPServer
Protocol = "HTTP/1.0"

if sys.argv[1:]:
    port = int(sys.argv[1])
else:
    port = 8000
server_address = ('127.0.0.1', port)

HandlerClass.protocol_version = Protocol
httpd = ServerClass(server_address, HandlerClass)

sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()