from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.dependency.usecase import user_usecase

from app.presentation.form.user import UserCreateModel, UserReadModel
from app.usecase.user import UserUseCase

from app.dependency.repository import user_repository

router = APIRouter(
    prefix='/users',
    tags=["users"],
)

@router.post("/")
def create_user(
    user: UserCreateModel,
    useCase: UserUseCase = Depends(user_usecase)
):
    # db_user = useCase.get_by_email(email=user.email)
    # if db_user:
    #     raise HTTPException(status_code=400, detail="Email already registered")
    return useCase.register(user=user)

@router.get("/me")
async def read_user_me():
    return { "user_id": "the current user" }

@router.get("/{user_id}", response_model=UserReadModel)
def read_user(
    user_id: str,
    useCase: UserUseCase = Depends(user_usecase)
):
    user = useCase.get(user_id=user_id)
    if user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return user


"""Change tasks to work with rankinglist tests

Revision ID: 665d18e875e9
Revises: 91e86031f915
Create Date: 2020-06-06 14:26:50.428429

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '665d18e875e9'
down_revision = '91e86031f915'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('task', sa.Column('rankinglist_test_id', sa.Integer(), nullable=True))
    op.create_foreign_key(None, 'task', 'rankinglist_tests', ['rankinglist_test_id'], ['id'])
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(None, 'task', type_='foreignkey')
    op.drop_column('task', 'rankinglist_test_id')
    # ### end Alembic commands ###


import sys
input=sys.stdin.readline
# 1시간 이상
# 처음에 정렬하고 이분 탐색해서 오답이 자꾸 나왔음
# start,last는 b의 최댓값,b의 합
# start가 b의 최댓값인 이유:만약 1이면 모든 동영상을 블루레이에 담을 수 없으니(1이면 길이 1이하의 동영상만 블루레이에 담을 수 있음)
# b의 최댓값이 모든 동영상을 블루레이에 담을 수 있는 최솟값
# last가 b의 합인 이유:블루레이의 개수가 1이라면 모든 동영상을 하나의 블루레이에 담아야하니 sum(b) 혹은 10**9를 써도 무방
n,m=map(int,input().split())
b=list(map(int,input().split()))
start,last=max(b),10**9
while start<=last:
    mid=(start+last)//2
    cnt,total=0,0
    # 블루레이 개수, 블루레이 크기
    for value in b:
        if total+value>mid:
            # 크기를 더하기 전에 mid를 넘는지 먼저 검사
            # 넘으면
            cnt+=1
            # 다른 블루레이에 나눠 담아야하므로 cnt+=1
            total=0
            # 다른 블루레이에 나눠 담아야하므로 total=0
        total+=value
        # 블루레이별로 나눈 뒤 크기를 더함
    if total!=0:
        # 마지막 동영상까지의 합이 mid보다 작을 경우
        # total은 0이 아닌데 다른 블루레이에 담아야하니 cnt+=1
        cnt+=1
    if cnt>m:
        # 블루레이 개수가 m보다 클 경우
        start=mid+1
        # 블루레이 크기를 늘려서 더 많이 담아야 개수를 줄이므로 start=mid+1
    else:
        # 블루레이 개수가 m보다 작거나 같을 경우
        last=mid-1
        # 블루레이 크기를 줄여도 조건을 만족하는지 확인하기 위해 last=mid-1
print(start)


import numpy as np
import h5py
import matplotlib.pyplot as plt
from testCases_v3 import *
from dnn_utils_v2 import sigmoid, sigmoid_backward, relu, relu_backward
import random

plt.rcParams['figure.figsize'] = (5.0, 4.0)  # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

np.random.seed(1)

# Initialization of 2 layer neural network
# input --> hidden1 --> output

def initialize_parameters(n_x, n_h, n_y):
    np.random.seed(1)

    W1 = np.random.randn(n_h, n_x) * 0.01
    b1 = np.zeros((n_h, 1))
    W2 = np.random.randn(n_y, n_h) * 0.01
    b2 = np.zeros((n_y, 1))

    assert(W1.shape == (n_h, n_x))
    assert(b1.shape == (n_h, 1))
    assert(W2.shape == (n_y, n_h))
    assert(b2.shape == (n_y, 1))

    parameters = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2}

    return parameters

# TEST
parameters = initialize_parameters(3, 2, 1)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

# Initialization of L layer neural network
# input --> hidden{L} --> output

def initialize_parameters_deep(layer_dims):
    np.random.seed(3)
    parameters = {}
    L = len(layer_dims)

    for l in range(1, L):
        parameters['W' + str(l)] = (np.random.randn(layer_dims[l],
                                                    layer_dims[l - 1])) * (0.01)
        parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))

    return parameters

# TEST
parameters = initialize_parameters_deep([5, 4, 3])
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

# FORWARD PROPAGATION
# 1. linear
# 2. linear -> activation(sigmoid + relu)
# 3. linear -> relu {L-1} --> linear ->sigmoid

def linear_forward(A, w, b):
    Z = np.dot(w, A) + b
    cache = (A, w, b)  # Store A, w, b in a tuple named cache

    return Z, cache  # This is actually a tuple (Z, cache)

# TEST
A, W, b = linear_forward_test_case()
Z, linear_cache = linear_forward(A, W, b)
print("Z = " + str(Z))

def linear_activation_forward(A_prev, w, b, activation):
    if activation == "sigmoid":
        Z, linear_cache = linear_forward(A_prev, w, b)
        A, activation_cache = sigmoid(Z)

    elif activation == "relu":
        Z, linear_cache = linear_forward(A_prev, w, b)
        A, activation_cache = relu(Z)

    cache = (linear_cache, activation_cache)

    return A, cache

# TEST
A_prev, W, b = linear_activation_forward_test_case()
A, linear_activation_cache = linear_activation_forward(A_prev, W, b,
                                                       activation="sigmoid")
print("With sigmoid: A = " + str(A))
A, linear_activation_cache = linear_activation_forward(A_prev, W, b,
                                                       activation="relu")
print("With ReLU: A = " + str(A))

def L_model_forward(X, parameters):
    caches = []
    A = X
    L = len(parameters) // 2

    for l in range(1, L):
        A_prev = A
        A, cache = linear_activation_forward(A_prev,
                                             parameters['W' + str(l)],
                                             parameters['b' + str(l)],
                                             activation='relu')
        caches.append(cache)

    AL, cache = linear_activation_forward(A,
                                          parameters['W' + str(L)],
                                          parameters['b' + str(L)],
                                          activation='sigmoid')
    caches.append(cache)

    return AL, caches

# TEST
X, parameters = L_model_forward_test_case_2hidden()
AL, caches = L_model_forward(X, parameters)
print("AL = " + str(AL))
print("Length of caches list = " + str(len(caches)))

def compute_cost(AL, Y):
    m = Y.shape[1]
    cost = (-1/m) * (np.dot(Y, (np.log(AL)).T) + np.dot((1 - Y),
                                                        (np.log(1 - AL).T)))

    cost = np.squeeze(cost)      # e.g. this turns [[17]] into 17
    assert(cost.shape == ())

    return cost

Y, AL = compute_cost_test_case()

print("cost = " + str(compute_cost(AL, Y)))

# BACK PROPAGATION
# 1. linear backward
# 2. linear -> activation backward
# 3. Linear --> relu (L-1) --> linear --> sigmoid backward

def linear_backward(dZ, cache):
    A_prev, W, b = cache
    m = A_prev.shape[1]

    dW = (1/m) * np.dot(dZ, cache[0].T)
    db = (1/m) * np.squeeze(np.sum(dZ, axis=1, keepdims=True))
    dA_prev = np.dot(cache[1].T, dZ)
    return dA_prev, dW, db

# TEST
# Set up some test inputs
dZ, linear_cache = linear_backward_test_case()

dA_prev, dW, db = linear_backward(dZ, linear_cache)
print ("dA_prev = " + str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))

def linear_activation_backward(dA, cache, activation):
    linear_cache, activation_cache = cache

    if activation == "relu":
        dZ = relu_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)

    elif activation == "sigmoid":
        dZ = sigmoid_backward(dA, activation_cache)
        dA_prev, dW, db = linear_backward(dZ, linear_cache)

    return dA_prev, dW, db

# TEST
AL, linear_activation_cache = linear_activation_backward_test_case()

dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "sigmoid")
print ("\n\n\n\n\n\n\nsigmoid:")
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db) + "\n")

dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "relu")
print ("relu:")
print ("dA_prev = " + str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))

def L_model_backward(AL, Y, caches):
    grads = {}
    L = len(caches)  # the number of layers
    m = AL.shape[1]
    Y = Y.reshape(AL.shape)  # after this line, Y is the same shape as AL

    dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))

    current_cache = caches[L-1]
    grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, activation = "sigmoid")

    for l in reversed(range(L-1)):
        current_cache = caches[l]
        dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, activation = "relu")
        grads["dA" + str(l + 1)] = dA_prev_temp
        grads["dW" + str(l + 1)] = dW_temp
        grads["db" + str(l + 1)] = db_temp

    return grads

# TEST
AL, Y_assess, caches = L_model_backward_test_case()
grads = L_model_backward(AL, Y_assess, caches)
print ("dW1 = " + str(grads["dW1"]))
print ("db1 = " + str(grads["db1"]))
print ("dA1 = " + str(grads["dA1"]))

def update_parameters(parameters, grads, learning_rate):
    L = len(parameters) // 2
    for l in range(L):
        parameters["W" + str(l+1)] = parameters["W" + str(l+1)] - learning_rate * grads["dW" + str(l+1)]
        parameters["b" + str(l+1)] = parameters["b" + str(l+1)] - learning_rate * grads["db" + str(l+1)]

    return parameters

# TEST
parameters, grads = update_parameters_test_case()
parameters = update_parameters(parameters, grads, 0.1)

print ("W1 = " + str(parameters["W1"]))
print ("b1 = " + str(parameters["b1"]))
print ("W2 = " + str(parameters["W2"]))
print ("b2 = " + str(parameters["b2"]))


from random import randint

class Research:
    def __init__(self, file):
        self.file = file

    def file_reader(self, has_header=True):
        with open(self.file, "r") as f:
            res = []
            arr = f.read().split('\n')
            if has_header:
                if len(arr[0].split(',')) != 2:
                    raise Exception("Invalid header in csv")
                arr = arr[1:]
            for i in arr:
                if i != "0,1" and i != "1,0":
                    raise Exception("Invalid data in csv")
                else:
                    res.append(list(eval(i)))
            return res

    class Calculations:

        def __init__(self, lst):
            self.lst = lst

        def counts(self):
            a = 0
            b = 0
            for i in self.lst:
                if i[0] == 0:
                    a += 1
                else:
                    b += 1
            return a, b

        def fractions(self):
            ln = len(self.lst)
            a, b = self.counts()
            return a / ln * 100, b / ln * 100

class Analytics(Research.Calculations):
    def predict_random(self, k):
        pattern = [[0,1], [1, 0]]
        res = []
        for _ in range(0, k):
            res.append(pattern[randint(0, 1)].copy())
        return res

    def predict_last(self):
        return self.lst[-1]

    def save_file(self, data, file, exp):
        with open(f'{file}.{exp}', "w") as f:
            f.write(data)


#Edouard d.S.d.L 18072

import os
import sys, random, pygame
from pygame.locals import *
from abc import ABC, abstractmethod
from timeit import Timer

#Initialize pygame and setting up the window
os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()

WIDTH = 1080
HEIGHT = 720
FPS = 30
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("EcosysPOO")

#Declaring the color variables used to differenciate different lifeforms
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
dark_green = (0, 50, 0)
brown = (100, 40, 0)
gender = ["male", "female"]

#Initializing the base class called lifeforms which all organisms in the ecosystem will descend
class lifeforms(pygame.sprite.Sprite, ABC):
    def __init__(self, width, height, pos_x, pos_y):
        pygame.sprite.Sprite.__init__(self)
        self.hp = 10
        self.dead = False
        self.energy = 100
        self.isOrgWas  = False
        self.image = pygame.Surface([width, height]).convert_alpha()
        self.width = width
        self.height = height
        self.rect = self.image.get_rect()
        self.rect.center = [pos_x, pos_y]

    def isDead(self):
        if self.hp >= 0:
           return True
        else:
            return False

    def get_posX(self):
        return self.rect.x

    def get_posY(self):
        return self.rect.y

    def set_posX(self, value):
        self.rect.x = value

    def set_posY(self, value):
        self.rect.y = value

    def set_color(self, value):
        self.color = value

    def set_meat(self):
        self.image.fill(red)
        self.color = red

    def set_waste(self):
        self.image.fill(brown)
        self.color = brown

    def set_black(self):
        self.image.fill(black)
        self.color = black

    def get_color(self):
        return self.color

    def set_hp(self, value):
        i = value
        self.hp += i

    def get_hp(self):
        return self.hp

    def set_energy(self, value):
        i = value
        self.energy += i

    def get_energy(self):
        return self.energy

    def get_width(self):
        return self.width

    def get_height(self):
        return self.height

    def set_X(self, value):
        self.rect.x = value

#Initializing the base class for the two main animalforms there are going to be predator and prey
class Animals(lifeforms):
    def __init__(self, width, height, pos_x, pos_y):
        lifeforms.__init__(self, width, height, pos_x, pos_y)
        self.isGender = random.choice(gender)
        self.isFood = False
        self.speed = random.randrange(1, 5)
        self.move = [None, None]
        self.direction = None
        self.preg = False

    def set_direction(self):
        self.direction *= -1

    def get_direction(self):
        return self.direction

    def get_gender(self):
        return self.isGender

    def pregnant(self):
        self.preg = True

    def not_pregnant(self):
        self.preg = False

    def get_pregnant(self):
        return self.preg

    def get_isFood(self):
        return self.isFood

    def set_isFood(self, value):
        self.isFood = value

#here we are defining the classes of the two types of animals there are going to be predators and herbivores
class Predator(Animals):
    def __init__(self, width, height, pos_x, pos_y):
        lifeforms.__init__(self, width, height, pos_x, pos_y)
        Animals.__init__(self, width, height, pos_x, pos_y)
        pygame.sprite.Sprite.__init__(self)

        self.color = white
        self.image.fill(self.color)
        self.zoneVisionP = pygame.Rect(self.rect.x-10, self.rect.y-10, width+20, height+20)

    def set_x(self, value):
        self.zoneVisionP.x = value

    def set_y(self, value):
        self.zoneVisionP.y = value

    def get_x(self):
        return self.zoneVisionP.x

    def get_y(self):
        return self.zoneVisionP.y
 #here is an update on the state of the animal and to add a random movement in any direction
    def update(self):
        if self.hp > 0:
            self.energy -= 0.05
            if self.energy <= 0 :
                self.energy = 0
                if self.hp > 0:
                    self.hp -= 0.5

            directions = {"S":((-1,2),(1,self.speed)),"SW":((-self.speed,-1),(1,self.speed)),"W":((-self.speed,-1),(-1,2)),"NW":((-self.speed,-1),(-self.speed,-1)),"N":((-1,2),(-self.speed,-1)),"NE":((1,self.speed),(-self.speed,-1)),"E":((1,self.speed),(-1,2)),"SE":((1,self.speed),(1,self.speed))} #((min x, max x)(min y, max y))
            directionsName = ("S","SW","W","NW","N","NE","E","SE") #possible directions
            if random.randrange(0,5) == 2: #move about once every 5 frames
                if self.direction == None: #if no direction is set, set a random one
                    self.direction = random.choice(directionsName)
                else:
                    a = directionsName.index(self.direction) #get the index of direction in directions list
                    b = random.randrange(a-1,a+2) #set the direction to be the same, or one next to the current direction
                    if b > len(directionsName)-1: #if direction index is outside the list, move back to the start
                        b = 0
                        self.direction = directionsName[b]
                    self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                    self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.rect.x < 5 or self.rect.x > WIDTH - 5 or self.rect.y < 5 or self.rect.y > HEIGHT - 5: #if cell is near the border of the screen, change direction
                if self.rect.x < 5:
                    self.direction = "E"
                elif self.rect.x > WIDTH - 5:
                    self.direction = "W"
                elif self.rect.y < 5:
                    self.direction = "S"
                elif self.rect.y > HEIGHT - 5:
                    self.direction = "N"
                self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.move[0] != None: #add the relative coordinates to the cells coordinates
                self.rect.x += self.move[0]
                self.zoneVisionP.x += self.move[0]
                self.rect.y += self.move[1]
                self.zoneVisionP.y += self.move[1]

        elif self.hp <= 0 and self.isFood == False:
            self.hp = 0
            self.image.fill(red)
            self.color = red

class Herbivore(Animals):
    def __init__(self, width, height, pos_x, pos_y):
        Animals.__init__(self, width, height, pos_x, pos_y)
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height]).convert_alpha()
        self.color = dark_green
        self.image.fill(self.color)
        self.zoneVisionH = pygame.Rect(self.rect.x-10, self.rect.y-10, self.width+20, self.height+20)

    def set_x(self, value):
        self.zoneVisionH.x = value

    def set_y(self, value):
        self.zoneVisionH.y = value

    def get_x(self):
        return self.zoneVisionH.x

    def get_y(self):
        return self.zoneVisionH.y
 #here is an update on the state of the animal and to add a random movement in any direction
    def update(self):
        if self.hp > 0:
            self.energy -= 0.05
            if self.energy <= 0 :
                self.energy = 0
                if self.hp > 0:
                    self.hp -= 0.5
            directions = {"S":((-1,2),(1,self.speed)),"SW":((-self.speed,-1),(1,self.speed)),"W":((-self.speed,-1),(-1,2)),"NW":((-self.speed,-1),(-self.speed,-1)),"N":((-1,2),(-self.speed,-1)),"NE":((1,self.speed),(-self.speed,-1)),"E":((1,self.speed),(-1,2)),"SE":((1,self.speed),(1,self.speed))} #((min x, max x)(min y, max y))
            directionsName = ("S","SW","W","NW","N","NE","E","SE") #possible directions
            if random.randrange(0,5) == 2: #move about once every 5 frames
                if self.direction == None: #if no direction is set, set a random one
                    self.direction = random.choice(directionsName)
                else:
                    a = directionsName.index(self.direction) #get the index of direction in directions list
                    b = random.randrange(a-1,a+2) #set the direction to be the same, or one next to the current direction
                    if b > len(directionsName)-1: #if direction index is outside the list, move back to the start
                        b = 0
                    self.direction = directionsName[b]
                self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.rect.x < 5 or self.rect.x > WIDTH - 5 or self.rect.y < 5 or self.rect.y > HEIGHT - 5: #if cell is near the border of the screen, change direction
                if self.rect.x < 5:
                    self.direction = "E"
                elif self.rect.x > WIDTH - 5:
                    self.direction = "W"
                elif self.rect.y < 5:
                    self.direction = "S"
                elif self.rect.y > HEIGHT - 5:
                    self.direction = "N"
                self.move[0] = random.randint(directions[self.direction][0][0],directions[self.direction][0][1])  #change relative x to a random number between min x and max x
                self.move[1] = random.randint(directions[self.direction][1][0],directions[self.direction][1][1])  #change relative x to a random number between min x and max x
            if self.move[0] != None: #add the relative coordinates to the cells coordinates
                self.rect.x += self.move[0]
                self.zoneVisionH.x += self.move[0]
                self.rect.y += self.move[1]
                self.zoneVisionH.y += self.move[1]
        elif self.hp <= 0 and self.isFood == False:
            self.hp = 0
            self.image.fill(red)
            self.color = red

#defining a new lifeform here the plants which can not move in our game
class Plants(lifeforms):
    def __init__(self, width, height, pos_x, pos_y):
        lifeforms.__init__(self, width, height, pos_x, pos_y)
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface([width, height]).convert_alpha()

        self.color = green
        self.image.fill(self.color)
        self.zoneVisionPlant = pygame.Rect(self.rect.x-50, self.rect.y-50, self.width+100, self.height+100)

    def update(self):
        if self.isDead() == False and self.energy > 0 :
            self.energy -= 0.05
        elif self.energy <= 0 :
            self.energy = 0
            if self.hp > 0:
                self.hp -= 0.05
        elif self.hp <= 0 :
            self.hp = 0
            self.image.fill(brown)
            self.color = brown

pred_group = pygame.sprite.Group()
herb_group = pygame.sprite.Group()
plant_group = pygame.sprite.Group()
child_group = pygame.sprite.Group()

#Declaring the number of initial objects
number_of_Predators = range(10)
number_of_Herbivores = range(25)
number_of_Plants = range(50)

objPr = list()
for i in number_of_Predators:
    objPr.append(Predator(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    pred_group.add(objPr[i])

objH = list()
for i in number_of_Herbivores:
    objH.append(Herbivore(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    herb_group.add(objH[i])

objP = list()
for i in number_of_Plants:
    objP.append(Plants(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    plant_group.add(objP[i])

#here we define the method where the predators hunt the herbivores
def hunt():
    global number_of_Herbivores_alive
    for i in range(0,len(objPr)):
         for j in range(0,len(objH)):
             if objH[j].get_hp() > 0:
                collide = pygame.Rect.colliderect(objPr[i].rect, objH[j].rect)
                if collide and objH[j].get_color != brown :
                    objH[j].set_hp(-5)
                    if objH[j].isDead() == True:
                        if objPr[i].get_energy() <= 5:
                            objPr[i].set_energy(5)

                        elif objPr[i].get_energy() == 6:
                            objPr[i].set_energy(4)
                            if objPr[i].get_hp() <= 9:
                                objPr[i].set_hp(1)

                        elif objPr[i].get_energy() == 7:
                            objPr[i].set_energy(3)
                            if objPr[i].get_hp() <= 8:
                                objPr[i].set_hp(2)
                            elif objPr[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

                        elif objPr[i].get_energy() == 8:
                            objPr[i].set_energy(2)
                            if objPr[i].get_hp() <= 7:
                                objPr[i].set_hp(3)
                            elif objPr[i].get_hp() == 8:
                                objPr[i].set_hp(2)
                            elif objH[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

                        elif objPr[i].get_energy() == 9:
                            objPr[i].set_energy(1)
                            if objPr[i].get_hp() <= 6:
                                objPr[i].set_hp(4)
                            elif objPr[i].get_hp() == 7:
                                objPr[i].set_hp(3)
                            elif objPr[i].get_hp() == 8:
                                objPr[i].set_hp(2)
                            elif objPr[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

                        elif objPr[i].get_energy() == 10:
                            objPr[i].set_energy(0)
                            if objPr[i].get_hp() <= 5:
                                objPr[i].set_hp(5)
                            elif objPr[i].get_hp() == 6:
                                objPr[i].set_hp(4)
                            elif objPr[i].get_hp() == 7:
                                objPr[i].set_hp(3)
                            elif objPr[i].get_hp() == 8:
                                objPr[i].set_hp(2)
                            elif objPr[i].get_hp() == 9:
                                objPr[i].set_hp(1)
                            else:
                                objPr[i].set_hp(0)

#here we define the method where the herbivores hunt the plant that are alive
def eatplant():
    global number_of_Plants_alive
    for i in range(0,len(objH)):
         for j in range(0,len(objP)):
             if objP[j].get_hp() > 0:
                collide = pygame.Rect.colliderect(objH[i].rect, objP[j].rect)
                if collide:
                    objP[j].set_hp(-10)
                    if objP[j].isDead() == True:
                        objP[j].set_X(WIDTH+50)
                    if objH[i].get_energy() <= 5:
                        objH[i].set_energy(5)
                        number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 6:
                        objH[i].set_energy(4)
                        if objH[i].get_hp() <= 9:
                            objH[i].set_hp(1)
                            number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 7:
                        objH[i].set_energy(3)
                        if objH[i].get_hp() <= 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 8:
                        objH[i].set_energy(2)
                        if objH[i].get_hp() <= 7:
                            objH[i].set_hp(3)
                        elif objH[i].get_hp() == 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 9:
                        objH[i].set_energy(1)
                        if objH[i].get_hp() <= 6:
                            objH[i].set_hp(4)
                        elif objH[i].get_hp() == 7:
                            objH[i].set_hp(3)
                        elif objH[i].get_hp() == 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

                    elif objH[i].get_energy() == 10:
                        objH[i].set_energy(0)
                        if objH[i].get_hp() <= 5:
                            objH[i].set_hp(5)
                        elif objH[i].get_hp() == 6:
                            objH[i].set_hp(4)
                        elif objH[i].get_hp() == 7:
                            objH[i].set_hp(3)
                        elif objH[i].get_hp() == 8:
                            objH[i].set_hp(2)
                        elif objH[i].get_hp() == 9:
                            objH[i].set_hp(1)
                        else:
                             objH[i].set_hp(0)
                             number_of_Plants_alive -= 1

#here we define the methods where the dead herbivores and predators are absorbed by the plants
def absorbHerb():
    global number_of_Herbivores_alive
    for i in range(0,len(objH)):
        for j in range(0,len(objP)):
            if objH[i].get_color() == brown:
                collide = pygame.Rect.colliderect(objH[i].rect, objP[j].zoneVisionPlant)
                if collide:
                    objH[i].set_X(WIDTH+50)
                    number_of_Herbivores_alive -= 1

def absorbPred():
    global number_of_Predators_alive
    for i in range(0,len(objPr)):
        for j in range(0,len(objP)):
            if objPr[i].get_color() == brown:
                collide = pygame.Rect.colliderect(objPr[i].rect, objP[j].zoneVisionPlant)
                if collide:
                    objPr[i].set_X(WIDTH+50)
                    number_of_Predators_alive -= 1

#here we create the objects aka our 3 lifeforms
def create_herb():
    global number_of_Herbivores_alive
    objH.append(Herbivore(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    herb_group.add(objH)
    number_of_Herbivores_alive += 1

def create_pred():
    global number_of_Predators_alive
    objPr.append(Predator(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    pred_group.add(objPr)
    number_of_Predators_alive += 1

def create_plant():
    global number_of_Plants_alive
    objP.append(Plants(20, 20, random.randrange(10, WIDTH-10), random.randrange(10, HEIGHT-10)))
    plant_group.add(objP)
    number_of_Plants_alive += 1

#Reproduction of a male and femal predator also reproduction of plants
def reproduction_Herbivores():
    for i in range(0,len(objH)):
        for j in range(0,len(objH)):
            collide = pygame.Rect.colliderect(objH[i].zoneVisionH, objH[j].zoneVisionH)
            if objH[j].get_energy() >= 8 and objH[i].get_energy() >= 8:
                if collide and objH[j].get_gender() == "male" and objH[i].get_gender() == "female" and objH[j].isDead() == False and objH[i].isDead() == False and objH[i].get_pregnant() == False:
                    objH[i].pregnant()
                if random.randrange(0,20) == 2 and objH[i].get_pregnant() == True and number_of_Herbivores_alive < 20:
                    create_herb()
                    objH[i].not_pregnant()

def reproduction_Predators():
    for i in range(0,len(objPr)):
        for j in range(0,len(objPr)):
            collide = pygame.Rect.colliderect(objPr[i].zoneVisionP, objPr[j].zoneVisionP)
            if objPr[j].get_energy() >= 8 and objPr[i].get_energy() >= 8:
                if collide and objPr[j].get_gender() == "male" and objPr[i].get_gender() == "female" and objPr[j].isDead() == False and objPr[i].isDead() == False and objPr[i].get_pregnant() == False:
                    objPr[i].pregnant()
                if random.randrange(0,20) == 2 and objPr[i].get_pregnant() == True and number_of_Predators_alive < 20:
                    create_pred()
                    objPr[i].not_pregnant()

def reproduction_Plants():
    if random.randrange(0,20) == 2:
        for i in range(0,len(objP)):
            if objP[i].get_energy() >= 5 and objP[i].get_hp() != 0 and random.randrange(0,20) == 2:
                    create_plant()
            else:
                return None

def zoneVisionPlant():
    for i in number_of_Plants:
        pygame.draw.rect(screen, (255, 255, 0), objP[i].zoneVisionPlant )

def become_orgw():
    for j in range(0,len(objH)):
        for i in range(0,len(objPr)):
            if objH[j].get_color() == red and random.randrange(0,100) == 2:
                objH[j].set_isFood(True)
                objH[j].set_waste()

def become_orgw2():
    for i in range(0,len(objPr)):
        if objPr[i].get_color() == red and random.randrange(0,100) == 2:
            objPr[i].set_isFood(True)
            objPr[i].set_waste()

def become_orgw3():
    for i in range(0,len(objP)):
        if objP[i].get_hp() == 0 and random.randrange(0,100) == 2:
            objP[i].set_X(WIDTH+50)

number_of_Herbivores_alive = 0
initial_Herbivores = len(objH)

number_of_Predators_alive = 0
initial_Predators = len(objPr)

number_of_Plants_alive = 0
initial_Plants = len(objP)

#Initializing the mainloop for our pygame
def mainloop():
    while True:
        pygame.time.Clock().tick(FPS)
        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()

        screen.fill(0)

        hunt()
        eatplant()
        absorbHerb()
        absorbPred()
        reproduction_Predators()
        reproduction_Herbivores()
        reproduction_Plants()
        pred_group.update()
        herb_group.update()
        plant_group.update()
        become_orgw()
        become_orgw2()
        become_orgw3()
        pred_group.draw(screen)
        herb_group.draw(screen)
        plant_group.draw(screen)
        pygame.display.flip()

mainloop()


import math
from random import Random, randrange

from shape import *

# ----------------------------------------------
class Triangle(Shape):
    # Function for reading information from file.
    def __init__(self):
        self.ax = 0
        self.ay = 0
        self.bx = 0
        self.by = 0
        self.cx = 0
        self.cy = 0
        self.a = 0
        self.b = 0
        self.c = 0

    # Function for reading information from file.

    def ReadStrArray(self, strArray, i):
        # должно быт как минимум три непрочитанных значения в массиве
        if i >= len(strArray) - 5:
            return 0
        self.ax = int(strArray[i])
        self.ay = int(strArray[i + 1])
        self.bx = int(strArray[i + 2])
        self.by = int(strArray[i + 3])
        self.cx = int(strArray[i + 4])
        self.cy = int(strArray[i + 5])
        self.SidesCalculating()
        i += 6
        return i

    # Function for random generating parameters.

    def RandomGenerating(self):
        self.ax = randrange(1000)
        self.ay = randrange(1000)
        self.bx = randrange(1000)
        self.by = randrange(1000)
        self.cx = randrange(1000)
        self.cy = randrange(1000)
        self.SidesCalculating()

    def SidesCalculating(self):
        a = math.sqrt(pow(self.bx - self.ax, 2) + pow(self.by - self.ay, 2))
        b = math.sqrt(pow(self.cx - self.bx, 2) + pow(self.cy - self.by, 2))
        c = math.sqrt(pow(self.cx - self.ax, 2) + pow(self.cy - self.ay, 2))
        self.a = a
        self.b = b
        self.c = c

    # Function for printing information about the shape.

    def Print(self):
        if self.a < self.b + self.c and self.b < self.a + self.c and self.c < self.a + self.b:
            print("Triangle: a = ", self.a, " b = ", self.b, "c = ", self.c, ", Square = ", self.Square(), ", Color = ", self.color)
        else:
            print("Something wrong with Triangle coordinates! Square = 0")
        pass

    # Function for writing information to the file.

    def Write(self, ostream):
        if self.a < self.b + self.c and self.b < self.a + self.c and self.c < self.a + self.b:
            ostream.write("Triangle: a = {}  b = {}  c = {}, Square = {}, Color = {}".format(self.a, self.b, self.c, self.Square(), self.color))
        else:
            ostream.write("Something wrong with Triangle coordinates! Square = 0")
        pass

    # Function for counting the square.

    def Square(self):
        if self.a < self.b + self.c and self.b < self.a + self.c and self.c < self.a + self.b:
            return 0.5 * abs(
                self.ax * (self.by - self.cy) + self.bx * (self.cy - self.ay) + self.cx * (self.ay - self.by))
        else:
            return 0.0
        pass


import intcode, itertools
from numpy import loadtxt

inputProgram =  loadtxt("input.txt", delimiter=",", dtype=int)
nAmps = 5
# phaseSequences = map(lambda x:  list(x), list(itertools.permutations(range(nAmps), nAmps)))
#
# program, out = intcode.executeIntcode(inputProgram.copy(), [0,0])
#
#
# def executePhaseSequence(inputVector):
#     out = 0;
#     for _ in range(nAmps):
#         program, out = intcode.executeIntcode(inputProgram.copy(), [inputVector.pop(0),out])
#     return out
#
# outmax = max(map(executePhaseSequence, phaseSequences))
# print "OUTMAX %s" %outmax

phaseSequences = map(lambda x:  list(x), list(itertools.permutations(range(5,10), nAmps)))

def executePhaseSequenceFeedback(inputVector):
    print ("Executing Phase Sequence %s" %inputVector)
    amps = [intcode.IntcodeComputer(inputProgram.copy(), inputBuffer=[], outputBuffer=[], haltOnOutput=True) for _ in
            range(5)]
    i = 0
    lastOutput = 0;
    while not amps[0].halted:
        print ("Executing Amp %s" %(i%5))
        if inputVector:
            amps[i%5].inputBuffer.append(inputVector.pop(0))
        amps[i % 5].inputBuffer.append(lastOutput)
        amps[i % 5].execute()
        if not amps[i%5].halted:
            lastOutput = amps[i%5].outputBuffer.pop()
            i += 1
    print ("Value %s" %lastOutput)
    return lastOutput

outmax = max(map(executePhaseSequenceFeedback, phaseSequences))
print("OUTMAX %s" %outmax)


import numpy as np
import random
import pygame

SCREEN_SIZE = 500
TILE_SIZE = SCREEN_SIZE // 5
GAP = SCREEN_SIZE // 25
SCORE_HEIGHT = 100

BACKGROUND = (187, 173, 160)
COLOURS = {
    0: (205, 193, 180),
    2: (238, 228, 218),
    4: (242, 224, 200),
    8: (242, 177, 121),
    16: (245, 149, 99),
    32: (246, 124, 95),
    64: (246, 94, 59),
    128: (237, 207, 114),
    256: (237, 204, 97),
    512: (237, 200, 80),
    1024: (237, 197, 63),
    2048: (238, 194, 46)
}

pygame.init()

win = pygame.display.set_mode((SCREEN_SIZE, SCREEN_SIZE + SCORE_HEIGHT))
pygame.display.set_caption('2048')
clock = pygame.time.Clock()

font = pygame.font.Font('sourcesanspro.ttf', 60)

class Block:
    def __init__(self, value):
        self.value = value
        self.colour = COLOURS[value]

    def draw(self, x, y):
        pygame.draw.rect(win, self.colour, (x, y, TILE_SIZE, TILE_SIZE))
        if self.value != 0:
            text = font.render(str(self.value), 1, (120, 120, 120))
            win.blit(text, text.get_rect(center=(x + TILE_SIZE // 2, y + TILE_SIZE // 2)))

    def __add__(self, other):
        if self.value == other.value:
            return Block(self.value * 2)

    def __eq__(self, other):
        if self.value == other.value:
            return True
        return False

class Grid:
    def __init__(self):
        self.blocks = list([Block(0) for _ in range(4)] for _ in range(4))
        self.new_turn()
        self.score = 0

    def __gt__(self, other):
        if self.score > other.score:
            return True
        return False

    def new_turn(self):
        for _ in range(2):
            new_spawn(self.blocks)

    def draw(self):
        for i in range(4):
            for j in range(4):
                x = GAP + i * (TILE_SIZE + GAP)
                y = SCORE_HEIGHT + GAP + j * (TILE_SIZE + GAP)
                self.blocks[j][i].draw(x, y)

        text = font.render(str(self.score), 1, (50, 50, 50))

        win.blit(text, text.get_rect(center=(SCREEN_SIZE // 2, SCORE_HEIGHT // 2)))

    def shift(self):
        for row in self.blocks:
            for index, block in enumerate(row):
                if block.value == 0:
                    row.pop(index)
                    row.insert(0, Block(0))

        self.blocks = np.flip(self.blocks, 1).tolist()
        self.merge()
        self.blocks = np.flip(self.blocks, 1).tolist()

    def check_end(self):
        for row in self.blocks:
            for block in row:
                if block.value == 0:
                    return False

        arr = convert(self.blocks)

        for direction in range(4):
            arr = np.rot90(arr, direction).tolist()
            for row in arr:
                for index in range(len(row) - 1):
                    if row[index + 1] == row[index] and row[index] != 0:
                        row[index] = row[index] + row[index + 1]
                        row.pop(index + 1)
                        row.append(0)

            arr = np.rot90(arr, 4 - direction).tolist()

            if arr != convert(self.blocks):
                return False

        return True

    def merge(self):
        for row in self.blocks:
            for index in range(len(row) - 1):
                if row[index + 1] == row[index] and row[index].value != 0:
                    row[index] = row[index] + row[index + 1]
                    row.pop(index + 1)
                    row.append(Block(0))
                    self.score += row[index].value

    def move(self, direction):
        original = convert(self.blocks)

        if direction == 'right':
            self.shift()

        elif direction == 'left':
            self.blocks = np.flip(self.blocks, 1).tolist()

            self.shift()

            self.blocks = np.flip(self.blocks, 1).tolist()

        elif direction == 'down':
            self.blocks = np.rot90(self.blocks, 1).tolist()

            self.shift()

            self.blocks = np.rot90(self.blocks, 3).tolist()

        elif direction == 'up':
            self.blocks = np.rot90(self.blocks, 3).tolist()

            self.shift()

            self.blocks = np.rot90(self.blocks, 1).tolist()

        if original != convert(self.blocks):
            self.blocks = new_spawn(self.blocks)

def convert(arr):
    output = []
    for row in arr:
        output.append(list((map(lambda x: x.value, row))))
    return output

def new_spawn(arr):
    choices = np.argwhere(np.array(arr) == Block(0)).tolist()
    x, y = tuple(random.choice(choices))

    block = Block(2)

    if random.random() > 0.9:
        block = Block(4)

    arr[x][y] = block

    return arr

def draw(grid):
    win.fill(BACKGROUND)
    grid.draw()

def end(score):
    s = pygame.Surface((1000, 750), pygame.SRCALPHA)
    s.fill((255, 255, 255, 200))
    win.blit(s, (0, 0))

    text = font.render('Congratulations!', 1, (0, 0, 0))
    text2 = font.render('Your Score: {}'.format(score), 1, (0, 0, 0))

    win.blit(text, text.get_rect(center=(SCREEN_SIZE // 2, (SCREEN_SIZE + SCORE_HEIGHT) // 2 - 50)))
    win.blit(text2, text2.get_rect(center=(SCREEN_SIZE // 2, (SCREEN_SIZE + SCORE_HEIGHT) // 2 + 50)))

    pygame.display.update()

    play = True
    while play:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
                pygame.quit()

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    play = False
                    main()

def main():
    grid = Grid()

    play = True
    while play:
        clock.tick(30)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                play = False
                pygame.quit()

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    grid.move('left')
                elif event.key == pygame.K_RIGHT:
                    grid.move('right')
                elif event.key == pygame.K_UP:
                    grid.move('up')
                elif event.key == pygame.K_DOWN:
                    grid.move('down')

        if play:
            draw(grid)
            pygame.display.update()

            if grid.check_end():
                end(grid.score)

main()


n = int(input())

while n != -1:
    total = 0
    old_t = 0

    for i in range(n):
        line = input()
        (s, t) = line.split()
        s = int(s)
        t = int(t)
        total += s * (t-old_t)
        old_t = t

    print(total, "miles")

    n = int(input())


import os
import glob
import tempfile
from pathlib import Path

import logging
import argparse

import numpy as np
import numpy.testing as npt

import dask
from dask.distributed import Client

import xarray as xr

from src import utils

dask.config.set(**{"array.slicing.split_large_chunks": False})
xr.set_options(keep_attrs=True)

PROJECT_DIR = Path(__file__).resolve().parents[1]
DATA_DIR = PROJECT_DIR / "data/raw"

# Dataset-specific opening code
# ===============================================

class _open:
    """
    Class containing the dataset-specific code for opening each available dataset
    """

    @staticmethod
    def JRA55(variables, realm, preprocess):
        """Open JRA55 variables from specified realm"""
        file = DATA_DIR / f"JRA55/{realm}.zarr.zip"
        ds = xr.open_dataset(
            file,
            engine="zarr",
            chunks={},
            use_cftime=True,
        )[variables]
        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def HadISST(variables, realm, preprocess):
        """Open HadISST variables from specified realm"""
        file = DATA_DIR / "HadISST/HadISST_sst.nc"
        ds = xr.open_dataset(
            file,
            chunks={},
            use_cftime=True,
        )[variables]
        ds = ds.where(ds > -1000)
        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def EN422(variables, _, preprocess):
        """Open EN.4.2.2 variables"""
        files = sorted(glob.glob(f"{DATA_DIR}/EN.4.2.2/*.nc"))
        ds = xr.open_mfdataset(
            files,
            preprocess=preprocess,
            parallel=True,
            use_cftime=True,
        )[variables]
        return ds

    @staticmethod
    def GPCP(variables, _, preprocess):
        """Open GPCP v2.3 variables"""
        files = sorted(glob.glob(f"{DATA_DIR}/GPCP/????/*.nc"))
        ds = xr.open_mfdataset(
            files,
            preprocess=preprocess,
            parallel=True,
            use_cftime=True,
        )[variables]
        return ds

    @staticmethod
    def AGCD(variables, _, preprocess):
        """Open AGCD variables"""
        result = []
        chunks = {"time": 8000, "lon": 40, "lat": 40}  # Based on chunking on disk
        max_time = "2021-12-31"  # Recent times can be flaky
        for variable in variables:
            hist_file = f"{DATA_DIR}/AGCD/HISTORICAL/climate/{variable}.nc"
            recent_file = f"{DATA_DIR}/AGCD/climate/{variable}.nc"
            ds_hist = xr.open_dataset(hist_file, use_cftime=True, chunks=chunks)[
                [variable]
            ]
            ds_recent = xr.open_dataset(recent_file, use_cftime=True, chunks=chunks)[
                [variable]
            ]
            ds_hist = ds_hist.sel(time=slice(None, ds_recent.isel(time=0).time.values))
            ds_recent = ds_recent.sel(
                time=slice(ds_recent.isel(time=1).time.values, max_time)
            )
            result.append(xr.concat((ds_hist, ds_recent), dim="time"))
        ds = xr.merge(result).chunk(chunks)
        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def CAFEf6(variables, realm, preprocess):
        """Open CAFE-f6 variables from specified realm applying preprocess prior to
        concanenating forecasts
        """
        files = sorted(
            glob.glob(f"{DATA_DIR}/CAFEf6/c5-d60-pX-f6-????1101/{realm}.zarr.zip")
        )
        return xr.open_mfdataset(
            files,
            compat="override",
            preprocess=preprocess,
            engine="zarr",
            coords="minimal",
            parallel=True,
        )[variables]

    @staticmethod
    def CAFEf5(variables, realm, preprocess):
        """Open CAFE-f5 variables from specified realm, including appending first
        10 members of CAFE-f6 for 2020 forecast
        """
        file = DATA_DIR / f"CAFEf5/NOV/{realm}.zarr.zip"
        ds = xr.open_dataset(file, engine="zarr", chunks={})[variables]
        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def CAFE60v1(variables, realm, preprocess):
        """Open CAFE60v1 variables from specified realm"""
        file = DATA_DIR / f"CAFE60v1/{realm}.zarr.zip"
        ds = xr.open_dataset(file, engine="zarr", chunks={})[variables]
        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def CAFE_hist(variables, realm, preprocess):
        """Open CAFE historical run variables from specified realm"""
        hist_file = DATA_DIR / f"CAFE_hist/{realm}.zarr.zip"
        hist = xr.open_dataset(hist_file, engine="zarr", chunks={})[variables]

        ctrl_file = DATA_DIR / f"CAFE_ctrl/{realm}.zarr.zip"
        ctrl = xr.open_dataset(ctrl_file, engine="zarr", chunks={})[variables]

        hist = utils.truncate_latitudes(hist)
        ctrl = utils.truncate_latitudes(ctrl)

        drift = (
            ctrl.mean("ensemble")
            .groupby("time.month")
            .map(lambda x: x - x.mean(["time"]))
        )
        ds = hist - drift
        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def _cmip6_dcppA_hindcast(
        model, variant_id, grid, variables, realm, years, members, version
    ):
        """Open CMIP6 dcppA-hindcast variables from specified monthly realm"""

        def _dcpp_file(y, m, v):
            path = f"{DATA_DIR}/{model}/s{y}-r{m}{variant_id}/{realm}/{v}/{grid}"
            if version == "latest":
                versions = sorted(glob.glob(f"{path}/v????????"))
                if len(versions) == 0:
                    raise ValueError(f"No versions found for {path}")
                else:
                    path = versions[-1]
            else:
                path = f"{path}/{version}"

            file_pattern = (
                f"{v}_{realm}_{model}_dcppA-hindcast_s{y}-r{m}{variant_id}_{grid}_*.nc"
            )
            files = sorted(glob.glob(f"{path}/{file_pattern}"))
            if len(files) == 0:
                raise ValueError(f"No files found for {path}/{file_pattern}")
            else:
                return files

        @dask.delayed
        def _open_dcpp_delayed(y, m, v):
            files = _dcpp_file(y, m, v)
            return xr.concat(
                [xr.open_dataset(f, chunks={}, use_cftime=True) for f in files],
                dim="time",
            )[v]

        def _open_dcpp(y, m, v, d0):
            var_data = _open_dcpp_delayed(y, m, v).data
            return dask.array.from_delayed(var_data, d0.shape, d0.dtype)

        ds = []
        for v in variables:
            f0 = _dcpp_file(years[0], members[0], v)
            ds0 = xr.concat(
                [xr.open_dataset(f, chunks={}, use_cftime=True) for f in f0], dim="time"
            )
            ds0 = utils.convert_time_to_lead(ds0, time_freq="months")
            ds0 = utils.round_to_start_of_month(ds0, dim="init")
            d0 = ds0[v]

            delayed = []
            for y in years:
                delayed.append(
                    dask.array.stack([_open_dcpp(y, m, v, d0) for m in members], axis=0)
                )
            delayed = dask.array.stack(delayed, axis=0)

            init = xr.cftime_range(
                ds0.init.dt.strftime(
                    "%Y-%m-%d"
                ).item(),  # Already rounded to start of month
                periods=len(years),
                freq="12MS",
                calendar="julian",
            )
            time = [
                xr.cftime_range(
                    i, periods=ds0.sizes["lead"], freq="MS", calendar="julian"
                )
                for i in init
            ]
            ds.append(
                xr.DataArray(
                    delayed,
                    dims=["init", "member", *d0.dims],
                    coords={
                        "member": members,
                        "init": init,
                        **d0.coords,
                        "time": (["init", "lead"], time),
                    },
                    attrs=d0.attrs,
                ).to_dataset(name=v)
            )
        return xr.merge(ds).compute()

    @staticmethod
    def CanESM5(variables, realm, preprocess):
        """Open CanESM5 dcppA-hindcast variables from specified monthly realm"""
        model = "CanESM5"
        variant_id = "i1p2f1"
        grid = "gn"
        years = range(1960, 2019 + 1)
        members = range(1, 40 + 1)
        version = "v20190429"
        ds = _open._cmip6_dcppA_hindcast(
            model, variant_id, grid, variables, realm, years, members, version
        )
        # Add cell area
        if realm == "Omon":
            file = (
                f"{DATA_DIR}/{model}_hist/r1{variant_id}/Ofx/areacello/{grid}/{version}/"
                f"areacello_Ofx_{model}_historical_r1{variant_id}_{grid}.nc"
            )
        elif realm == "Amon":
            file = (
                f"{DATA_DIR}/{model}_hist/r1{variant_id}/fx/areacella/{grid}/{version}/"
                f"areacella_fx_{model}_historical_r1{variant_id}_{grid}.nc"
            )
        else:
            raise ValueError(f"I don't know where to find the area for realm: {realm}")
        area = xr.open_dataset(file, chunks={})
        ds = ds.assign_coords(area)

        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def EC_Earth3(variables, realm, preprocess):
        """Open EC-Earth3 dcppA-hindcast variables from specified monthly realm"""
        model = "EC-Earth3"
        variant_id = "i1p1f1"
        grid = "gn" if realm == "Omon" else "gr"
        years = range(1960, 2018 + 1)
        members = range(1, 10 + 1)
        version = "v2020121?"
        ds = _open._cmip6_dcppA_hindcast(
            model, variant_id, grid, variables, realm, years, members, version
        )
        # Add cell area
        if realm == "Omon":
            file = (
                f"{DATA_DIR}/{model}_hist/r1{variant_id}/Ofx/areacello/{grid}/v20200918/"
                f"areacello_Ofx_{model}_historical_r1{variant_id}_{grid}.nc"
            )
        elif realm == "Amon":
            file = (
                f"{DATA_DIR}/{model}_hist/r1{variant_id}/fx/areacella/{grid}/v20210324/"
                f"areacella_fx_{model}_historical_r1{variant_id}_{grid}.nc"
            )
        else:
            raise ValueError(f"I don't know where to find the area for realm: {realm}")
        area = xr.open_dataset(file, chunks={})

        # Lat and lon values are not exactly the same to numerical precision for ds and area
        for c in area.coords:
            if c in ds.coords:
                if np.array_equiv(ds[c].values, area[c].values):
                    pass
                else:
                    npt.assert_allclose(ds[c].values, area[c].values, rtol=1e-06)
                    area = area.assign_coords({c: ds[c]})

        ds = ds.assign_coords(area)

        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def HadGEM3(variables, realm, preprocess):
        """Open HadGEM3-GC31-MM dcppA-hindcast variables from specified monthly realm"""
        model = "HadGEM3-GC31-MM"
        variant_id = "i1p1f2"
        grid = "gn"
        years = range(1960, 2018 + 1)
        members = range(1, 10 + 1)
        version = "v20200???"
        ds = _open._cmip6_dcppA_hindcast(
            model, variant_id, grid, variables, realm, years, members, version
        )
        # Add cell area
        if realm == "Omon":
            file = (
                f"{DATA_DIR}/{model}_ctrl/r1i1p1f1/Ofx/areacello/{grid}/v20200108/"
                f"areacello_Ofx_{model}_piControl_r1i1p1f1_{grid}.nc"
            )
        elif realm == "Amon":
            file = (
                f"{DATA_DIR}/{model}_ctrl/r1i1p1f1/fx/areacella/{grid}/v20200108/"
                f"areacella_fx_{model}_piControl_r1i1p1f1_{grid}.nc"
            )
        else:
            raise ValueError(f"I don't know where to find the area for realm: {realm}")
        area = xr.open_dataset(file, chunks={})
        ds = ds.assign_coords(area)

        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    #     def CESM1(variables, realm, preprocess):
    #         """
    #         NOT CURRENTLY USED
    #         Open CESM1-1-CAM5-CMIP5 dcppA-hindcast variables from specified monthly realm
    #         """
    #         model = "CESM1-1-CAM5-CMIP5"
    #         variant_id = "i1p1f1"
    #         grid = "gr" if realm == "Omon" else "gn"
    #         years = range(1981, 2017 + 1)  # CESM1 ocean files end in 2017
    #         members = range(1, 40 + 1)
    #         version = "v20191016" if realm == "Omon" else "v20191007"
    #         ds = _open._cmip6_dcppA_hindcast(
    #             model,
    #             variant_id,
    #             grid,
    #             variables,
    #             realm,
    #             years,
    #             members,
    #             version)
    #         if preprocess is not None:
    #             return preprocess(ds)
    #         else:
    #             return ds

    @staticmethod
    def _cmip6(model, experiment, variant_id, grid, variables, realm, members, version):
        """
        Open CMIP6 variables from specified realm

        Can specify version='latest' but this is slower as it has to search each
        directory for the latest version
        """

        def _cmip_files(m, v):
            # Would be much better just to name the datasets by the experiment, but too much
            # effort to change now
            if experiment == "historical":
                dataset = f"{model}_hist"
            elif experiment == "piControl":
                dataset = f"{model}_ctrl"
            else:
                raise ValueError("I'm not sure I can open this experiment type")

            path = f"{DATA_DIR}/{dataset}/r{m}{variant_id}/{realm}/{v}/{grid}"
            if version == "latest":
                versions = sorted(glob.glob(f"{path}/v????????"))
                if len(versions) == 0:
                    raise ValueError(f"No versions found for {path}")
                else:
                    path = versions[-1]
            else:
                path = f"{path}/{version}"
            file_pattern = (
                f"{v}_{realm}_{model}_{experiment}_r{m}{variant_id}_{grid}_*.nc"
            )
            files = sorted(glob.glob(f"{path}/{file_pattern}"))
            if len(files) == 0:
                raise ValueError(f"No files found for {path}/{file_pattern}")
            else:
                return files

        @dask.delayed
        def _open_cmip_delayed(m, v):
            files = _cmip_files(m, v)
            return xr.concat(
                [xr.open_dataset(f, chunks={}, use_cftime=True) for f in files],
                dim="time",
            )[v]

        def _open_cmip(m, v):
            var_data = _open_cmip_delayed(m, v).data
            return dask.array.from_delayed(var_data, d0.shape, d0.dtype)

        ds = []
        for v in variables:
            f0 = _cmip_files(members[0], v)
            d0 = xr.concat(
                [xr.open_dataset(f, chunks={}, use_cftime=True) for f in f0], dim="time"
            )[v]

            delayed = dask.array.stack([_open_cmip(m, v) for m in members], axis=0)

            ds.append(
                xr.DataArray(
                    delayed,
                    dims=["member", *d0.dims],
                    coords={
                        "member": members,
                        **d0.coords,
                    },
                    attrs=d0.attrs,
                ).to_dataset(name=v)
            )

        return xr.merge(ds).compute()

    @staticmethod
    def CanESM5_hist(variables, realm, preprocess):
        """Open CanESM5 historical variables from specified realm"""
        ds = _open._cmip6(
            "CanESM5",
            "historical",
            "i1p2f1",
            "gn",
            variables,
            realm,
            range(1, 40 + 1),
            "v20190429",
        )
        # Add cell area
        if realm == "Omon":
            file = (
                f"{DATA_DIR}/CanESM5_hist/r1i1p2f1/Ofx/areacello/gn/v20190429/"
                f"areacello_Ofx_CanESM5_historical_r1i1p2f1_gn.nc"
            )
        elif realm == "Amon":
            file = (
                f"{DATA_DIR}/CanESM5_hist/r1i1p2f1/fx/areacella/gn/v20190429/"
                f"areacella_fx_CanESM5_historical_r1i1p2f1_gn.nc"
            )
        else:
            raise ValueError(f"I don't know where to find the area for realm: {realm}")
        area = xr.open_dataset(file, chunks={})
        ds = ds.assign_coords(area)

        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def CanESM5_ctrl(variables, realm, preprocess):
        """Open CanESM5 piControl variables from specified realm"""
        ds = _open._cmip6(
            "CanESM5", "piControl", "i1p2f1", "gn", variables, realm, [1], "v20190429"
        ).sel(member=1, drop=True)
        # Add cell area
        if realm == "Omon":
            file = (
                f"{DATA_DIR}/CanESM5_hist/r1i1p2f1/Ofx/areacello/gn/v20190429/"
                f"areacello_Ofx_CanESM5_historical_r1i1p2f1_gn.nc"
            )
        elif realm == "Amon":
            file = (
                f"{DATA_DIR}/CanESM5_hist/r1i1p2f1/fx/areacella/gn/v20190429/"
                f"areacella_fx_CanESM5_historical_r1i1p2f1_gn.nc"
            )
        else:
            raise ValueError(f"I don't know where to find the area for realm: {realm}")
        area = xr.open_dataset(file, chunks={})
        ds = ds.assign_coords(area)

        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def EC_Earth3_hist(variables, realm, preprocess):
        """Open EC-Earth3 historical variables from specified realm"""
        grid = "gn" if realm == "Omon" else "gr"
        # Member 3 has screwy lats that can't be readily concatenated
        # Members 11, 13, 15 start in 1849
        # Member 19 has very few variables replicated for Omon
        # Members 101-150 only span 197001-201412
        members = [1, 2, 4, 6, 9, 10, 12, 14, 16, 17]  # ,18] + list(range(20, 26))
        ds = _open._cmip6(
            "EC-Earth3",
            "historical",
            "i1p1f1",
            grid,
            variables,
            realm,
            members,
            "v20200???",
        )
        # Add cell area
        if realm == "Omon":
            file = (
                f"{DATA_DIR}/EC-Earth3_hist/r1i1p1f1/Ofx/areacello/{grid}/v20200918/"
                f"areacello_Ofx_EC-Earth3_historical_r1i1p1f1_{grid}.nc"
            )
        elif realm == "Amon":
            file = (
                f"{DATA_DIR}/EC-Earth3_hist/r1i1p1f1/fx/areacella/{grid}/v20210324/"
                f"areacella_fx_EC-Earth3_historical_r1i1p1f1_{grid}.nc"
            )
        else:
            raise ValueError(f"I don't know where to find the area for realm: {realm}")
        area = xr.open_dataset(file, chunks={})

        # Lat and lon values are not exactly the same to numerical precision for ds and area
        for c in area.coords:
            if c in ds.coords:
                if np.array_equiv(ds[c].values, area[c].values):
                    pass
                else:
                    npt.assert_allclose(ds[c].values, area[c].values, rtol=1e-06)
                    area = area.assign_coords({c: ds[c]})

        ds = ds.assign_coords(area)

        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

    @staticmethod
    def EC_Earth3_ctrl(variables, realm, preprocess):
        """Open EC-Earth3 piControl variables from specified realm"""
        grid = "gn" if realm == "Omon" else "gr"
        ds = _open._cmip6(
            "EC-Earth3", "piControl", "i1p1f1", grid, variables, realm, [1], "v20200???"
        ).sel(member=1, drop=True)
        # Add cell area
        if realm == "Omon":
            file = (
                f"{DATA_DIR}/EC-Earth3_hist/r1i1p1f1/Ofx/areacello/{grid}/v20200918/"
                f"areacello_Ofx_EC-Earth3_historical_r1i1p1f1_{grid}.nc"
            )
        elif realm == "Amon":
            file = (
                f"{DATA_DIR}/EC-Earth3_hist/r1i1p1f1/fx/areacella/{grid}/v20210324/"
                f"areacella_fx_EC-Earth3_historical_r1i1p1f1_{grid}.nc"
            )
        else:
            raise ValueError(f"I don't know where to find the area for realm: {realm}")
        area = xr.open_dataset(file, chunks={})

        # Lat and lon values are not exactly the same to numerical precision for ds and area
        for c in area.coords:
            if c in ds.coords:
                if np.array_equiv(ds[c].values, area[c].values):
                    pass
                else:
                    npt.assert_allclose(ds[c].values, area[c].values, rtol=1e-06)
                    area = area.assign_coords({c: ds[c]})

        ds = ds.assign_coords(area)

        if preprocess is not None:
            return preprocess(ds)
        else:
            return ds

# Preparation
# ===============================================

def maybe_generate_CAFE_grid_files():
    """Generate files containing CAFE grids"""
    path = DATA_DIR / "CAFE_hist/"

    atmos_file = DATA_DIR / "gridinfo/CAFE_atmos_grid.nc"
    if not os.path.exists(atmos_file):
        atmos = (
            xr.open_zarr(f"{path}/atmos_hybrid_month.zarr.zip")
            .isel(time=0, ensemble=0)
            .drop(
                [
                    "time",
                    "ensemble",
                    "average_DT",
                    "average_T1",
                    "average_T2",
                ]
            )
        )
        atmos = utils.truncate_latitudes(atmos)
        atmos_grid = xr.zeros_like(
            atmos[["t_ref", "latb", "lonb"]].rename({"t_ref": "CAFE_atmos_grid"})
        )
        atmos_grid.attrs = {}
        atmos_grid.attrs = {}
        atmos_grid.to_netcdf(atmos_file, mode="w")

    ocean_file = DATA_DIR / "gridinfo/CAFE_ocean_grid.nc"
    if not os.path.exists(ocean_file):
        ocean = (
            xr.open_zarr(f"{path}/ocean_month.zarr.zip")
            .isel(time=0, ensemble=0)
            .drop(
                [
                    "time",
                    "ensemble",
                    "average_DT",
                    "average_T1",
                    "average_T2",
                ]
            )
        )
        ocean_ut_grid = xr.zeros_like(
            ocean[["u", "area_t", "geolat_t", "geolon_t", "st_edges_ocean"]].rename(
                {"u": "CAFE_ocean_tu_grid"}
            )
        )
        ocean_tu_grid = xr.zeros_like(
            ocean[["wt", "area_t", "geolat_t", "geolon_t", "sw_edges_ocean"]].rename(
                {"wt": "CAFE_ocean_ut_grid"}
            )
        )
        ocean_grid = xr.merge([ocean_ut_grid, ocean_tu_grid])
        ocean_grid.attrs = {}
        ocean_grid.to_netcdf(ocean_file, mode="w")

# def maybe_generate_HadISST_grid_file():
#     """Generate file containing HadISST grid"""
#     file = DATA_DIR / "gridinfo/HadISST_grid.nc"
#     if not os.path.exists(file):
#         path = DATA_DIR / "HadISST/ocean_month.zarr"
#         had = xr.open_zarr(path)[["sst"]].isel(time=0).drop("time")
#         grid = xr.zeros_like(
#             had.rename({"sst": "HadISST_grid", "latitude": "lat", "longitude": "lon"})
#         )
#         grid.attrs = {}
#         grid.to_netcdf(file, mode="w")

# Command line interface
# ===============================================

def prepare_dataset(config, save_dir, save=True):
    """
    Prepare a dataset according to a provided config file

    Parameters
    ----------
    config : str
        The name of the config file
    save_dir : str
        The directory to save to
    save : boolean, optional
        If True (default), save the prepared dataset(s) in zarr format to save_dir. If
        False, return an xarray Dataset containing the prepared data. The latter is
        useful for debugging
    """
    logger = logging.getLogger(__name__)

    cfg = utils.load_config(config)

    # List of datasets that have open methods impletemented
    methods = [
        method_name
        for method_name in dir(_open)
        if callable(getattr(_open, method_name))
    ]
    methods = [m for m in methods if "__" not in m]

    if "name" not in cfg:
        raise ValueError(
            "Please provide an entry for 'name' in the config file so that I know how "
            f"to open the data. Available options are {methods}"
        )

    if "prepare" in cfg:
        prepared = []

        for identifier, params in cfg["prepare"].items():
            input_variables = params["uses"]
            if isinstance(input_variables, list):
                input_variables = {None: input_variables}

            # Build composite preprocess function
            if "preprocess" in params:
                preprocess = utils.composite_function(params["preprocess"])
            else:
                preprocess = None

            logger.info(f"Processing {identifier} from {cfg['name']}")
            ds = []
            for realm, var in input_variables.items():
                if realm == "prepared":
                    ds.append(
                        xr.merge(
                            xr.open_zarr(
                                f"{save_dir}/{cfg['name']}.{v}.zarr",
                                decode_timedelta=False,
                                use_cftime=True,
                            )
                            for v in var
                        )
                    )
                else:
                    if hasattr(_open, cfg["name"]):
                        ds.append(getattr(_open, cfg["name"])(var, realm, preprocess))
                    else:
                        raise ValueError(
                            f"There is no method available to open '{cfg['name']}'. Please "
                            "ensure that the 'name' entry in the config file matches an "
                            "existing method in src.data._open, or add a new method for this "
                            f"data. Available methods are {methods}"
                        )
            ds = xr.merge(ds)

            if "apply" in params:
                ds = utils.composite_function(params["apply"])(ds)

            prepared.append(ds)
            if save:
                ds = ds.unify_chunks()
                for var in ds.variables:
                    ds[var].encoding = {}
                ds.to_zarr(f"{save_dir}/{cfg['name']}.{identifier}.zarr", mode="w")

        logger.info("Succeeded processing all variables")
        return prepared

    else:
        raise ValueError("No variables were specified to prepare")

def main(config, config_dir, save_dir):
    """
    Spin up a dask cluster and process and save raw data according to a provided config file

    Parameters
    ----------
    config : str
        The name of the config file
    config_dir : str
        The directory containing the config file
    save_dir : str
        The directory to save to
    """
    logger = logging.getLogger(__name__)

    logger.info("Spinning up a dask cluster")
    local_directory = tempfile.TemporaryDirectory()
    with Client(processes=False, local_directory=local_directory.name) as client:
        logger.info("Generating grid files")
        maybe_generate_CAFE_grid_files()

        logger.info(f"Preparing raw data using {config}")
        prepare_dataset(f"{config_dir}/{config}", save_dir)

if __name__ == "__main__":
    log_fmt = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    logging.basicConfig(level=logging.INFO, format=log_fmt)

    parser = argparse.ArgumentParser(
        description="Process a raw dataset according to a provided config file"
    )
    parser.add_argument(
        "config",
        type=str,
        help="Configuration file to process",
    )
    parser.add_argument(
        "--config_dir",
        type=str,
        default=f"{PROJECT_DIR}/config/prepare_data",
        help="Location of directory containing config file(s) to use, defaults to <project_dir>/config/prepare_data",
    )
    parser.add_argument(
        "--save_dir",
        type=str,
        default=f"{PROJECT_DIR}/data/processed/",
        help="Location of directory to save processed data to, defaults to <project_dir>/data/processed/",
    )

    args = parser.parse_args()
    config = args.config
    config_dir = args.config_dir
    save_dir = args.save_dir

    main(config, config_dir, save_dir)


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

from srcs.ft_calculate import ft_calculate
from srcs.ft_solve import ft_solve

import re
from sys import exit

class Calc(object):

    def __init__(self):
        self.sum_list = []
        self.variable = "None"
        self.filters = [r"([*/][*/])",           # chained //
                        r"([*/][+\-]{2})",       # * or / before - or +
                        r"([+-][*/])",           # - or + before * or /
                        r"([a-zA-Z][a-zA-Z])",   # letter followed by letter
                        r"([a-zA-Z][0-9])",      # letter followed by digit
                        r"[+\-*/\^]$",           # operator followed by power
                        r"^[*/\^]",              # division followed by power
                        r"[a-zA-Z][\^]\d+\.\d+", # floating point var power
                        r"[a-zA-Z][\^][-]"       # negatives power
                        ]

    def degree(self, reduced):
        if self.variable != "None":
            if re.search(r"([a-zA-Z][\^][2-9])", reduced):
                for idx, char in enumerate(reduced):
                    if char == "^":
                        degree_val = int(reduced[idx + 1])
                        break
            elif "^1" in reduced or \
                 (self.variable in reduced and not("^" in reduced)):
                degree_val = 1
            else:
                return ("None")
        else:
            return("None")
        return(str(degree_val))

    def calcul(self, equation, gui):
        # Process reduction / Calc
        reduced, reduced_list = ft_calculate(equation, self.variable, False)
        if len(reduced_list) == 0:
            if gui == False:
                print("Error: Invalid input")
                exit(0)
            else:
                result = "Error: Invalid input"
        # Get degree
        degree_val = self.degree(reduced)
        discr = ""
        # Calculate equation's result
        if degree_val == "None":
            if "=" in reduced and self.variable not in reduced:
                result = "None"
            else:
                result = reduced
            reduced = "None"
        else:
            discr, result = ft_solve(reduced, self.variable, int(degree_val))
        if gui == False:
            if self.variable != "None" and degree_val != "None":
                print("Degree : " + degree_val)
                print("Reduced form : " + reduced)
                if degree_val == "2":
                    if discr > 0:
                        discr = str(discr) + " >0"
                    elif discr < 0:
                        discr = str(discr) + " <0"
                    elif discr == 0:
                        discr = str(discr) + " =0"
                    print("Discriminant : " + discr)
                    print("Result(s) : " + result)
                else:
                    print("Result : " + result)
            else:
                print("Result : " + result)
        return (reduced, degree_val, discr, result)

    def parse(self, equation, gui):
        if len(equation) == 0:
            print("Error: Empty input")
            exit(0)
        if re.match(r"^[a-zA-Z0-9.+\-/*\^= ]+$", equation) and \
           all(not re.search(filt, equation) for filt in self.filters):
            equation = equation.replace(",", ".")
            if self.check(equation, gui) == True:
                return (equation)
            else:
                return (False)
        else:
            print("Error: Invalid input")
            exit(0)

    def check(self, equation, gui):
        for char in equation:
            if re.match('^[a-zA-Z]+$', char) and char != "i" and char != "I":
                if self.variable == "None":
                    self.variable = char
                elif char != self.variable:
                    if gui == False:
                        print("Error: Only one variable supported")
                        exit(0)
                    else:
                        return (False)
            elif char == "i" or char == "I":
                if gui == False:
                    print("Error: 'i' or 'I' var forbidden")
                    exit(0)
                else:
                    return (False)
        if re.search(r"([\^][a-zA-Z])", equation):
            if gui == False:
                print("Error: Variable power not allowed")
                exit(0)
            else:
                return (False)
        var_count = 0
        for char in equation:
            if char == self.variable:
                var_count += 1
        if "=" in equation:
            if self.variable == "None" or \
               (var_count == 1 and self.variable + "^0" in equation):
                if gui == False:
                    print("Error: Missing variable")
                    exit(0)
                else:
                    return (False)
        return (True)


import argparse
import logging as logger

from src import *
from src.runs.preprocess_genia import run_preprocess_genia
from src.runs.training import run_training
from src.utils.config import Config

if __name__ != '__main__':
    exit(0)

parser = argparse.ArgumentParser(description='Arguments for preprocess.')
parser.add_argument('--log_to_file',
                    default=None,
                    type=none_or_str,
                    action='store')
parser.add_argument('--dataset',
                    type=str,
                    action='store')
parser.add_argument('--raw_filepath',
                    type=str,
                    default='./data/GENIA/GENIAcorpus3.02.merged.xml',
                    action='store')
parser.add_argument('--cased',
                    type=int,
                    default=False,
                    action='store')
parser.add_argument('--lm_name',
                    default='dmis-lab/biobert-v1.1',
                    type=str,
                    action='store')
args = parser.parse_args()
setup_logger(logger, args.log_to_file)

if args.dataset == 'genia':
    run_preprocess_genia(logger, args.raw_filepath, args.cased, args.lm_name)
else:
    logger.error('Unknown dataset: %s' % args.dataset)
    raise Exception('Unknown dataset: %s' % args.dataset)


# 问题描述
# 写一个方法找到两个单链表最初的交叉节点并返回该节点。若两个链表没有交叉，则返回null

# 示例
# 输入AB两个链表：
# A: a1 -> a2
# B:       b1 -> b2 -> b3    ===> c1 -> c2 -> c3
# 输出：c1

# 源码实现
class ListNode:
	def __init__(self, val = None, next = None):
		self.value = val
		self.next = next

class Solution:
	def get_list_length(self, head):
		length = 0
		while head:
			length += 1
			head = head.next
		return length

	def get_intersect_node(self, list_a, list_b):
		length_a = self.get_list_length(list_a)
		length_b = self.get_list_length(list_b)
		cur1, cur2 = list_a, list_b
		if length_a > length_b:
			for i in range(length_a - length_b):
				cur1 = cur1.next
		else:
			for i in range(length_b - length_a):
				cur2 = cur2.next
		flag = False
		while cur1 and cur2:
			if cur1.value == cur2.value:
				print(cur1.value)
				flag = True
				break
			else:
				cur1 = cur1.next
				cur2 = cur2.next
		if not flag:
			print('链表没有交叉节点')

if __name__ == '__main__':
	s = Solution()
	list_a = ListNode('a1', ListNode('a2', ListNode('c1', ListNode('c2', ListNode('c3')))))
	list_b = ListNode('b1', ListNode('b2', ListNode('b3', ListNode('c1', ListNode('c2', ListNode('c3'))))))
	print("输入：")
	print("a = a1 a2 c1 c2 c3")
	print("b = b1 b2 b3 c1 c2 c3")
	print("输出：")
	s.get_intersect_node(list_a, list_b)

# 运行结果
# 输入：
# a = a1 a2 c1 c2 c3
# b = b1 b2 b3 c1 c2 c3
# 输出：
# c1

进阶/例287.两个链表的交叉.py

例287.两个链表的交叉.py


"""Tests ``execution`` module."""

import pytest

from uranie_launcher import execution

## execution
def test_execution_default():
    """test exec default"""

    exe = execution.Execution(working_directory=".")

    assert (
        exe.working_directory == "." and
        exe.clean is True and
        exe.save == -1 and
        exe.visualization is False
    )

def test_execution_enable_visualization():
    """test exec enable visualization"""

    exe = execution.Execution(working_directory=".")
    exe.enable_visualization()
    exe.clean_outputs(False)
    exe.save_outputs(0)

    assert (
        exe.clean is False and
        exe.save == 0 and
        exe.visualization is True
    )

def test_execution_local():
    """test local exec"""

    with pytest.raises(ValueError) as error:
        execution.ExecutionLocal(working_directory=".", nb_jobs=0)
    assert "nb_jobs' (0) must be >=1." in str(error.value)

    nb_jobs = 2
    exe = execution.ExecutionLocal(working_directory="test_execution_local", nb_jobs=nb_jobs)

    assert (
        exe.visualization is False and
        exe.nb_jobs == nb_jobs
    )

def test_execution_slurm_raise():
    """test slurm exec"""

    with pytest.raises(NotImplementedError) as error:
        execution.ExecutionSlurm()
    assert "Execution on cluster is not implemented yet" in str(error.value)


import pygame
from game.settings import WHITE

def display_text(
    display,
    text,
    x,
    y,
    size=50,
):
    font = pygame.font.Font("terminus.ttf", size)
    text = font.render(str(text), True, WHITE)
    display.blit(text, (x, y))

def is_between(val: float, from_to: list) -> bool:
    """
    Recives a val and a list with to values, from and to
    Returns True if val is between those two values
    """
    if len(from_to) == 2:
        if from_to[0] <= val <= from_to[1]:
            return True
        else:
            return False
    else:
        print("wrong list size")


"""
백준 문제 13325번 '이진 트리'
https://www.acmicpc.net/problem/13325

점화식을 세워 dynamic programming과 유사하게 풀면 끝.

time - O(V)
space - O(V)
auxilliary space - O(V)
V = number of nodes (== number of edges + 1)
"""
def tree_bin():
    k = int(input().strip())
    edge_weights = [int(c) for c in input().strip().split()]
    num_nodes = 2 ** (k + 1) - 1
    # root node = 0 (no weight)
    node_weights = [0] + edge_weights
    max_path = [0] * num_nodes

    # parent -> child : 2n + 1, 2n + 2, n == parent index
    # max_path(n) = max(max_path(left_child) + weight(left_child), max_path(right_child) + weight(right_child))
    for i in range(num_nodes - 1, -1, -1):
        if (i * 2 + 1) > (num_nodes - 1):
            # leaf nodes
            continue

        left_idx = i * 2 + 1
        right_idx = i * 2 + 2
        max_path_to = max(max_path[left_idx] + node_weights[left_idx],
                max_path[right_idx] + node_weights[right_idx])

        max_path[i] = max_path_to
        node_weights[left_idx] = max_path_to - max_path[left_idx]
        node_weights[right_idx] = max_path_to - max_path[right_idx]

    return sum(node_weights)

if __name__ == '__main__':
    print(tree_bin())


from django.shortcuts import render
from .models import Schedule
from .forms import ScheduleForm, NewSchedule

def home(request):

	all_items = Schedule.objects.all
	return render(request, 'home.html', {'all_items':all_items})

def create(request):

	if request.method == 'POST':
		form = NewSchedule(request.POST or None)

		if form.is_valid():
			usuario = form.cleaned_data["usuario"]
			jornada = form.cleaned_data["jornada"]
			codigo = form.cleaned_data["codigo"]
			date = form.cleaned_data["date1"]
			t = Schedule(usuario=usuario, jornada=jornada, codigo=codigo, date=date)
			t.save()
		return render(request, "create.html" , {"form": form})

	else:
		form = NewSchedule()

	return render(request, "create.html" , {"form": form})

	# if request.method == 'POST':
	# 	form = ScheduleForm(request.POST or None)

	# 	if form.is_valid():
	# 		form.save()
	# 		all_items = Schedule.objects.all
	# 		return render(request, 'create.html' , {'all_items' : all_items})
	# else:

	# 	all_items = Schedule.objects.all
	# 	return render(request, 'create.html', {'all_items' : all_items})

# def create_user(request):
#     user_form = UserForm()
#     return render(request, 'my_template.html', {'my_form': user_form})


import requests
from django.core.cache import cache
from rest_framework import status
from rest_framework.response import Response
from rest_framework.generics import ListCreateAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

from .permissions import IsOwnerOrReadOnly
from .serializers import *

class ImageSearchView(APIView):
    def get(self, request):
        keyword = request.GET.get('keyword', None)
        if not keyword:
            return Response({'error': 'Please provide a keyword in the request.'}, status=status.HTTP_400_BAD_REQUEST)

        cache_key = f'images_{keyword}'
        cached_data = cache.get(cache_key)
        if cached_data:
            return Response(cached_data, status=status.HTTP_200_OK)

        # Ваш ключ API и URL внешнего сервиса для поиска изображений
        api_key = 'ВАШ_API_КЛЮЧ'
        api_url = f'https://example.com/api/images?keyword={keyword}&api_key={api_key}'

        try:
            # Запрос к внешнему сервису для получения данных об изображениях
            response = requests.get(api_url)
            data = response.json()
        except requests.RequestException as e:
            return Response({'error': 'Failed to fetch data from the external service.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        images = []
        for item in data['images']:
            # Сохранение данных об изображениях в базе данных
            image, _ = Image.objects.get_or_create(
                url=item['url'],
                description=item['description'],
                source=item['source']
            )
            images.append(image)

        serializer = ImageSerializer(images, many=True)
        cache.set(cache_key, serializer.data, timeout=60 * 15)  # Кэширование на 15 минут
        return Response(serializer.data, status=status.HTTP_200_OK)

class WomenAPIList(ListCreateAPIView):
    queryset = Women.objects.all()
    serializer_class = WomenSerializer
    permission_classes = [IsAuthenticated]

class WomenAPIUpdate(RetrieveUpdateAPIView):
    queryset = Women.objects.all()
    serializer_class = WomenSerializer
    permission_classes = [IsOwnerOrReadOnly]

class WomenAPIDestroy(RetrieveDestroyAPIView):
    queryset = Women.objects.all()
    serializer_class = WomenSerializer
    permission_classes = [IsOwnerOrReadOnly]

    # def get_queryset(self):
    #     pk = self.kwargs.get('pk')
    #
    #     if not pk:
    #         return Women.objects.all()[:3]
    #
    #     return Women.objects.filter(pk=pk)
    #
    # @action(methods=['get'], detail=True)
    # def category(self, request, pk=None):
    #     cats = Category.objects.get(pk=pk)
    #     return Response({'cats': cats.name})


# 类别中每个属性都要有初始值，就算这个值是0或空字串都可以，例如在设定预设值时，在__init__方法中指定这初始值是允许的，如果对某个属性进行了这样的设定，就无需放入对此属性提供初始值的参数

class Car():
    def __init__(self,year,brand,color):
        self.year = year
        self.brand = brand
        self.color = color
        self.miles = 0 # 建立一个miles属性初始值为0

    def get_mile(self): # 定义一个get_mile方法可以存取miles
        print("Your "+self.brand+" has "+str(self.miles)+" miles on it")

    def update_mile(self,mileage): # 更新公里数
        self.miles = mileage

    def add_mile(self,kilometer): # 增加公里数
        self.miles += kilometer # 接收一个kilometer值加到self.miles里

car1 = Car(10,"toyota","black") # 建立一个car1实例
car1.get_mile() # 用实例呼叫get_mile方法

# * 直接修改属性的值

car1.miles = 87 # 使用据点标示法来直接存取并设定出仔的miles属性
car1.get_mile() # 在car1实例中找出miles属性并将87指定进去

# * 透过方法修改属性的值

car1.update_mile(87) # 呼叫update_mile方法并将87当作引数传入mileage参数中
car1.get_mile()

# * 利用方法进行值的增加
car1.update_mile(8730)
car1.get_mile()

car1.add_mile(9400) # 呼叫add_mile方法并传入9400
car1.get_mile()