import tensorflow as tf
import numpy as np
from data.client_data import client_data
from models.server_model import create_server_model
from models.client_model import create_client_model

# Define the constants
NUM_ROUNDS = 10
LEARNING_RATE = 0.1
NUM_CLIENTS = 5
NUM_SAMPLES_PER_CLIENT = 1000
INPUT_SHAPE = 10
NUM_CLASSES = 2

# Create the models
server = create_server_model(INPUT_SHAPE, NUM_CLASSES)
client = create_client_model(INPUT_SHAPE, NUM_CLASSES)

# Define the optimizer and loss function
optimizer = tf.keras.optimizers.legacy.SGD(learning_rate=LEARNING_RATE)  # Use the legacy optimizer
loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

def client_training(client_model, X, y):
    with tf.GradientTape() as tape:
        logits = client_model(X, training=True)
        loss = loss_fn(y, logits)
    gradients = tape.gradient(loss, client_model.trainable_weights)
    optimizer.apply_gradients(zip(gradients, client_model.trainable_weights))
    return client_model.get_weights()

for round_num in range(NUM_ROUNDS):
    server_weights = server.get_weights()
    client_weights = []

    for client_id in range(NUM_CLIENTS):
        client_model = create_client_model(INPUT_SHAPE, NUM_CLASSES)
        client_model.set_weights(server_weights)
        X_client, y_client = client_data[client_id]
        client_weights.append(client_training(client_model, X_client, y_client))

    average_weights = np.mean(client_weights, axis=0)
    server.set_weights(average_weights)

print("Federated learning training complete.")

# Save the trained federated model
server.save("federated_learning_project/federated_model")

print("Model saved.")


'''
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0?
Find all unique triplets in the array which gives the sum of zero.

Note:

 Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets. For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
'''

def threeSum(A):
    # Can use either Front-back or Fast-slow pointers
    # We need 3 items hence at-least O(N^2) is needed
    A = sorted(A)
    s = set()
    # for i in range(len(A)-2):
    #     start, end = i+1, i+2
    #     while start < len(A)-1 and end < len(A):
    #         print(i, start, end)
    #         a, b, c = A[i], A[start], A[end]
    #         if a + b + c == 0:
    #             s.add((a, b, c))
    #             start = start + 1
    #             end = start + 1
    #         elif a + b + c < 0:
    #             end += 1
    #         else:
    #             start += 1
    # return list(map(list, sorted(s)))

    for i in range(len(A)-2):
        start, end = i+1, len(A)-1
        while start < end:
            a, b, c = A[i], A[start], A[end]
            if a + b + c == 0:
                s.add((a, b, c))
                start, end = start + 1, end - 1
            elif a + b + c < 0:
                start += 1
            else:
                end -= 1
    return list(map(list, sorted(s)))

A = [-1, 0, 1, 2, -1, -4]
# A = [ 1, -4, 0, 0, 5, -5, 1, 0, -2, 4, -4, 1, -1, -4, 3, 4, -1, -1, -3 ]
print(threeSum(A))


from typing import List
from src.lib.SecondChanceSpecializedQueue import SecondChanceSpecializedQueue

def secondchance(in_: List[str]) -> int:
    """
    @param  int in_ lista de paginas referenciadas

    @return int numero de pagefaults
    """

    faults = 0
    q = SecondChanceSpecializedQueue()

    for page in in_:
        if page not in q.getItems():
            if q.isEmpty or (not q.isFull):
                q.enqueue(page)
                faults += 1
            else:
                q.giveSecondChance(page)
                faults += 1
        else:
            q.setIsReferenced(page)

    return faults


def maior_numero(num1, num2, num3):
    # Verificamos se o primeiro número é maior que o segundo número e se o primeiro número é maior que o tarceiro número
    if (num1 > num2 and num1 > num3):
        return num1
    # Verificamos se o segundo número é maior que o primeiro número e se o segundo número é maior que o tarceiro número
    elif (num2 > num1 and num2 > num3):
        return num2
    # Verificamos se o segundo número é maior que o primeiro número e se o segundo número é maior que o tarceiro número
    elif (num3 > num1 and num3 > num2):
        return num3

# Declarar variaveis inteiras e receber valores do teclado
numero1 = int (input("Digite o primeiro numero: "))
numero2 = int (input("Digite o segundo numero: "))
numero3 = int (input("Digite o terceiro numero: "))

#Exemplo de uso da função
print("O maior número é:", maior_numero(numero1, numero2, numero3))


def test(*args, **kwargs):
    print(args)
    print(kwargs)

def not_convention(*required, **optional):
    print(required)
    print(optional)

test('asdf', 'cvccc', 'asdfasdf', name='pong', namex='gib')
# ('asdf', 'cvccc', 'asdfasdf')
# {'name': 'pong', 'namex': 'gib'}
not_convention('aaa', 'bbb', 'ccc', name='pong', namex='gib')
# ('aaa', 'bbb', 'ccc')
# {'name': 'pong', 'namex': 'gib'}

from functools import reduce

primes = [2, 3, 5, 7, 11, 13]

def product(*numbers):
    print(numbers)
    p = reduce(lambda x, y: x * y, numbers)
    return p

print(product(*primes))
# 30030

print(product(primes))
# [2, 3, 5, 7, 11, 13]

# dict use **

headers = {
    'Accept': 'text/plain',
    'Content-Length': 348,
    'Host': 'http://mingrammer.com'
}

def pre_process(**headers):
    content_length = headers['Content-Length']
    print('content length: ', content_length)

    host = headers['Host']
    if 'https' not in host:
        raise ValueError('You must use SSL for http communication')

pre_process(**headers)

# content length:  348
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 7, in pre_process
# ValueError: You must use SSL for http communication

numbers = [1, 2, 3, 4, 5, 6]

# The left side of unpacking should be list or tuple.
*a, = numbers
# a = [1, 2, 3, 4, 5, 6]

*a, b = numbers
# a = [1, 2, 3, 4, 5]
# b = 6

a, *b, = numbers
# a = 1
# b = [2, 3, 4, 5, 6]

a, *b, c = numbers
# a = 1
# b = [2, 3, 4, 5]
# c = 6


from PIL import Image
import helpers

class Colour:
    def __init__(self, red, green, blue):
        """
        :param red: component of rgb [0 to 31 inclusive].
        :param green: component of rgb [0 to 31 inclusive].
        :param blue: component of rgb [0 to 31 inclusive].
        :var _x: internal variable for x, x coordinate for colour.
        :var _y: internal variable for y, y coordinate for colour.
        """
        self.red = red * 8
        self.green = green * 8
        self.blue = blue * 8
        self._x = None
        self._y = None

    def __eq__(self, other):
        return self.rgb == other.rgb

    def __repr__(self):
        return "({}, {}, {})".format(self.red, self.green, self.blue)

    def __hash__(self):
        return hash(str(self))

    def rgb(self):
        return self.red, self.green, self.blue

    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @property
    def y(self):
        return self._y

    @y.setter
    def y(self, value):
        self._y = value

class Canvas:
    def __init__(self, x_size, y_size):
        """
        :param x_size: Width of the canvas.
        :param y_size: Height of the canvas.
        """
        self.x_size = x_size
        self.y_size = y_size
        self.pixels = []
        self.image = Image.new('RGB', (x_size, y_size))
        self.image_contents = self.image.load()

    def add(self, colour):
        self.pixels.append(colour)

    def generate(self):
        for pixel in self.pixels:
            self.image_contents[pixel.x, pixel.y] = pixel.rgb()
        return self.image

    def generate_from_list(self, colours, coordinates):
        for colour, coordinates in zip(colours, coordinates):
            colour.x = coordinates[0]
            colour.y = coordinates[1]
            self.image_contents[colour.x, colour.y] = colour.rgb()

    def save(self, filename):
        ext = (filename.split("."))[-1].upper()
        self.image.save(filename, ext)

def alg_sort_by_component_sum(x_max, y_max, reverse=False):
    """
    Sort by the sum of the red, blue and green components of each colour.
    :param x_max:
    :param y_max:
    :param reverse:
    """
    x = 0
    y = 0
    coordinates = []
    for i in range(0, x_max * y_max):
        if x >= x_max:
            x = 0
            y += 1
        if y >= y_max:
            y = 0
        coordinates.append((x, y))
        x += 1

    colours = helpers.generate_colours(Colour)
    colours.sort(key=lambda colour: (colour.red + colour.green + colour.blue), reverse=reverse)

    return colours, coordinates

def alg_sort_by_component_sum_reverse(x_max, y_max):
    """
    Reverse sort by the sum of the red, blue and green components of each colour.
    :param x_max:
    :param y_max:
    """
    return alg_sort_by_component_sum(x_max, y_max, reverse=True)

def alg_sort_by_component_difference(x_max, y_max, reverse=False):
    """
    Sort by the difference of the red, blue and green components of each colour.
    :param x_max:
    :param y_max:
    :param reverse:
    """
    x = 0
    y = 0
    coordinates = []
    for i in range(0, x_max * y_max):
        if x >= x_max:
            x = 0
            y += 1
        if y >= y_max:
            y = 0
        coordinates.append((x, y))
        x += 1

    colours = helpers.generate_colours(Colour)
    colours.sort(key=lambda colour: (colour.red - colour.green - colour.blue), reverse=reverse)

    return colours, coordinates

def alg_sort_by_component_difference_reverse(x_max, y_max):
    """
    Reverse sort by the difference of the red, blue and green components of each colour.
    :param x_max:
    :param y_max:
    """
    return alg_sort_by_component_difference(x_max, y_max, reverse=True)

def alg_sort_by_coordinate_sum(x_max, y_max, reverse=False):
    """
    Sort by the sum of the x and y coordinates of each colour.
    :param x_max:
    :param y_max:
    """
    x = 0
    y = 0
    coordinates = []
    colours = helpers.generate_colours(Colour)

    for colour in colours:
        if x >= x_max:
            x = 0
            y += 1
        if y >= y_max:
            y = 0
        coordinates.append((x, y))
        colour.x = x
        colour.y = y
        x += 1

    colours.sort(key=lambda colour: (colour.x + colour.y), reverse=reverse)
    return colours, coordinates

def alg_sort_by_coordinate_sum_reverse(x_max, y_max):
    """
    Reverse sort by the sum of the x and y cooridnates of each colour.
    :param x_max:
    :param y_max:
    """
    return alg_sort_by_coordinate_sum(x_max, y_max, reverse=True)

def alg_sort_by_coordinate_x(x_max, y_max, reverse=False):
    """
    Sort by the x coordinate of each colour.
    :param x_max:
    :param y_max:
    """
    x = 0
    y = 0
    coordinates = []
    colours = helpers.generate_colours(Colour)

    for colour in colours:
        if x >= x_max:
            x = 0
            y += 1
        if y >= y_max:
            y = 0
        coordinates.append((x, y))
        colour.x = x
        colour.y = y
        x += 1

    colours.sort(key=lambda colour: (colour.x), reverse=reverse)
    return colours, coordinates

def alg_random(x_max, y_max):
    """
    Colours and coordinates are (psuedo) randomised
    :param x_max:
    :param y_max:
    """
    colours = helpers.shuffle_list(helpers.generate_colours(Colour))
    coordinates = helpers.shuffle_list(helpers.generate_coordinates(x_max, y_max))
    return colours, coordinates

def alg_reverse_red(x_max, y_max):
    """
    Red component is reversed
    :param x_max:
    :param y_max:
    """
    x = 0
    y = 0
    colours = []
    coordinates = []

    for r in range(32, 0, -1):
        for g in range(1, 33):
            for b in range(1, 33):
                if x >= x_max:
                    x = 0
                    y += 1
                if y >= y_max:
                    y = 0
                colours.append(Colour(r, g, b))
                coordinates.append((x, y))
                x += 1
    return colours, coordinates

def alg_rgb(x_max, y_max):
    """
    Iterate through red -> green -> blue
    :param x_max:
    :param y_max:
    """
    x = 0
    y = 0

    colours = []
    coordinates = []
    for colour in helpers.generate_colours(Colour):
        if x >= x_max:
            x = 0
            y += 1
        if y >= y_max:
            y = 0
        colours.append(colour)
        coordinates.append((x, y))
        x += 1

    return colours, coordinates

def alg_bgr(x_max, y_max):
    """
    Iterate through blue -> green -> red
    :param x_max:
    :param y_max:
    """
    x = 0
    y = 0
    colours = []
    coordinates = []

    for b in range(1, 33):
        for g in range(1, 33):
            for r in range(1, 33):
                if x >= x_max:
                    x = 0
                    y += 1
                if y >= y_max:
                    y = 0
                colours.append(Colour(r, g, b))
                coordinates.append((x, y))
                x += 1
    return colours, coordinates

def alg_grb(x_max, y_max):
    """
    Iterate through green -> red -> blue
    :param x_max:
    :param y_max:
    """
    x = 0
    y = 0
    colours = []
    coordinates = []

    for g in range(1, 33):
        for r in range(1, 33):
            for b in range(1, 33):
                if x >= x_max:
                    x = 0
                    y += 1
                if y >= y_max:
                    y = 0
                colours.append(Colour(r, g, b))
                coordinates.append((x, y))
                x += 1
    return colours, coordinates

def alg_rbg(x_max, y_max):
    """
    Iterate through red -> blue -> green
    :param x_max:
    :param y_max:
    """
    x = 0
    y = 0
    colours = []
    coordinates = []

    for r in range(1, 33):
        for b in range(1, 33):
            for g in range(1, 33):
                if x >= x_max:
                    x = 0
                    y += 1
                if y >= y_max:
                    y = 0
                colours.append(Colour(r, g, b))
                coordinates.append((x, y))
                x += 1
    return colours, coordinates

def create_image(algorithm):
    """
    :param algorithm: The name of an algorithm function
    :return:
    """
    canvas = Canvas(256, 128)
    colours, coordinates = eval(algorithm)(canvas.x_size, canvas.y_size)
    canvas.generate_from_list(colours, coordinates)
    return canvas.image


import numpy as np
import pandas as pd

import gpflow
from sklearn.base import BaseEstimator, RegressorMixin, TransformerMixin

from sklearn import model_selection, metrics

import logging
logger = logging.getLogger(__name__)  # a named logger

class GPRegressor(BaseEstimator, RegressorMixin):
  """Gaussian Process regressor.
  """
  def __init__(self, *args, kfunc=gpflow.kernels.Matern32(), mfunc=None, **kwargs):
    """
    Args
    ----
    kfunc:
        kernel function
    mfunc:
        mean function
    """
    # super().__init__(*args, **kwargs)
    assert self.dim_out==1, "Only scalar output is supported."

    self.kfunc = kfunc
    self.mfunc = mfunc

  def _fit(self, *, opt=gpflow.optimizers.Scipy(), maxiter=10**3, **kwargs):
    """Fit the model.
    """
    gpr = gpflow.models.GPR(data=(self._Xtrn.values, np.atleast_1d(self._Ytrn.values.squeeze())), kernel=self.kfunc, mean_function=self.mfunc)
    opt_logs = opt.minimize(gpr.training_loss, gpr.trainable_variables, options=dict(maxiter=maxiter))
    # print_summary(gpr)
    self.basemodel = gpr

  def _predict(self, x:pd.DataFrame) -> np.ndarray:
    self._predict_mean, self._predict_var = self.basemodel.predict_y(x.values)
    # dm = pd.Series(moo.numpy().squeeze(), index=x.index, name=self.data_out.columns[0]).to_frame()
    # self._predict_dv = pd.Series(voo.numpy().squeeze(), index=x.index, name=self.data_out.columns[0]).to_frame()
    return self._predict_mean

  def __init__(self, **kwargs) -> None:
    """
    Notes
    -----
    Methods in `sklearn.pipeline` require that all variables must be saved with their given names.
    """
    self.model = None

  def __str__(self) -> str:
    # Gotcha: f-string does not allow backslash!
    # return f"MLPRegressor(hidden={self.n_hidden}, bias={self.bias}, activ={str(self.activ).split('.')[-1].split('\'')[0]}, loss={self.loss}, reg={self.reg_parms})"
    return "MLPRegressor(hidden={}, bias={}, activ={}, loss={}, reg={})".format(self.n_hidden, self.bias, str(self.activ).split('.')[-1].split('\'')[0], str(self.loss).split('(')[0], self.reg_parms)

  @property
  def name(self) -> str:
    # return "MLPRegressor({}, {}, {}, {})".format(self.n_hidden, str(self.activ).split('.')[-1].split('\'')[0], str(self.loss).split('(')[0], self.reg_parms)
    return "MLPRegressor({}, {})".format(self.n_hidden, str(self.activ).split('.')[-1].split('\'')[0])

  def init_model(self, n_input, n_output):
      self.model = MLP_Regression(n_input, n_output, n_hidden=self.n_hidden,
      activ=[self.activ(**self.activ_parms) for _ in range(len(self.n_hidden))])
      self.solver = self.optim(self.model.parameters(), **self.optim_parms)

  def fit(self, X, Y, *,
  # Xtrn:np.ndarray, Ytrn:np.ndarray, *, Xtst:np.ndarray=None, Ytst:np.ndarray=None,
  n_epochs=10**3, batch_size=32, shuffle=True, split_parms=dict(test_size=0.25, random_state=1234), test_step=10):
    """
    Args
    ----
    X, Y: input and output data.
    n_epochs: number of training epochs.
    batch_size, shuffle: for training data only.
    split_parms: keyword parameters for `sklearn.model_selection.train_test_split()`, a dictionary.
    test_step: evaluate and log every `test_step`.
    """
    if self.model is None:
      # self.init_model(Xtrn.shape[1], Ytrn.shape[1])
      self.init_model(X.shape[1], Y.shape[1])

    Xtrn, Xtst, Ytrn, Ytst = model_selection.train_test_split(np.asarray(X), np.asarray(Y), **split_parms)
    train_dl = DataLoader(TensorDataset(Tensor(np.asarray(Xtrn)), Tensor(np.asarray(Ytrn))), batch_size=batch_size, shuffle=shuffle)
    test_dl = DataLoader(TensorDataset(Tensor(np.asarray(Xtst)), Tensor(np.asarray(Ytst))), batch_size=batch_size, shuffle=False)
    reg_weight = self.reg_parms['weight']
    reg_pow = self.reg_parms['pow']

    n_parameters = self.model.number_parameters  # total number of parameters in NN

    res = []
    for epoch in range(n_epochs):
      # set the training mode
      self.model.train()
      # enumerate mini batches
      for i, (x, y) in enumerate(train_dl):
        # clear the gradients
        self.solver.zero_grad()
        # compute the model output and loss
        loss = self.loss(self.model(x), y)

        # compute loss + reg
        if reg_weight > 0:
          val = 0.
          for p in self.model.parameters():
            val += p.abs().pow(reg_pow).sum()
          if self.loss.reduction == 'mean':
            val /= n_parameters
          full_loss = loss + reg_weight * val #.pow(1/reg_pow)
        else:
          full_loss = loss

        # credit assignment
        full_loss.backward()
        # update model weights
        self.solver.step()

      # set the evaluation mode
      if (test_step > 0) & (Xtst is not None) & (Ytst is not None):
        if epoch % test_step==0:
          # https://stackoverflow.com/questions/55627780/evaluating-pytorch-models-with-torch-no-grad-vs-model-eval
          self.model.eval()
          with torch.no_grad():
            # mse = nn.functional.mse_loss(self.model(Tensor(np.asarray(Xtst))).detach(), Tensor(np.asarray(Ytst)))
            mse = self.evaluate(test_dl)
            res.append((full_loss.numpy(), loss.numpy(), mse.numpy()))
            logger.info(f"Epoch {epoch}: full_loss={full_loss:.3e}, loss={loss:.3e}, eval.mse={mse:.3e}")
      else:
        res.append((full_loss.numpy(), loss.numpy()))
        logger.info(f"Epoch {epoch}: full_loss={full_loss:.3e}, loss={loss:.3e}")
        # logger.info(f"Epoch {epoch}: loss={loss:.3e}")

      self._fit_info = np.asarray(res)

  def evaluate(self, test_dl):
    prd, obs = [], []
    for i, (x, y) in enumerate(test_dl):
      prd.append(self.model(x).detach().numpy())
      obs.append(y.numpy())
      # obs.append(y.numpy().reshape((-1, 1)))

    # mse = metrics.mean_squared_error(obs, prd)
    mse = nn.functional.mse_loss(Tensor(np.vstack(prd)), Tensor(np.vstack(obs)))
    return mse

  def predict(self, X:np.ndarray):
    with torch.no_grad():
      Y = self.model(Tensor(np.asarray(X))).detach().numpy()

    return Y

  # def get_params(self, **kwargs):
  #   return [p.detach().numpy() for p in self.model.parameters()]

  # def set_params(self, *args, **kwargs):
  #   with torch.no_grad():
  #     for n, layer in enumerate(self.layers):
  #       try:
  #         layer.weight = nn.Parameter(torch.Tensor(args[2*n]))
  #         layer.bias = nn.Parameter(torch.Tensor(args[2*n+1]))
  #       except:
  #         break


import csv
import sys
import copy
import argparse
import logging

_logger = logging.getLogger(__name__)

def read_csv(path,*fields, rows_with_fieldnames=True):
    """ Read CSV As a Whole
    """
    with open(path, newline="", encoding='utf-8') as f:
        reader = csv.DictReader(f)
        rows = [r for r in reader]

        if fields:
            cols = tuple([r[fd] for r in rows] for fd in fields if fd in reader.fieldnames)
#            print("type(cols):", type(cols), "type(fields)",type(fields))
            if len(cols) == len(fields) == 1:
                # outer variable could recieve a list, not tuple with a list as the single element
                return cols[0]
            elif len (cols) == len(fields):
                return cols
            else:
                raise ValueError("Invalid fields:{}.".format(",".join(list(filter(lambda x:x not in reader.fieldnames)))))
        elif rows_with_fieldnames:
            return rows, reader.fieldnames
        else:
            return rows

def write_csv(pth, rows, fieldnames):
    with open(pth, "w", newline='', encoding='utf-8') as f:
        writer = csv.DictWriter(f, fieldnames = fieldnames)
        writer.writeheader()
        writer.writerows(rows)

def merge_by(
    dreader1,
    fieldnames1,
    dreader2,
    fieldnames2,
    key
):
    """ Merge two csv files
    """

    if key not in fieldnames1 or key not in fieldnames2:
        raise ValueError("Invalid key:{}".formqat(key))

    # Note: nrows of merged csv = nrows of dreader1
    merged = { r1[key]:r1 for r1 in dreader1 }

    # fieldnames appended to fieldnames1 list
    # preference with same fieldname: dreader1 > dreader2
#    print("fn1=",fieldnames1,"fn2=",fieldnames2)
#    appdfn = [f2 for f2 in fieldnames2 if f2 not in fieldnames1]
#    print("APPDFN=",appdfn)
    for r2 in dreader2:
        if r2[key] in merged: # shared key
            r1 = merged[r2[key]]

            for k,v in r2.items():
                if k not in r1 or not r1[k]:
                    r1[k] = v # update

#            r3 = copy.deepcopy(r1)
#
#            for fn in appdfn: # append new fields
#                if fn in r3:
#                    raise ValueError("Duplicately set field {} at key {} ".format(fn, r2[key]))
#                else:
#                    r3[fn] = r2[fn]
#
#            for fn, functor in kwargs.items(): # fieldnames
#                r3[fn] = functor(r1, r2)
#
#            merged[r2[key]] = r3 # update
#            del r1

    # no need for the key
#    merged = [v for k,v in merged.items()]
#    fieldnames3 = fieldnames1 + appdfn
#
#    return merged, fieldnames3
    merged = list(merged.values())
    fieldnames3 = fieldnames1 + [ fn for fn in fieldnames2 if fn not in fieldnames1]
#    print(fieldnames3)

    return merged, fieldnames3

def merge_csv(key, out_pth, csv_path1, csv_path2, *pths):
    def read_with_check(csv_path, key):
        rows, fns = read_csv(csv_path)
        if not len(set([ r[key] for r in rows])) == len(rows):
            _logger.warn("duplicated keys in {}".format(csv_path))
        return rows, fns

    first, fn1 = read_with_check(csv_path1, key)
    second, fn2 = read_with_check(csv_path2, key)
    remain = list(pths)
    while first and second:
        first, fn1 = merge_by(first, fn1, second, fn2, key)
        second, fn2 = read_with_check(remain.pop(0), key) if remain else ([], [])

    write_csv(out_pth, first, fn1)

def parse_args(args):
    parser = argparse.ArgumentParser(description='Simple script to help to merge several csv files')
    parser.add_argument("csvout", metavar="CSVOUT",
                        help="Where to place the merged csv file.")
    parser.add_argument("csvpths", metavar='CSVPTH', nargs='+',
                        help="Multiple csv paths stored pieces of information. The former CSVPTH arguemnts will get higher priority.")
    parser.add_argument('--key', default="asin",
                        help="criteria to merge records, by default set to 'asin'.")

    return parser.parse_args(args)

def main(args=None):
    if args is None:
        args = sys.argv[1:]
    args = parse_args(args)

    if len(args.csvpths) == 1:
        raise ValueError("More than one CSVPTH arguemnts are required.")

#    pths = args.csvpths
#    print("args.csvout=",args.csvout,"args.csvpths=",args.csvpths)
    merge_csv(args.key, args.csvout, *args.csvpths)

if __name__ == "__main__":
    main()


from datetime import datetime

from app.consts import DATETIME_FORMAT

def data_gaps_located_message(gap_info: list[tuple[datetime, datetime]]) -> str:
    singular = True if len(gap_info) == 1 else False
    gap_str: str = 'There {} {} date gap{} that {} already been located on the table :\n'.format(
        'is' if singular else 'are',
        len(gap_info),
        '' if singular else 's',
        'has' if singular else 'have'
    )
    for data_pair in gap_info:
        gap_str += f"From {data_pair[0].strftime(DATETIME_FORMAT)} to {data_pair[1].strftime(DATETIME_FORMAT)}\n"
    return gap_str

def rows_were_affected_message(affected: int) -> str:
    singular: bool = affected == 1
    return '{} row{} {} affected'.format(
        affected,
        '' if singular else 's',
        'was' if singular else 'were'
    )


from CoolProp.CoolProp import PropsSI
import math
#圆形管槽内的强制对流换热

#Hyper parameter
#P_Pa_Pressure
#T_K_Temperature;
#D_kg/m^3_Mass density;
#C_J/(kg`k)_比热容
#U_pa`s_动力粘度
#V_m^2/s_运动粘度
#λ_W/(m`L)_导热系数

#Pr

p = 101325
def para(p,t):
    D=PropsSI('D', 'P', p, 'T', t, 'Water')
    C = PropsSI('C', 'P', p, 'T', t, 'Water')
    V = PropsSI('V', 'P', p, 'T', t, 'Water')*0.001
    λ = PropsSI('L', 'P', p, 'T', t, 'Water')
    Pr=PropsSI('Prandtl', 'P', p, 'T', t, 'Water')
    return [D,C,V,λ,Pr]

#U=D`V
#a=V/Pr

def Re(v,d,V):
    re=v*d/V
    return  re

def Nu(Re,Pr,l,d,tf,tw,p):
    if Pr>0.6:
        if Re <2300 and Pr >=0.48 and Pr <=16700:
            if (para(p,tf)[0]*para(p,tf)[2])/(para(p,tw)[0]*para(p,tw)[2])>=0.0044 and (para(p,tf)[0]*para(p,tf)[2])/(para(p,tw)[0]*para(p,tw)[2]) <=9.75 and pow(Re*Pr*d/l,1/3)*pow((para(p,tf)[0]*para(p,tf)[2])/(para(p,tw)[0]*para(p,tw)[2]),0.14)>=2:
                nu=1.86*pow(Re*Pr*d/l,1/3)*pow((para(p,tf)[0]*para(p,tf)[2])/(para(p,tw)[0]*para(p,tw)[2]),0.14)
                print('Sieder-Tade公式',nu)
                return nu
            else:
                print('error',Pr,(para(p,tf)[0]*para(p,tf)[2])/(para(p,tw)[0]*para(p,tw)[2]),pow(Re*Pr*d/l,1/3)*pow((para(p,tf)[0]*para(p,tf)[2])/(para(p,tw)[0]*para(p,tw)[2]),0.14))
                pass

        elif Re>=10000 and Re <=120000 and Pr >=0.7 and Pr<=120 and l/d>=10:
            if tw >tf:
                nu=0.023*pow(Re,0.8)*pow(Pr,0.4)
                print('Dittus-Boelter公式(加热流体)： ',nu)
                return nu
            else:
                nu=0.023*pow(Re,0.8)*pow(Pr,0,3)
                print('Dittus-Boelter公式（冷却流体）： ', nu)
                return nu
        elif Re>=2300 and Re <=1000000 and Pr >=0.6 and Pr<=100000 :
            f=pow(1.8*math.log10(Re)-1.5,-2)
            nu=(1+pow(d/l,2/3))*(f*Pr*(Re-1000)/8)/(1+12.7*(pow(Pr,2/3)-1)*pow(f/8,1/2))
            print('Gnielinski公式： ',nu)
            return nu
    elif Pr>=0.003 and Pr <= 0.05:
        print('内容待补充2')
        pass

d1=0.012
d2=0.15
v=0.6
tw=80+273.15
t_1=20+273.15
n=4
pi=3.1415926535
l=4*pi*d2
A=pi*pow(d1,2)/4

Cr=1+10.3*pow(2*d1/d2,3)
count=0
for i in range(57,58):
    j=0
    WQE=57.138
    while j <10000000:
        count+=1
        print(count)
        t_2=WQE+273.15
        tf=(t_1+t_2)/2
        re=Re(v,d1,para(p,tf)[2])
        nu=Nu(re,para(p,tf)[4],l,d1,tf,tw,p)
        nu=nu*Cr
        h1=nu*para(p,tf)[3]/d1
        if math.log((tw-t_1)/(tw-t_2))==0:
            h2=h1
        else:
            h2=(para(p,tf)[1]*para(p,tf)[0]*v*A)/(pi*d1*l/math.log((tw-t_1)/(tw-t_2)))
        print(h1-h2,t_2-273.15)
        qwer=h1-h2
        j+=1
        WQE+=0.0000001
        if qwer<=0.0001:
            print(t_2)
            break


#空间复杂度O(n)
class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        from collections import defaultdict
        dic = defaultdict(int)
        for num in nums:
            dic[num] += 1
        for num,v in dic.items():
            if v == 1:
                return num
        return -1

#空间复杂度O(1)
class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        #建立一个长度为 32 的数组 counts ，通过以上方法可记录所有数字的各二进制位的 1 的出现次数
        count = [0] * 32
        for num in nums:
            for i in range(32):
                count[i] += num & 1 #更新第 i 位
                num >>= 1 #第 i 位 --> 第 i + 1 位
        res = 0
        m = 3
        #将 counts 各元素对 3 求余，则结果为 “只出现一次的数字” 的各二进制位。
        for i in range(32):
            res <<= 1 #左移 1 位
            res |= count[31-i] % m #得到 只出现一次的数字 的第 (31 - i) 位并恢复第 i 位的值到 res
        return res if count[31] % m == 0 else ~(res ^ 0xffffffff) #如果res为负,则求得到res的补码，需要先将 0 - 32 位取反（即 res ^ 0xffffffff ），再将所有位取反（即 ~ ）

bit/位运算基础/137只出现一次的数字 II.py

137只出现一次的数字 II.py


from pandas import DataFrame, read_csv, read_excel
from typing import Tuple

def load_data(filepath: str, filetype: str = "csv") -> DataFrame:
    """
    Loads a dataset from a file.

    Parameters:
        filepath (str): Path to the file.
        filetype (str): Type of file ('csv', 'excel'). Default is 'csv'.

    Returns:
        DataFrame: Loaded DataFrame.
    """
    if filetype == "csv":
        return read_csv(filepath)
    elif filetype == "excel":
        return read_excel(filepath)
    else:
        raise ValueError(
            "Unsupported filetype. Supported types are 'csv' and 'excel'."
        )

def save_data(df: DataFrame, filepath: str, filetype: str = "csv") -> None:
    """
    Saves a DataFrame to a file.

    Parameters:
        df (DataFrame): DataFrame to save.
        filepath (str): Path to save the file.
        filetype (str): Type of file ('csv', 'excel'). Default is 'csv'.

    Returns:
        None: The DataFrame is saved to disk.
    """
    if filetype == "csv":
        df.to_csv(filepath, index=False)
    elif filetype == "excel":
        df.to_excel(filepath, index=False)
    else:
        raise ValueError(
            "Unsupported filetype. Supported types are 'csv' and 'excel'."
        )

def train_test_split(
    df: DataFrame, ratio: float
) -> Tuple[DataFrame, DataFrame]:
    """
    Splits a DataFrame into training and testing sets.

    Parameters:
        df (DataFrame): DataFrame to split.
        ratio (float): Proportion of data to include in the training set.

    Returns:
        Tuple[DataFrame, DataFrame]: Training and testing DataFrames.
    """
    train_size = int(len(df) * ratio)
    train_set = df[:train_size]
    test_set = df[train_size:]
    return train_set, test_set


def manual(txt):
    titulo(f'Acessando o manual do comando {txt}')
    help(txt)
def titulo(msg):
    tam = len(msg) + 4
    print(f' {msg}')

comando = ''
while True:
    titulo('SISTEMA DE AJUDA PyHELP')
    comando = str(input('Função ou comando > '))
    if comando.upper() == 'FIM':
        break
    else:
        manual(comando)
titulo('ATÉ LOGO!')


from colored import fg, bg, attr

#tableau pour l'echiquier:
ech=["0"]*64

#coordone du tableau
coordone=[
    "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8",
    "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7",
    "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6",
    "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5",
    "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4",
    "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3",
    "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2",
    "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"
    ]
print(coordone[63])
bordure=["a8","a7","a6","a5","a4","a3","a2","a1","h8","h7","h6","h5","h4","h3","h2","h1"]
bordureG=["a8","a7","a6","a5","a4","a3","a2","a1"]
bordureD=["h8","h7","h6","h5","h4","h3","h2","h1"]
switchBordureD= ["a8","a7","a6","a5","a4","a3","a2","a1"]
switchBordureG = ["h8","h7","h6","h5","h4","h3","h2","h1"]

def initech():
    a=["0"]*64
    return a
def plateau(tour):

    i=0
    ligne = "    "
    #couleur rose et gris
    color2= fg("#FF00C2")
    color1=fg("#00D7FA")

    res = attr('reset')

    # affichage des lettre
    while(i<8):
        ligne=ligne+color2+chr(97+i).rjust(3)+res
        i+=1
    print (ligne)

    i=0
    t=0
    l=0
    ligne=""

    print(ligne)

    while(i<8):
        j=0

        #Affichage des numero de ligne
        ligne=color2 + chr(56-i).ljust(4) +res

       #affichage des element du plateaux
        while(j<8):

          col= ech[t]

          if col == "0":
              ligne = ligne + color1 + ech[t].rjust(3)+ res
          else:
            ligne=ligne+ech[t]

          j+=1
          t+=1
          l+=1

        print(ligne)
        i+=1

def reverplateau():
    #inverse les coordone
    ech.reverse()
    coordone.reverse()
    print (coordone)

    i=0
    ligne = "    "
    #couleur rose et gris
    color2= fg("#FF00C2")
    color1=fg("#00D7FA")

    res = attr('reset')

    # affichage des lettre
    while(i<8):
        ligne=ligne+color2+chr(97+i).rjust(3)+res
        i+=1
    print (ligne)

    i=0
    t=0
    l=0
    ligne=""

    print(ligne)

    while(i<8):
        j=0

        #Affichage des numero de ligne
        ligne=color2 + chr(49+i).ljust(4) +res

       #affichage des element du plateaux
        while(j<8):

          col= ech[t]

          if col == "0":
              ligne = ligne + color1 + ech[t].rjust(3)+ res
          else:
            ligne=ligne+ech[t]

          j+=1
          t+=1
          l+=1

        print(ligne)
        i+=1

def switch(ech):
    ech.reverse()
    coordone.reverse()


import asyncio
from ftx import FtxManager
import logging
import sys
import time
from lib.events import OrderEvent
from lib.interface import OrderBook
from lib.metrics import LatencyMetric

# to keep track of market data update frequency
book_latency = LatencyMetric()

def ftx_depth_callback(contract_name: str, book: OrderBook):
    book_latency.mark()

def ftx_execution_callback(order_event: OrderEvent):
    logging.info('Execution callback: {}'.format(order_event))

async def print_latency():
    logging.info('Processing latency: %.2f ms' % book_latency.get_mean())

    await asyncio.sleep(1)

if __name__ == '__main__':
    # logging stuff
    root = logging.getLogger()
    root.setLevel(logging.INFO)
    handler = logging.StreamHandler(sys.stdout)
    handler.setLevel(logging.INFO)
    logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")
    handler.setFormatter(logFormatter)
    root.addHandler(handler)

    logging.info("main program starts")

    # start FTX connection and register callbacks.
    contracts = {'ETH-PERP'}
    ftx = FtxManager(symbol=contracts)
    ftx.register_depth_callback(ftx_depth_callback)
    ftx.register_execution_callback(ftx_execution_callback)
    ftx.connect()

    # loop till ready
    while True:
        if ftx.not_ready():
            logging.info("Not ready to trade")
            time.sleep(1)
        else:
            asyncio.run(print_latency())


import curses
from time import perf_counter_ns
from typing import List
from string import ascii_lowercase
import numpy as np

def is_okay(x: str) -> bool:
    if len(x) < 3:
        return False
    for c in x:
        if c not in ascii_lowercase:
            return False
    return True

with open('./dict.txt') as f:
    all_words = [x.strip() for x in f.readlines() if is_okay(x.strip())]
all_words_count = len(all_words)

buffers: List[str] = []
logtimes: List[List[int]] = []
num_word = 0

screen = curses.initscr()
curses.noecho()

for the_word in all_words[:100]:
    num_word += 1
    screen.clear()
    screen.addstr(0,0, f'Current word number: {num_word}')
    screen.addstr(1,0, the_word)
    screen.refresh()

    buffer: str = ""
    logtime: List[int] = []

    screen.move(2,0)
    while buffer != the_word:
        screen.refresh()
        s = screen.getch()
        c = chr(s)
        if s == 3:
            break
        if c == the_word[len(buffer)]:
            logtime.append(perf_counter_ns())
            buffer += c
            screen.addstr(c)
            screen.move(2,len(buffer))

    if buffer == the_word:
        buffers.append(np.array([c for c in buffer]))
        logtimes.append(np.array(logtime, dtype=np.int64))
    else:
        break

curses.endwin()

import pickle
with open('./buffers.pkl', 'wb') as f:
    pickle.dump(buffers, f)
with open('./logtimes.pkl', 'wb') as f:
    pickle.dump(logtimes, f)


from django.urls import path

from .views import (
    DiagnosisCodeGetUpdateDeleteAPIView,
    DiagnosisCodeListAPIView,
    DiagnosisCodeUploadAPIView,
    DiagnosisCreateAPIView,
)

urlpatterns = [
    path("list-diagnosis-codes/", DiagnosisCodeListAPIView.as_view(), name="diagnosis-code-list"),
    path("create-diagnosis-code/", DiagnosisCreateAPIView.as_view(), name="diagnosis-code-create"),
    path(
        "diagnosis-code/<int:pk>/",
        DiagnosisCodeGetUpdateDeleteAPIView.as_view(),
        name="diagnosis-code-get-update-delete",
    ),
    path("diagnosis-codes/upload/", DiagnosisCodeUploadAPIView.as_view(), name="diagnosis-code-upload"),
]


# -*- coding: utf-8 -*-
"""
Created on Sun Mar 29 11:09:20 2020

@author: eric
"""

import xlsxwriter
import random

workbook = xlsxwriter.Workbook("My_result.xlsx")

worksheet_data = workbook.add_worksheet("data")
worksheet_analysis = workbook.add_worksheet("analysis")

#plusieurs types d'accès
#A1
worksheet_analysis.write("A1","Spock")

#A2
worksheet_analysis.write(
    1, #ligne
    0, #colonne
    "Kirk")
#E1 a E4
worksheet_analysis.write(0,4,42)#en E1 on a 42
worksheet_analysis.write(1,4,55)
worksheet_analysis.write(2,4,68)
worksheet_analysis.write(3,4,12)
worksheet_analysis.write(4,4,"=SUM(E1:E4)")

#format
success_cell = workbook.add_format(
    {
     "bg_color":"#00ff00",
     "border":1
     }
    )

fail_cell = workbook.add_format(
    {
     "bg_color":"#ff0000",
     "border":1
     }
    )

worksheet_analysis.write(
    5,6,"=PI()",
    success_cell
    )

#serie colonnes
worksheet_analysis.write_column(
    "B1",
    [3,1,2,5,8,9,10]
     )

#serie lignes
worksheet_analysis.write_row(
    "A8",
    [5,4,9,7,8]
    )

#formattage conditionnel
worksheet_analysis.conditional_format(
    "B1:B8",
    {
     "type":"cell",
     "criteria":"<=",
     "value":5,
     "format":success_cell
     }
    )
xrange = range
#remplissage loop
def fill_columns(columns=[], start_cell=0,end_cell=0,worksheet=None):
    for column_name in columns:
        worksheet_analysis.write_column(
            column_name + str(start_cell)+":"+column_name+str(end_cell), #A1:A10
            random.sample(xrange(10),(end_cell - start_cell +1))

            )
fill_columns(
    columns=["A","B","C","D","E"],
    start_cell = 11,
    end_cell = 20,
    worksheet=worksheet_analysis

    )

def apply_conditional_format(columns=[], start_cell=0,end_cell=0,worksheet=None, formats=[]):
    for column_name in columns:
        worksheet.conditional_format(
            column_name + str(start_cell) + ":" + column_name + str(end_cell),
            {
                "type":"cell",
                "criteria":"<=",
                "value":5,
                "format": formats[0]
            }
        )

        worksheet.conditional_format(
            column_name + str(start_cell) + ":" + column_name + str(end_cell),
            {
                "type":"cell",
                "criteria":">",
                "value":5,
                "format": formats[1]
            }
        )

apply_conditional_format(
    columns = ["A", "B", "C", "D","E"],
    start_cell=11,
    end_cell = 20,
    worksheet = worksheet_analysis,
    formats=[success_cell, fail_cell]

    )

workbook.close()


from autobahn.asyncio.websocket import WebSocketServerProtocol, WebSocketServerFactory
import logging
import asyncio
import time
import numpy as np
import numba

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class NBodyServerProtocol(WebSocketServerProtocol):
    def onOpen(self):
        self.factory.register(self)

    def connectionLost(self, reason):
        super(NBodyServerProtocol, self).connectionLost(self, reason)
        self.factory.unregister(self)

N = 20
X = 0
Y = 1
DXDT = 2
DYDT = 3
M = 4
ID = 5
DT = 60.0 ** (-1)
G = -0.0001
BROADCAST_DT = 24.0 ** (-1)

@numba.jit
def calculate_forces(bodies):
    """
    :param bodies: np.ndarray
    """
    forces = np.zeros((bodies.shape[1], )*2 + (2, ))
    for i in range(forces.shape[0]):
        for j in range(forces.shape[1]):
            vector_x = bodies[X, j] - bodies[X, i]
            vector_y = bodies[Y, j] - bodies[Y, i]
            norm = np.sqrt(vector_x**2 + vector_y**2)
            distance_squared = vector_x ** 2 + vector_y ** 2
            if distance_squared < 0.001:
                continue
            vector_x /= norm
            vector_y /= norm
            magnitude = G * bodies[M, i] * bodies[M, j] / distance_squared
            forces[i, j, X] = magnitude * vector_x / bodies[M, j]
            forces[i, j, Y] = magnitude * vector_y / bodies[M, j]

    total_forces = np.sum(forces, axis=0)

    return total_forces

class NBodyServerFactory(WebSocketServerFactory):

    def __init__(self, eventloop, *args, **kwargs):
        super(NBodyServerFactory, self).__init__(*args, **kwargs)
        self.clients = set()
        self.eventloop = eventloop
        angles = np.random.uniform(-np.pi, np.pi, N)
        distances = np.random.uniform(0.5, 6, N)
        masses = 2.0**np.random.poisson(1.0, N)
        masses[0] = 1000
        orbital_velocities = np.sqrt(-G*masses[0]/distances)
        distances[0] = 0
        orbital_velocities[0] = 0
        xs = distances * np.cos(angles)
        ys = distances * np.sin(angles)
        normalized_directions = np.array([-ys, xs]).T
        normalized_directions /= np.hypot(-ys, xs)[:,None]
        velocities = normalized_directions * orbital_velocities[:,None]
        velocities[0] = [0, 0]

        self.bodies = np.array([
            xs,
            ys,
            velocities.T[X],
            velocities.T[Y],
            masses,
            np.arange(N)
        ])

        logger.info('Starting simulation')
        self.times = []
        self.last_broadcast = 0
        self.tick()

    def tick(self):
        self.eventloop.call_later(DT/4, self.tick)
        start = time.time()
        forces = calculate_forces(self.bodies)
        self.bodies[[DXDT, DYDT]] += forces.T * DT
        self.bodies[[X, Y]] += self.bodies[[DXDT, DYDT]] * DT
        end = time.time()

        if end > self.last_broadcast + BROADCAST_DT:
            self.broadcast(self.bodies.tobytes('C'))
            self.last_broadcast = time.time()

        self.times.append(end-start)
        if(len(self.times) >= 1000):
            logger.info('Calculated {} ticks at {} ms/tick'.format(len(self.times), np.mean(self.times)*1000))
            self.times = []

            offset_x = self.bodies[X][0]
            offset_y = self.bodies[Y][0]
            if np.hypot(offset_x, offset_y) > 1e3:
                offset_x = self.bodies[X][0]
                offset_y = self.bodies[Y][0]
                logger.info('Recentering to {}, {}'.format(offset_x, offset_y))
                self.bodies[X] -= offset_x
                self.bodies[Y] -= offset_y

    def register(self, client):
        self.clients.add(client)

    def unregister(self, client):
        self.clients.remove(client)

    def broadcast(self, message):
        is_binary = isinstance(message, bytes)
        if not is_binary:
            message = message.encode('utf-8')
        for client in self.clients:
            client.sendMessage(message, is_binary)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    factory = NBodyServerFactory(
        loop,
        'ws://0.0.0.0:9000',
        debug=False,
        debugCodePaths=False
    )
    factory.protocol = NBodyServerProtocol

    coro = loop.create_server(factory, '0.0.0.0', 9000)
    server = loop.run_until_complete(coro)

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        server.close()
        loop.close()


import os
import numpy as np
import pandas as pd
import statsmodels.api as sm
from tinkoff.invest import Client

CONFIG_FILE = "../../settings.ini"
TICKER = 'SBER'  # Цифровой актив
TOKEN = os.environ["INVEST_TOKEN"]
client = Client(TOKEN)

# Получение Данных
def data_fr():
    futures_data = client.klines(symbol=TICKER, interval=client.KLINE_INTERVAL_5MINUTE, limit=100)
    temp_df = pd.DataFrame(futures_data, columns=['Opentime', 'Open', 'High', 'Low', 'Close', 'Volume',
                                                  'Closetime', 'Quotee',
                                                  'Number', 'TakerBuyBaseAssetVolume',
                                                  'TakerBuyQuoteAssetVolume', 'Ignore'])
    df = temp_df.loc[:, ['Opentime', 'Open', 'High', 'Low', 'Close', 'Volume']].astype(float)

    return df

# Подготовка данных для индикатора
def PrepareDF(DF):
    ohlc = DF.iloc[:, [0, 1, 2, 3, 4, 5]]
    ohlc.columns = ["date", "open", "high", "low", "close", "volume"]
    ohlc = ohlc.set_index('date')
    df = indBollingerBands(ohlc).reset_index()
    df['slope_up'] = indSlope(df['high'], 9)
    df['slope_low'] = indSlope(df['low'], 9)
    df['dif_up'] = df['high'].diff()
    df['dif_low'] = df['low'].diff()
    df['position_up'] = (df['high'] - df['upper'])
    df['position_low'] = (df['low'] - df['lower'])
    df = df.set_index('date')
    df = df.reset_index()
    return df

# Находим наклон ценовой линии для индикатора
def indSlope(series, n):
    array_sl = [j * 0 for j in range(n - 1)]

    for j in range(n, len(series) + 1):
        y = series[j - n:j]
        x = np.array(range(n))
        x_sc = (x - x.min()) / (x.max() - x.min())
        y_sc = (y - y.min()) / (y.max() - y.min())
        x_sc = sm.add_constant(x_sc)
        model = sm.OLS(y_sc, x_sc)
        results = model.fit()
        array_sl.append(results.params[-1])
    slope_angle = (np.rad2deg(np.arctan(np.array(array_sl))))
    return np.array(slope_angle)

# Расчет True Range и Average True Range индикатора
def indBollingerBands(source_DF, window=20, std_dev=3):
    df = pd.DataFrame(source_DF)
    # Рассчитываем скользящую среднюю
    df['sma'] = df['close'].rolling(window).mean()
    # Рассчитываем стандартное отклонение
    df['std'] = df['close'].rolling(window).std()
    # Рассчитываем верхнюю полосу Боллинджера
    df['upper'] = df['sma'] + (std_dev * df['std'])
    # Рассчитываем нижнюю полосу Боллинджера
    df['lower'] = df['sma'] - (std_dev * df['std'])
    # Ширина канала Болинджера
    df['chanal'] = df['upper'] - df['lower']
    return df

# локальный минимум/локальный максимум
def isLCC(DF, i):
    df = DF.copy()
    LCC = 0

    if df['low'][i] < df['low'][i + 1] and df['low'][i] < df['low'][i - 1] < df['low'][i + 1]:
        # найдено Дно
        LCC = i
    return LCC

def isHCC(DF, i):
    df = DF.copy()
    HCC = 0
    if df['high'][i] > df['high'][i + 1] and df['high'][i] > df['high'][i - 1] > df['high'][i + 1]:
        # найдена вершина
        HCC = i
    return HCC

# Функция для формирования сигнала для бота от индикатора
def check_if_signal():
    ohlc = data_fr()
    prepared_df = PrepareDF(ohlc)
    signalAtr = None

    i = len(prepared_df) - 1  # 99 - текущая незакрытая свеча, 98 - последняя закрытая свеча, нижняя или верхняя

    if isLCC(prepared_df, i - 1) > 0:
        print("Найдена дно")
        # найдено дно
        if prepared_df['position_low'][i - 1] < 10:
            # Пересечение нижней границы канала
            if prepared_df['chanal'][i - 1] > 100:
                # Ширина канала больше 100
                signalAtr = 2  # Если нет направления AutoTs то  Long

    if isHCC(prepared_df, i - 1) > 0:
        # найденная вершина
        if prepared_df['position_up'][i - 1] > 10:
            print("Найдена вершина")
            # Пересечение верхней границы канала
            if prepared_df['chanal'][i - 1] > 100:
                # Ширина канала больше 100
                signalAtr = 1  # Если нет направления AutoTs то Short
    # print('Сигнал от бота:', signalAtr)
    return signalAtr


import heapq as hq

def solution(arr, processes):
    answer = []

    rq = []
    wq = []
    twwait = 0

    for idx, each in enumerate(processes):
        readtf, start, lapse, *alias = each.split()
        if readtf == "read":
            hq.heappush(rq, (int(start), int(lapse), idx, 1, list(map(int, alias))))
        else:
            hq.heappush(rq, (int(start), int(lapse), idx, 0, list(map(int, alias))))

    yet = 0
    currtime = 0
    qing = []

    while rq:
        tc=0
        while rq[0][0] <= currtime:
            tc = 1
            currcand = hq.heappop(rq)
            if currcand[3]:
                hq.heappush(qing, currcand)
            else:
                hq.heappush(wq, currcand)
                yet += 1

        if rq and wq and yet:
            curring = hq.heappop(wq)
            print(curring)
            yet -= 1
            for i in range(curring[-1][0], curring[-1][1]+1):
                arr[i] = curring[-1][2]
            currtime = currtime + curring[1]
            tc = 1

        if rq and wq and not yet:
            curring = hq.heappop(qing)
            print("".join([str(arr[i]) for i in range(curring[-1][0], curring[-1][1]+1)]))
            currtime = max(currtime, curring[0]+curring[1])
            tc = 1

        if not tc:
            currtime = rq[0][0]
            #currtime += 1

        print("currtime", currtime)

    return answer

#읽기동시가능
#ing읽기가있으면쓰기는대기
#대기중인쓰기있으면읽기도대기

#초기 배열의 상태가 담긴 문자열 배열 arr과 읽기,
# 쓰기 작업 요청이 담긴 문자열 배열 processes가
#매개변수로 주어집니다. 읽기 작업에서 읽은 내용을
#processes에서 주어진 순서대로 정답 배열에 담은 뒤,
#배열이 전체 프로세스에 의해 사용된 시간을 정답
#배열의 마지막에 담아 return 하도록 solution
#함수를 완성해주세요.


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

# In[1]:

import sys
# Python 3.7 is required
assert sys.version_info >= (3,7)

import cv2 as cv
import numpy as np

# Make sure that optimization is enabled
if not cv.useOptimized():
    cv.setUseOptimized(True)

cv.useOptimized()

# In[3]:

import matplotlib.pyplot as plt

# # Weekly Activity

# 1. Convert the code chunk found under section Divide an image into smaller patches using cropping into a function with the following signature:
#
# crop_grid(img, num_horizontal_grid, num_vertical_grid, line_color)
# - img is the source image
# - num_horizontal_grid and num_vertical_grid are the number of patches along x and y axes.
# - line_color is the color of the grid line.
# - The output of the function should be image with grids
#
#

# In[ ]:

def crop_grid(img, num_horizontal_grid, num_vertical_grid, line_color):
    M, N = int(height/num_horizontal_patches), int(width/num_vertical_patches)

    x1, y1 = 0, 0

    for y in range(0, height, M):
        for x in range(0, width, N):
    #         if (height - y) < M or (width - x) < N:
    #             break

            y1 = y + M    # lower right coordinate that will be used to construct rectangle
            x1 = x + N

            # Check whether patch lower right coordinate exceeds image height and width
            if x1 >= width and y1 >= height:
                x1 = width - 1
                y1 = height - 1
                tile = img[y:height, x:width]
                cv.rectangle(img_copy, (x, y), (x1, y1),line_color , 1)
                #cv.imshow('tile', tile)
            # When patch lower right y-coordinate exceeds patch height
            elif y1 >= height:
                y1 = height - 1
                cv.rectangle(img_copy, (x, y), (x1, y1), line_color, 1)

            # When patch lower right x-coordinate exceeds patch width
            elif x1 >= width:
                x1 = width - 1
                cv.rectangle(img_copy, (x, y), (x1, y1), line_color, 1)

            else:
                cv.rectangle(img_copy, (x, y), (x1, y1), line_color, 1)

    cv.imshow('patched image', img_copy)
    cv.waitKey(0)
    cv.destroyAllWindows()

img = cv.imread('dog.jfif')
img_copy = img.copy()

height, width = img.shape[:2]
num_horizontal_grid = 2
num_vertical_grid = 3
line_color = (0, 255, 0)
crop_grid(img, num_horizontal_grid, num_vertical_grid, line_color)

# 2. Display image sequences of smooth transition of two images with different values of α. Refer to code in section "Image blending". Use "lena.jfif" and "coins.jfif" as the base images.

# In[ ]:

img1 = cv.imread('lena.jfif')
img2 = cv.imread('coins.jfif')

# Resize img2
new_shape = img1.shape[:2]
img2 = cv.resize(img2, new_shape)

dst1 = cv.addWeighted(img1, 0.25, img2, 0.75, 0)
dst2 = cv.addWeighted(img1, 0.75, img2, 0.25, 0)

cv.imshow('blend_image1', dst1)
cv.imshow('blend_image2', dst2)
cv.waitKey(0)
cv.destroyAllWindows()

# 3. Rotate image by 45 degrees without cropping the sides of the image. (Hint: There are 2 strategies to tackle these problems). Use "lena.jfif" as the input image.
#
#     - Use external libraries imutils.
#     - Modify the transformation matrix.
#

# In[ ]:

#import imutils
img = cv.imread('lena.jfif')

dst = imutils.rotate(img,45)

cv.imshow('rotated_image', dst)
cv.waitKey(0)
cv.destroyAllWindows()

# 4. Use the images with titles: "flower.jfif" and "native-bee.png". I want to put flower above an image. If I add two images, it will change color. If I blend it, I get a transparent effect. But I want it to be opaque. If it was a rectangular region, we could use the ROI as we did in the previous section. But flower is not a rectangular region. This is where bitwise operations, like AND, OR, NOT and XOR really come in handy. The associated functions are cv.bitwise_and(), cv.bitwise_or() and cv.bitwise_not(). You need to use cv.threshold function to segment the flower. Please refer to online documentation for more info. The result should resemble the following:

# In[ ]:

img1= cv.imread('native-bee.png')
img2= cv.imread('flower.jfif')

rows,cols,channels = img2.shape
roi = img1[0:rows,0:cols]
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret,mask = cv.threshold(img2gray,60,255,cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)
img2_fg = cv.bitwise_and(img2,img2,mask = mask)
result = cv.add(img1_bg,img2_fg)
img1[0:rows,0:cols] = result

cv.imshow("flower_bee", img1)
cv.waitKey(0)
cv.destroyAllWindows()

# In[ ]:


import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import AutoModel, AutoConfig
from torch.cuda.amp import autocast

class Model(nn.Module):
    def __init__(self, MODEL_NAME):
        super().__init__()

        self.model_config= AutoConfig.from_pretrained(MODEL_NAME)
        self.model_config.num_labels= 30
        self.model= AutoModel.from_pretrained(MODEL_NAME, config= self.model_config)
        self.hidden_dim= self.model_config.hidden_size # roberta hidden dim = 1024

        self.lstm= nn.LSTM(input_size= self.hidden_dim, hidden_size= self.hidden_dim, num_layers= 2, dropout= 0.2,
                            batch_first= True, bidirectional= True)
        self.fc= nn.Linear(self.hidden_dim*2, self.model_config.num_labels)

    @autocast()
    def forward(self, input_ids, attention_mask):
        # BERT output= (16, 244, 1024) (batch, seq_len, hidden_dim)
        output= self.model(input_ids= input_ids, attention_mask= attention_mask)[0]

        # LSTM last hidden, cell state shape : (2, 244, 1024) (num_layer, seq_len, hidden_size)
        hidden, (last_hidden, last_cell)= self.lstm(output)

        # (16, 1024) (batch, hidden_dim)
        cat_hidden= torch.cat((last_hidden[0], last_hidden[1]), dim= 1)
        logits= self.fc(cat_hidden)

        return {'logits': logits}


"""
Author: Yiqi (Nick) Zhao.

This file provides an example for the user to use the SMT solver created in this project in solving
the Partition Problem.
"""

from SMT_Solver.smt import *

def solve_partition(target_list, method = 'backtracking'):

    if len(target_list) == 0:
        return "UNSAT"

    # Formulate the SMT encoding.
    smt_variables = set()
    smt_encoding = dict()

    # Dictate the range of each variable and generate the temporary variables.
    index = 0
    list_vars = dict()
    summation = ""
    for i in range(len(target_list)):
        smt_variables.add(f"y{i}")
        list_vars[f"y{i}"] = i
        if i == len(target_list) - 1:
            summation += (f"y{i} * {target_list[i]}")
        else:
            summation += (f"y{i} * {target_list[i]} + ")
    smt_encoding["x" + str(index)] = ["eq", f"({summation}) * 2", sum(target_list)]
    index += 1

    # Make sure that the sum is not 0 and is not the total cardinality.
    summation = ""
    for i in range(len(target_list)):
        if i == len(target_list) - 1:
            summation += f"y{i}"
        else:
            summation += f"y{i} + "
    smt_encoding["x" + str(index)] = ["nq", summation, 0]
    index += 1
    smt_encoding["x" + str(index)] = ["nq", summation, len(target_list)]
    smt_variables = list(smt_variables)

    # Now, formulate the SAT encoding:
    sat_encoding = []
    if len(smt_encoding.keys()) == 1:
        sat_encoding = list(smt_encoding.keys())[0]
    else:
        for sat_node in smt_encoding:
            if len(sat_encoding) == 0:
                sat_encoding = sat_node
            else:
                sat_encoding = ["and", sat_node, sat_encoding]

    solution = solve_SMT(sat_encoding, smt_encoding, smt_variables, 0, 1, method = method)

    if solution == "UNSAT":
        return solution

    group_0 = []
    group_1 = []
    for node in solution:
        if solution[node] == 0:
            group_0.append(target_list[list_vars[node]])
        else:
            group_1.append(target_list[list_vars[node]])

    return group_0, group_1


# visualizations used in TEM-pipeline.ipynb
# Gio Ferro, Katie Partington, Akshay Trikha
# 1st January, 2021

import cv2 as cv                        # OpenCV for image processing
import matplotlib.pyplot as plt         # Matplotlib for visualizing
from mpl_toolkits.mplot3d import Axes3D # Axes3D for 3D visualization
import numpy as np                      # NumPy for quick maths

def display_images(images, titles, grayscales):
    """takes in list of images, list of titles, list of boolean grayscales and displays them"""
    if len(images) == 1:
        fig, axs = plt.subplots(1, 1, figsize=(10,10))
        plt.imshow(images[0], cmap=plt.cm.gray)
        axs.set_title(titles[0])
    else:
        fig, axs = plt.subplots(1, len(images), figsize=(15,15))

        # loop through images and display
        for i in range(len(images)):
            if grayscales[i]:
                axs[i].imshow(images[i], cmap=plt.cm.gray)
                axs[i].set_title(titles[i])
            else:
                axs[i].imshow(images[i])
                axs[i].set_title(titles[i])

# TODO: delete since it's context based?
def save_images(images, titles):
    for i in range(len(images)):
        plt.savefig(str(images[i]) + "_" + titles[i] + ".png", dpi=500)

def draw_point_contour(point, contour):
    """takes in a point and a contour and returns true if point is within contour and false otherwise"""
    # load new images
    color = cv.imread("./inputs/TES-36b-cropped.tif")
    gray = cv.cvtColor(color, cv.COLOR_BGR2GRAY)

    cv.drawContours(color, [contour], -1, (100, 255, 100), 2)
    color = cv.circle(color, point, 15, (255, 100, 100), 15)
    if cv.pointPolygonTest(contour, point, False) == 0.0:
        return True
    else:
        return False
    plt.imshow(color);

def draw_long_lengths(image, long_pairs):
    """draws long chord lengths on an image"""
    for long_pair in long_pairs:
        # cv.line(image, long_pair[1], long_pair[2], [255,255,0])
        cv.line(image, (long_pair[1][0], long_pair[1][1]), (long_pair[2][0], long_pair[2][1]), [255,255,0])

def draw_short_lengths(image, short_pairs):
    """draws short chord lengths on an image"""
    for short_pair in short_pairs:
        # cv.line(image, short_pair[0], short_pair[1], [0,255,255])
        cv.line(image, (short_pair[0][0], short_pair[0][1]), (short_pair[1][0], short_pair[1][1]), [0,255,255])

def split_image(gray_image, shape, display=False):
    """splits image into shape (# of vertical segments, # of horizontal segments)
       and returns image chunk boundaries (clockwise starting from top left), and modified image"""
    # shape = (1,1) means no transformation is performed
    # shape = (2,2) means image is split into 4 quadrants
    # TODO: only works for equal-sized chunks

    image_copy = gray_image.copy()

    rows = len(image_copy)
    cols = len(image_copy[0])

    # check if image is divisible equally into shape
    mod_rows = rows % shape[0]
    mod_cols = cols % shape[1]

    # if isn't crop rows and cols so that it is
    if mod_rows != 0:
        rows -= mod_rows
    if mod_cols != 0:
        cols -= mod_cols

    # calculate vertical and horizintal segment amounts
    vert_seg = int(rows/shape[0])
    hor_seg = int(cols/shape[1])

    # calculate and return sub-image boundaries
    boundaries = []
    for vert_i in range(0, shape[0]):
        for hor_i in range(0, shape[1]):
            boundaries += [[(hor_i * hor_seg, vert_i * vert_seg),
                           ((hor_i + 1) * hor_seg, vert_i * vert_seg),
                           ((hor_i + 1) * hor_seg, (vert_i + 1) * vert_seg),
                           (hor_i * hor_seg, (vert_i + 1) * vert_seg)]]

    # draw vertical lines
    for vert_i in range(1, shape[0]+1):
        cv.line(image_copy, (vert_i * hor_seg, 0), (vert_i * hor_seg, rows), [100,0,100], 10)
    # draw horizontal lines
    for hor_i in range(1, shape[1]+1):
        cv.line(image_copy, (0, hor_i * vert_seg), (cols, hor_i * vert_seg), [100,0,100], 10)

    if display:
        display_images([image_copy], ["Partitioned Image"], [1])

    return boundaries, image_copy

def layer_render(particles, layer_info, rendering_type="wireframe"):
    """renders a dictionary of particles"""
    fig1 = plt.figure(1)
    fig2 = plt.figure(2)
    fig3 = plt.figure(3)

    ax = fig1.add_subplot(111, projection='3d')
    # ax = Axes3D(fig1)
    ax.set_xlabel("$X$", fontsize = 20, rotation = 150)
    ax.set_ylabel("$Y$", fontsize = 20, rotation = 150)
    ax.set_zlabel("$Z$", fontsize = 20, rotation = 150)

    ay = fig2.add_subplot(111, projection='3d')
    # ay = Axes3D(fig2)
    ay.set_xlabel("$X$", fontsize = 20, rotation = 150)
    ay.set_ylabel("$Y$", fontsize = 20, rotation = 150)
    ay.set_zlabel("$Z$", fontsize = 20, rotation = 150)

    az = fig3.add_subplot(111, projection='3d')
    # az = Axes3D(fig3)
    az.set_xlabel("$X$", fontsize = 20, rotation = 150)
    az.set_ylabel("$Y$", fontsize = 20, rotation = 150)
    az.set_zlabel("$Z$", fontsize = 20, rotation = 150)

    ax.view_init(elev=0, azim=0)
    ay.view_init(elev=0, azim=-90)
    az.view_init(elev=90, azim=-90)

    for particle in particles:
        # get particle data
        particle_data = particles[particle]
        # if the particle has an x and y position, a, b, and c radii, and an angle
        if len(particle_data) == 6:
            # extract particle info
            center_x = particle_data[0][1]
            center_y = particle_data[1][1]
            a_radius = particle_data[2][1]
            b_radius = particle_data[4][1]
            c_radius = particle_data[5][1]
            theta = particle_data[3][1]
            center_z = layer_info[4]+1

            # plot the ellipsoid
            u = np.linspace(0, 2 * np.pi, 100)
            v = np.linspace(0, np.pi, 100)

            # converting theta from degrees to radians
            theta = theta/180*np.pi

            x1_1 = np.outer(a_radius * np.cos(u), np.sin(v)) + center_x
            y1_1 = np.outer(b_radius * np.sin(u), np.sin(v)) + center_y
            z = np.outer(c_radius * np.ones(np.size(u)), np.cos(v)) + center_z
            x = np.cos(theta) * (x1_1 - center_x) - np.sin(theta) * (y1_1 - center_y) + center_x
            y = np.sin(theta) * (x1_1 - center_x) + np.cos(theta) * (y1_1 - center_y) + center_y

            if rendering_type == "surface":
            #colored ellipsoid -- here the particles look like colored balloons: pretty but takes a couple extra seconds to run.
                ax.plot_surface(x, y, z, linewidth=0.0)
                ay.plot_surface(x, y, z, linewidth=0.0)
                az.plot_surface(x, y, z, linewidth=0.0)

            if rendering_type == "wireframe":
            #wireframe rendering -- this is a mesh sort of look. Runs more quickly.
                ax.plot_wireframe(x, y, z, rstride=25, cstride=25, color='b', alpha=0.3)
                ay.plot_wireframe(x, y, z, rstride=25, cstride=25, color='b', alpha=0.3)
                az.plot_wireframe(x, y, z, rstride=25, cstride=25, color='b', alpha=0.3)

    ax.set_zlim(layer_info[2], layer_info[0])
    ay.set_zlim(layer_info[3], layer_info[0])
    az.set_zlim(0, layer_info[0])
    plt.xlim([layer_info[2], layer_info[0]])
    plt.ylim([layer_info[3], layer_info[0]])

    plt.show()

def composite_render(composite_particles, composite_info, rendering_type="wireframe"):
    """renders a dictionary of particles"""
    fig4 = plt.figure(4)
    fig5 = plt.figure(5)
    fig6 = plt.figure(6)

    ax2 = fig4.add_subplot(111, projection='3d')
    # ax = Axes3D(fig1)
    ax2.set_xlabel("$X$", fontsize = 20, rotation = 150)
    ax2.set_ylabel("$Y$", fontsize = 20, rotation = 150)
    ax2.set_zlabel("$Z$", fontsize = 20, rotation = 150)

    ay2 = fig5.add_subplot(111, projection='3d')
    # ay = Axes3D(fig2)
    ay2.set_xlabel("$X$", fontsize = 20, rotation = 150)
    ay2.set_ylabel("$Y$", fontsize = 20, rotation = 150)
    ay2.set_zlabel("$Z$", fontsize = 20, rotation = 150)

    az2 = fig6.add_subplot(111, projection='3d')
    # az = Axes3D(fig3)
    az2.set_xlabel("$X$", fontsize = 20, rotation = 150)
    az2.set_ylabel("$Y$", fontsize = 20, rotation = 150)
    az2.set_zlabel("$Z$", fontsize = 20, rotation = 150)

    ax2.view_init(elev=0, azim=0)
    ay2.view_init(elev=0, azim=-90)
    az2.view_init(elev=90, azim=-90)

    for particle in composite_particles:
        # get particle data
        particle_data = composite_particles[particle]
        # if the particle has an x and y position, a, b, and c radii, and an angle
        if len(particle_data) == 7:
            # extract particle info
            center_x = particle_data[0][1]
            center_y = particle_data[1][1]
            center_z = particle_data[6][1]
            a_radius = particle_data[2][1]
            b_radius = particle_data[4][1]
            c_radius = particle_data[5][1]
            theta = particle_data[3][1]

            # plot the ellipsoid
            u = np.linspace(0, 2 * np.pi, 100)
            v = np.linspace(0, np.pi, 100)

            # converting theta from degrees to radians
            theta = theta/180*np.pi

            x1_1 = np.outer(a_radius * np.cos(u), np.sin(v)) + center_x
            y1_1 = np.outer(b_radius * np.sin(u), np.sin(v)) + center_y
            z = np.outer(c_radius * np.ones(np.size(u)), np.cos(v)) + center_z
            x = np.cos(theta) * (x1_1 - center_x) - np.sin(theta) * (y1_1 - center_y) + center_x
            y = np.sin(theta) * (x1_1 - center_x) + np.cos(theta) * (y1_1 - center_y) + center_y

            if rendering_type == "surface":
            #colored ellipsoid -- here the particles look like colored balloons: pretty but takes a couple extra seconds to run.
                ax2.plot_surface(x, y, z, linewidth=0.0)
                ay2.plot_surface(x, y, z, linewidth=0.0)
                az2.plot_surface(x, y, z, linewidth=0.0)

            if rendering_type == "wireframe":
            #wireframe rendering -- this is a mesh sort of look. Runs more quickly.
                ax2.plot_wireframe(x, y, z, rstride=25, cstride=25, color='b', alpha=0.3)
                ay2.plot_wireframe(x, y, z, rstride=25, cstride=25, color='b', alpha=0.3)
                az2.plot_wireframe(x, y, z, rstride=25, cstride=25, color='b', alpha=0.3)

    ax2.set_zlim(composite_info[4], (composite_info[4]+composite_info[1]))
    ay2.set_zlim(composite_info[5], (composite_info[4]+composite_info[1]))
    az2.set_zlim(0, (composite_info[4]+composite_info[1]))
    plt.xlim([composite_info[4], composite_info[4]+composite_info[1]])
    plt.ylim([composite_info[5], composite_info[4]+composite_info[1]])

    plt.show()

def draw_merged_contours(image, merge_contours):
    """draws the contours resulting from merging agglomerates and individual particles"""
    for particle in merge_contours:
        for pixel in merge_contours[particle]:
            image[pixel[1]][pixel[0]] = [255, 0, 0]

def annotate_TEM(color_image, long_pairs, short_pairs, merge_contour_colors):
    """draws contours, short, and long chord lengths on TEM image"""
    annotated_image = color_image.copy()
    draw_long_lengths(annotated_image, long_pairs)
    draw_short_lengths(annotated_image, short_pairs)
    draw_merged_contours(annotated_image, merge_contour_colors)

    return annotated_image


"""
Задача 5. Вагоны в электричке пронумерованы
натуральными числами, начиная с 1 (при этом
иногда вагоны нумеруются от "головы" поезда,
а иногда с "хвоста"; это зависит от того,
в какую сторону едет электричка). В каждом
вагоне написан его номер. Витя сел в i-й
вагон от головы поезда и обнаружил, что его
вагон имеет номер j. Он хочет определить,
сколько всего вагонов в электричке. Напишите
программу, которая будет это делать или сообщать,
что без допольнительной информации это сделать
невозможно
Input: 3 4(ввод на разных строках)
output: 6
"""

# первый вариант решения задачи
i = int(input('В какой по счету вагон сел Виктор: '))
j = int(input('Номер вагона, который Виктор увидел внутри: '))
if i != j:
    print('Количество вагонов в поезде', i + j - 1)
else:
    print('Данных недостаточно для расчета количества вагонов')

# второй вариант решения задачи
i, j = int(input()), int(input())
if i != j:
    print(i + j - 1)
else:
    print('Данных недостаточно для расчета')


import os
import signal
import time

import requests
from nostr_sdk import Event

from nostr_dvm.utils.definitions import EventDefinitions
from nostr_dvm.utils.mediasource_utils import check_source_type, media_source
from nostr_dvm.utils.nostr_utils import get_event_by_id, get_referenced_event_by_id

def get_task(event, client, dvm_config):
    try:
        if event.kind() == EventDefinitions.KIND_NIP90_GENERIC:  # use this for events that have no id yet, inclufr j tag
            for tag in event.tags():
                if tag.as_vec()[0] == 'j':
                    return tag.as_vec()[1]
            else:
                return "unknown job: " + event.as_json()
        elif event.kind() == EventDefinitions.KIND_DM:  # dm
            for tag in event.tags():
                if tag.as_vec()[0] == 'j':
                    return tag.as_vec()[1]
            else:
                return "unknown job: " + event.as_json()

        # This looks a bit more complicated, but we do several tasks for text-extraction in the future
        elif event.kind() == EventDefinitions.KIND_NIP90_EXTRACT_TEXT:
            for tag in event.tags():
                if tag.as_vec()[0] == "i":
                    if tag.as_vec()[2] == "url":
                        file_type = check_url_is_readable(tag.as_vec()[1])
                        print(file_type)
                        if file_type == "pdf":
                            return "pdf-to-text"
                        elif file_type == "audio" or file_type == "video":
                            return "speech-to-text"
                        elif file_type == "image":
                            return "image-to-text"
                        else:
                            return "unknown job"
                    elif tag.as_vec()[2] == "event":
                        evt = get_event_by_id(tag.as_vec()[1], client=client, config=dvm_config)
                        if evt is not None:
                            if evt.kind() == 1063:
                                for tg in evt.tags():
                                    if tg.as_vec()[0] == 'url':
                                        file_type = check_url_is_readable(tg.as_vec()[1])
                                        if file_type == "pdf":
                                            return "pdf-to-text"
                                        elif file_type == "audio" or file_type == "video":
                                            return "speech-to-text"
                                        else:
                                            return "unknown job"
                            else:
                                return "unknown type"
                    else:
                        return "unknown job"
        elif event.kind() == EventDefinitions.KIND_NIP90_GENERATE_IMAGE:
            has_image_tag = False
            has_text_tag = False
            for tag in event.tags():
                if tag.as_vec()[0] == "i":
                    if tag.as_vec()[2] == "url":
                        file_type = check_url_is_readable(tag.as_vec()[1])
                        if file_type == "image":
                            has_image_tag = True
                            print("found image tag")
                    elif tag.as_vec()[2] == "job":
                        evt = get_referenced_event_by_id(event_id=tag.as_vec()[1], kinds=
                        [EventDefinitions.KIND_NIP90_RESULT_EXTRACT_TEXT,
                         EventDefinitions.KIND_NIP90_RESULT_TRANSLATE_TEXT,
                         EventDefinitions.KIND_NIP90_RESULT_SUMMARIZE_TEXT],
                                                         client=client,
                                                         dvm_config=dvm_config)
                        if evt is not None:
                            file_type = check_url_is_readable(evt.content())
                            if file_type == "image":
                                has_image_tag = True
                    elif tag.as_vec()[2] == "text":
                        has_text_tag = True

            if has_image_tag:
                return "image-to-image"
            elif has_text_tag and not has_image_tag:
                return "text-to-image"
        #  TODO if a task can consist of multiple inputs add them here
        #  This is not ideal. Maybe such events should have their own kind

        #  else if kind is supported, simply return task
        else:

            for dvm in dvm_config.SUPPORTED_DVMS:
                if dvm.KIND == event.kind():
                    return dvm.TASK
    except Exception as e:
        print("Get task: " + str(e))

    return "unknown type"

def is_input_supported_generic(tags, client, dvm_config) -> bool:
    # Handle malformed tags, missing events etc here.
    try:
        for tag in tags:
            if tag.as_vec()[0] == 'i':
                if len(tag.as_vec()) < 3:
                    print("Job Event missing/malformed i tag, skipping..")
                    return False
                else:
                    input_value = tag.as_vec()[1]
                    input_type = tag.as_vec()[2]
                    if input_type == "event":
                        evt = get_event_by_id(input_value, client=client, config=dvm_config)
                        if evt is None:
                            print("Event not found")
                            return False
                    # TODO check_url_is_readable might be more relevant per task in the future
                    # if input_type == 'url' and check_url_is_readable(input_value) is None:
                    #    print("Url not readable / supported")
                    #    return False

        return True
    except Exception as e:
        print("Generic input check: " + str(e))

def check_task_is_supported(event: Event, client, config=None):
    try:
        dvm_config = config
        # Check for generic issues, event maformed, referenced event not found etc..
        if not is_input_supported_generic(event.tags(), client, dvm_config):
            return False, ""

        # See if current dvm supports the task
        task = get_task(event, client=client, dvm_config=dvm_config)
        if task not in (x.TASK for x in dvm_config.SUPPORTED_DVMS):
            return False, task
        # See if current dvm can handle input for given task
        for dvm in dvm_config.SUPPORTED_DVMS:
            if dvm.TASK == task:
                if not dvm.is_input_supported(event.tags()):
                    return False, task
        return True, task

    except Exception as e:
        print("Check task: " + str(e))

def check_url_is_readable(url):
    if not str(url).startswith("http"):
        return None

    source = check_source_type(url)
    type = media_source(source)

    if type == "url":
        # If link is comaptible with one of these file formats, move on.
        req = requests.get(url)
        content_type = req.headers['content-type']
        if content_type == 'audio/x-wav' or str(url).endswith(".wav") or content_type == 'audio/mpeg' or str(
                url).endswith(
                ".mp3") or content_type == 'audio/ogg' or str(url).endswith(".ogg"):
            return "audio"
        elif (content_type == 'image/png' or str(url).endswith(".png") or content_type == 'image/jpg' or str(
                url).endswith(
                ".jpg") or content_type == 'image/jpeg' or str(url).endswith(".jpeg") or content_type == 'image/png' or
              str(url).endswith(".png")):
            return "image"
        elif content_type == 'video/mp4' or str(url).endswith(".mp4") or content_type == 'video/avi' or str(
                url).endswith(
                ".avi") or content_type == 'video/mov' or str(url).endswith(".mov"):
            return "video"
        elif (str(url)).endswith(".pdf"):
            return "pdf"
    else:
        return type

    # Otherwise we will not offer to do the job.
    return None

def get_amount_per_task(task, dvm_config, duration=1):
    #  duration is either static 1 (for images etc) or in seconds by default (e.g. audio/video)
    for dvm in dvm_config.SUPPORTED_DVMS:  # this is currently just one
        if dvm.TASK == task:
            amount = dvm.FIX_COST + (dvm.PER_UNIT_COST * duration)
            return amount
    else:
        print("[" + dvm_config.SUPPORTED_DVMS[
            0].NAME + "] Task " + task + " is currently not supported by this instance, skipping")
        return None

def keep_alive():
    try:
        while True:
            time.sleep(10)
    except KeyboardInterrupt:
        os.kill(os.getpid(), signal.SIGKILL)
        exit(1)