# -*- coding=utf-8 -*-
'''
# @filename  : config.py
# @author    : cjr
# @date      : 2021-4-14
# @brief     : several config functions
'''
import torch
import torch.nn as nn

MEANS = (104, 117, 123)

voc = {
    #'num_classes': 21,
    #'lr_steps': (80000, 100000, 120000),
    #'max_iter': 120000,
    'feature_maps': [38, 19, 10, 5, 3, 1],
    'min_dim': 300,
    'steps': [8, 16, 32, 64, 100, 300],
    'min_sizes': [30, 60, 111, 162, 213, 264],
    'max_sizes': [60, 111, 162, 213, 264, 315],
    'aspect_ratios': [[2], [2, 3], [2, 3], [2, 3], [2], [2]],
    'variance': [0.1, 0.2],
    'clip': True,
    'name': 'VOC',
}

def detection_collate(batch):
    """Custom collate fn for dealing with batches of images that have a different
    number of associated object annotations (bounding boxes).

    Arguments:
        batch: (tuple) A tuple of tensor images and lists of annotations

    Return:
        A tuple containing:
            1) (tensor) batch of images stacked on their 0 dim
            2) (list of tensors) annotations for a given image are stacked on
                                 0 dim
    """
    targets = []
    imgs = []
    for sample in batch:
        imgs.append(sample[0])
        targets.append(torch.FloatTensor(sample[1]))
    return torch.stack(imgs, 0), targets

def xavier(param):
    torch.nn.init.xavier_uniform_(param)

def weights_init(m):
    if isinstance(m, nn.Conv2d):
        xavier(m.weight.data)
        m.bias.data.zero_()


import datetime
import uuid

from datetime import timedelta
from functools import wraps, update_wrapper

from flask import current_app, jsonify, make_response, request

class Auth(object):
    """Simple handler for auth tokens"""
    auth_tokens = {}

    def generate_token(self):
        return str(uuid.uuid4())

    def generate_expiration(self):
        return datetime.datetime.now() + datetime.timedelta(minutes=30)

    def set_token(self, token=None):
        if token is None:
            token = self.generate_token()
        self.auth_tokens[token] = self.generate_expiration()
        return token

    def valid_token(self, token):
        """Check is token is still valid

        If token is still live, update expiration
        otherwise remove it
        """
        if token in self.auth_tokens:
            if datetime.datetime.now() < self.auth_tokens[token]:
                self.auth_tokens[token] = self.generate_expiration()
                return True
            self.remove_token(token)
        return False

    def remove_token(self, token):
        try:
            self.auth_tokens.pop(token)
        except KeyError:
            # appears we don't actually have an auth token
            pass
        # need to periodically clean out auth tokens that
        # have expired
        for token, expiration in self.auth_tokens.iteritems():
            if expiration < datetime.datetime.now():
                self.auth_tokens.pop(token)

def check_auth(username, password):
    """Check is username password combination is valid"""
    from models.auth import AuthUser
    u = AuthUser.query.filter(AuthUser.username == username).first()
    if u is not None and u.check_password(password):
        return True
    return False

def authenticate():
    """Sends 401 response that enables basic auth"""
    message = {'message': 'Authenticate.'}
    res = jsonify(message)
    res.status_code = 401
    res.headers['WWW-Authenticate'] = 'Basic realm="Login Required"'
    return res

def requires_auth(f):
    """Checks whether user is logged in or raises error 401

    User able to have been logged in already and have session variable
    Or user is passing in authorization information
    """
    def valid_auth(request):
        auth = request.authorization
        if auth and check_auth(auth.username, auth.password):
            return True
        if 'AuthToken' in request.headers:
            if current_app.auth.valid_token(request.headers.get('AuthToken')):
                return True
        return False

    @wraps(f)
    def decorator(*args, **kwargs):
        if request.method != 'OPTIONS':
            if not valid_auth(request):
                return authenticate()
        return f(*args, **kwargs)
    return decorator

def register_api(app, view, endpoint, url, pk='id', pk_type='int'):
    view_func = view.as_view(endpoint)

    app.add_url_rule(
        url,
        defaults={"{0}".format(pk): None},
        view_func=view_func,
        methods=['GET', ]
    )
    app.add_url_rule(
        url,
        view_func=view_func,
        methods=['POST', ]
    )
    app.add_url_rule(
        "{url}/<{pk_type}:{pk}>".format(url=url, pk_type=pk_type, pk=pk),
        view_func=view_func,
        methods=['GET', 'PUT', 'DELETE']
    )

def crossdomain(origin=None, methods=None, headers=None,
                max_age=21600, attach_to_all=True,
                automatic_options=True):
    if methods is not None:
        methods = ', '.join(sorted(x.upper() for x in methods))
    if headers is not None and not isinstance(headers, basestring):
        headers = ', '.join(x.upper() for x in headers)
    if origin is not None and not isinstance(origin, basestring):
        origin = ', '.join(origin)
    if isinstance(max_age, timedelta):
        max_age = max_age.total_seconds()

    def get_methods():
        if methods is not None:
            return methods

        options_resp = current_app.make_default_options_response()
        return options_resp.headers['allow']

    def decorator(f):
        def wrapped_function(*args, **kwargs):
            if automatic_options and request.method == 'OPTIONS':
                resp = current_app.make_default_options_response()
                resp.status_code = 204
            else:
                resp = make_response(f(*args, **kwargs))
            if not attach_to_all and request.method != 'OPTIONS':
                return resp

            h = resp.headers

            h['Access-Control-Allow-Origin'] = origin
            h['Access-Control-Allow-Methods'] = get_methods()
            h['Access-Control-Max-Age'] = str(max_age)
            if headers is not None:
                h['Access-Control-Allow-Headers'] = headers

            return resp

        f.provide_automatic_options = False
        f.required_methods = ['OPTIONS']
        return update_wrapper(wrapped_function, f)
    return decorator


import numpy as np

from sklearn.datasets import fetch_mldata
import matplotlib.pyplot as plt

np.random.seed(1847)

class BatchSampler(object):
    '''
    A (very) simple wrapper to randomly sample batches without replacement.

    You shouldn't need to touch this.
    '''

    def __init__(self, data, targets, batch_size):
        self.num_points = data.shape[0]
        self.features = data.shape[1]
        self.batch_size = batch_size

        self.data = data
        self.targets = targets

        self.indices = np.arange(self.num_points)

    def random_batch_indices(self, m=None):
        '''
        Get random batch indices without replacement from the dataset.

        If m is given the batch will be of size m. Otherwise will default to the class initialized value.
        '''
        if m is None:
            indices = np.random.choice(self.indices, self.batch_size, replace=False)
        else:
            indices = np.random.choice(self.indices, m, replace=False)
        return indices

    def get_batch(self, m=None):
        '''
        Get a random batch without replacement from the dataset.

        If m is given the batch will be of size m. Otherwise will default to the class initialized value.
        '''
        indices = self.random_batch_indices(m)
        X_batch = np.take(self.data, indices, 0)
        y_batch = self.targets[indices]
        return X_batch, y_batch

class GDOptimizer(object):
    '''
    A gradient descent optimizer with momentum

        lr - learning rate
        beta - momentum hyperparameter
    '''

    def __init__(self, lr, beta):
        self.lr = lr
        self.beta = beta
        self.vel = 0.0

    def update_params(self, params, grad):
        # Update parameters using GD with momentum and return
        # the updated parameters
        self.vel = -self.lr * grad(params) + self.beta * self.vel
        return params + self.vel

class SVM(object):
    '''
    A Support Vector Machine
    '''

    def __init__(self, c, feature_count, bias):
        self.c = c
        self.w = np.random.normal(0.0, 0.1, feature_count)
        self.bias = bias
        self.feature_count = feature_count

    def hinge_loss(self, X, y):
        '''
        Compute the hinge-loss for input data X (shape (n, m)) with target y (shape (n,)).

        Returns a length-n vector containing the hinge-loss per data point.
        '''
        # Implement hinge loss
        h_loss=[]
        for i in range(len(y)):
            h_loss.append(max((1 - y[i] * (np.dot(self.w, X[i]) + self.bias)),0))
        return np.array(h_loss)

    def grad(self, X, y):
        '''
        Compute the gradient of the SVM objective for input data X (shape (n, m))
        with target y (shape (n,))

        Returns the gradient with respect to the SVM parameters (shape (m,)).
        '''
        # Compute (sub-)gradient of SVM objective
        h_loss = self.hinge_loss(X,y)
        sum_over = np.zeros(self.feature_count)
        for i in range(len(y)):
            if h_loss[i] > 0:
                sum_over += X[i] * y[i]
        return (self.w - sum_over)*(self.c / len(y))

    def classify(self, X):
        '''
        Classify new input data matrix (shape (n,m)).

        Returns the predicted class labels (shape (n,))
        '''
        # Classify points as +1 or -1
        classifies = []
        for i in range(X.shape[0]):
            sign = np.dot(self.w,X[i]) + self.bias
            classifies.append(sign)
        return np.sign(np.array(classifies))

def load_data():
    '''
    Load MNIST data (4 and 9 only) and split into train and test
    '''
    mnist = fetch_mldata('MNIST original', data_home='./data')
    label_4 = (mnist.target == 4)
    label_9 = (mnist.target == 9)

    data_4, targets_4 = mnist.data[label_4], np.ones(np.sum(label_4))
    data_9, targets_9 = mnist.data[label_9], -np.ones(np.sum(label_9))

    data = np.concatenate([data_4, data_9], 0)
    data = data / 255.0
    targets = np.concatenate([targets_4, targets_9], 0)

    permuted = np.random.permutation(data.shape[0])
    train_size = int(np.floor(data.shape[0] * 0.8))

    train_data, train_targets = data[permuted[:train_size]], targets[permuted[:train_size]]
    test_data, test_targets = data[permuted[train_size:]], targets[permuted[train_size:]]
    print("Data Loaded")
    print("Train size: {}".format(train_size))
    print("Test size: {}".format(data.shape[0] - train_size))
    print("-------------------------------")
    return train_data, train_targets, test_data, test_targets

def optimize_test_function(optimizer, w_init=10.0, steps=200):
    '''
    Optimize the simple quadratic test function and return the parameter history.
    '''
    def func(x):
        return 0.01 * x * x

    def func_grad(x):
        return 0.02 * x

    w = w_init
    w_history = [w_init]

    for _ in range(steps):
        w = optimizer.update_params(w, func_grad)
        w_history.append(w)
    return w_history

def optimize_svm(train_data, train_targets, penalty, optimizer, batchsize, iters, beta):
    '''
    Optimize the SVM with the given hyperparameters. Return the trained SVM.

    SVM weights can be updated using the attribute 'w'. i.e. 'svm.w = updated_weights'
    '''
    batch_sampler = BatchSampler(train_data, train_targets, batchsize)
    svm = SVM(penalty, train_data[0].shape[0], 1)
    theta = 0
    for  i in range(iters):
        X_b, y_b = batch_sampler.get_batch()
        theta = -0.05 * svm.grad(X_b, y_b) + beta * theta
        svm.w += theta
    return svm

def q2_1():
    t = list(range(0, 201))
    w_history = []
    for i in range(2):
        momentumGD_optimizer = GDOptimizer(1.0,0.9*i)
        w_history.append(optimize_test_function(momentumGD_optimizer))
    plt.plot(t,w_history[0],'r')
    plt.plot(t,w_history[1],'g')
    plt.show()

def q2_2(beta):
    train_data, train_targets, test_data, test_targets = load_data()
    momentumGD_optimizer = GDOptimizer(1.0,0.9)
    # for i in range(2):
    svm = optimize_svm(train_data, train_targets, 1, momentumGD_optimizer, 100, 500, beta)

    train_accuracy = svm.classify(train_data)
    test_accuracy = svm.classify(test_data)
    # print accuracy
    print("The training set accuracy is:",(train_targets == train_accuracy).mean())
    print("The test set accuracy is:", (test_targets == test_accuracy).mean())
    print("The hinge loss of training set is :", (svm.hinge_loss(train_data,train_targets)).mean())
    print("The hinge loss of test set is :", (svm.hinge_loss(test_data,test_targets)).mean())
    plt.imshow(svm.w.reshape(28,28),cmap='gray')
    plt.show()

if __name__ == '__main__':
    # q2_1()
    q2_2(0)
    # q2_2(0.1)
    pass


import numpy as np
import scipy.stats as sts
from numba.pycc import CC

S = 1000
T = 4160
rho, mu, sigma = 0.5, 3.0, 1.0
z_0 = mu

np.random.seed(25)
eps_mat = sts.norm.rvs(loc=0, scale=sigma, size=(T, S))
z_mat = np.zeros((T, S))

cc = CC('test_aot')

@cc.export('simulate_aot', 'f8[:,:](f8[:,:], f4, f4)')
def simulate_aot(z_mat, rho, mu):
    '''
    Compute S simulations of a lifetime T - AOT ACCELERATED
    Inputs:
        z_mat: (numpy) container for simulation results
        rho: (float) degree of persistence
        mu: long-run average
    Return:
        z_mat: (numpy) simulation result of size (T, S)
    '''

    T, S = z_mat.shape
    for s_ind in range(S):
        z_tm1 = z_0
        for t_ind in range(T):
            e_t = eps_mat[t_ind, s_ind]
            z_t = rho * z_tm1 + (1 - rho) * mu + e_t
            z_mat[t_ind, s_ind] = z_t
            z_tm1 = z_t

    return z_mat

cc.compile()


from utils.jwt import get_current_user, create_access_token

from models.user import session, User, UserDelete, UserRegister, UserLogin, UserRead
from models.device import DeviceDelete, DeviceUpdatePosition, DevicesList, Device
from models.security import Token
from models.helper import SuccessResponse, UuidResponse, UuidRequest

from typing import Optional
from uuid import UUID, uuid4

from fastapi import Depends, APIRouter, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from pydantic import BaseModel

user_router = APIRouter(prefix="/user")

# does not require auth
@user_router.post("/create", response_model=Token)
async def user_create():
    """
    creates basic user and returnes basic id from the user
    """

    user: User = User()
    session.add(user)
    session.commit()
    session.refresh(user)

    access_token = create_access_token(user.uuid)

    return {"access_token": str(access_token), "token_type": "bearer"}

@user_router.delete("/delete", response_model=SuccessResponse)
async def user_delete(request: UserDelete):
    """
    deletes requested user
    """

    user = User.query.get(uuid=request.user_uuid)

    if not user:
        return {"success": False}

    session.delete(user)
    session.commit()

    return {"success": True}

@user_router.post("/info", response_model=UserRead)
async def user_read(current_user: User = Depends(get_current_user)):
    """
    changes role of user to specified
    """

    return current_user

@user_router.put("/device", response_model=UuidResponse)
async def user_device_create(current_user: User = Depends(get_current_user)):
    """
    adds a new device to a user
    """

    device = Device(user_uuid=current_user.uuid)

    session.add(device)
    session.commit()
    session.refresh(device)

    return {"uuid": device.uuid}

@user_router.delete("/device", response_model=SuccessResponse)
async def user_device_delete(request: DeviceDelete, current_user: User = Depends(get_current_user)):
    """
    removes device from user
    """

    device = Device.query.get(uuid=request.uuid)

    if not device:
        return {"success": False}

    session.delete(device)
    session.commit()

    return {"success": True}

@user_router.post("/device/update", response_model=SuccessResponse)
async def user_device_update_position(request: DeviceUpdatePosition, current_user: User = Depends(get_current_user)):
    """
    updates the position of specified device
    """

    device = Device.query.get(uuid=request.uuid)

    if not device:
        return {"success": False}

    device.latitude = request.lat
    device.longitude = request.lon

    session.commit()

    return {"success": True}

@user_router.post("/device/list", response_model=DevicesList)
async def user_device_read(current_user: User = Depends(get_current_user)):
    """
    list all devices belonging to this user
    """

    return {"devices": session.query(Device.uuid).filter_by(user_uuid=current_user.uuid).all()}


from collections import deque
def bfs(x, y, index):
    global N, graph,steps
    q = deque()
    q.append((x, y))
    graph[x][y] = index
    cnt = 1
    while q:
        x, y = q.popleft()
        for dx, dy in steps:
            nx, ny = x + dx, y + dy
            if 0 <= nx < N and 0 <= ny < N:
                if graph[nx][ny] == 1:
                    q.append((nx, ny))
                    graph[nx][ny] = index
                    cnt += 1
    return cnt

steps = [(1,0),(0,1),(-1,0),(0,-1)]
N = int(input())
graph = list(list(map(int,list(input()))) for _ in range(N))
res = []
for i in range(N):
    for j in range(N):
        if graph[i][j] == 1:
            res.append(bfs(i,j,len(res) + 2))
print(len(res))
for r in sorted(res):
    print(r)

기초/그래프와 DFS/큐와 그래프/2667번 - 단지번호붙이기.py

2667번 - 단지번호붙이기.py


#1、创建8张表
#creat table user_0 like user_nhy;

#2、先从源表里面查到数据，然后取到phone,然后 % 8 计算出来在哪张表里，然后insert
#select * from user;
#[{id:1,nname:xx}]
#insert into user_0 values ()

#insert into user_0 select * from user where id =1;

from day8.homework import utlis
def create_table_more(src_table_name,number=8): #user_0 user_1
    print("开始创建表")
    for i in range(number):
        new_table_name = "%s_%s" % (src_table_name,i)
        sql = "create table if not exists %s like %s;" % (new_table_name,src_table_name)
        # sql = "drop table %s" % new_table_name
        print("创建表的sql ",sql)
        utlis.op_mysql(sql)
    print("分表创建完成")

def insert_data(src_table_name,number=8):
    sql = "select * from %s ; " % src_table_name
    all_data = utlis.op_mysql(sql,data_type=2)
    # print(all_data[0].keys())
    # return
    for data in all_data:
        table_index = int(data.get("phone")) % number
        table = "%s_%s" % (src_table_name,table_index)
        insert_sql = 'insert into %s select "",nick, gender, birthday, email, phone, password, qq, address, status, pay_status, avatar, register_time, last_login_ip, last_login_time from %s where phone = "%s"; ' % (table,src_table_name,data.get("phone"))
        utlis.op_mysql(insert_sql)
        print("数据 %s 迁移完成 " % data)
    print("所有数据迁移完成！")

if __name__ == '__main__':
    create_table_more("user_gy")
    insert_data("user_gy")


#text dataset creation from images

import cv2
import pytesseract

import os
line=[]
filepath = 'test.txt'
path = []
label = []
with open(filepath) as fp:
    for cnt, line in enumerate(fp):
        data = line.split()
        path.append( data[0])
        label.append("__label__" + data[1])
i=0
with open("dataset3.txt", 'w') as writer:
    for pa in path:
        try:
            img = cv2.imread(pa,0)
            img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
            img = cv2.GaussianBlur(img, (5, 5), 0)
            img = cv2.medianBlur(img, 3)
            img = cv2.bilateralFilter(img,9,75,75)
            text = pytesseract.image_to_string(img).rstrip()
            text = text.split()

            new=""
            for t in text:
                new = new +" "+t
            print(new)
            writer.write(label[i]+" "+new+"\n")
            i=i+1

        except:
            print("errror")


def answer_thirteen():
    Top15 = answer_one()
    Top15['PopEst'] = Top15['Energy Supply'] / Top15['Energy Supply per Capita']
    Top15 = Top15['PopEst']
    for i in range(len(Top15)):
        country = Top15.keys()[i]
        number = "{:,}".format((Top15.iloc[i]))
        Top15.replace(Top15.iloc[i], number, inplace=True)
    return Top15
answer_thirteen()


# Data gathering
#----------------------------------
#
# This function gives us the ways to access
# the various data sets we will need

# Data Gathering
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.python.framework import ops
ops.reset_default_graph()

# Iris Data
from sklearn import datasets

iris = datasets.load_iris()
print(len(iris.data))
print(len(iris.target))
print(iris.data[0])
print(set(iris.target))

# Low Birthrate Data
import requests

birthdata_url = 'https://github.com/nfmcclure/tensorflow_cookbook/raw/master/01_Introduction/07_Working_with_Data_Sources/birthweight_data/birthweight.dat'
birth_file = requests.get(birthdata_url)
birth_data = birth_file.text.split('\r\n')
birth_header = birth_data[0].split('\t')
birth_data = [[float(x) for x in y.split('\t') if len(x)>=1] for y in birth_data[1:] if len(y)>=1]
print(len(birth_data))
print(len(birth_data[0]))

# Housing Price Data
from keras.datasets import boston_housing
(x_train, y_train), (x_test, y_test) = boston_housing.load_data()
housing_header = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
print(x_train.shape[0])
print(x_train.shape[1])

# MNIST Handwriting Data
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
print(len(mnist.train.images))
print(len(mnist.test.images))
print(len(mnist.validation.images))
print(mnist.train.labels[1,:])

# CIFAR-10 Image Category Dataset
# The CIFAR-10 data ( https://www.cs.toronto.edu/~kriz/cifar.html ) contains 60,000 32x32 color images of 10 classes.
# It was collected by Alex Krizhevsky, Vinod Nair, and Geoffrey Hinton.
# Alex Krizhevsky maintains the page referenced here.
# This is such a common dataset, that there are built in functions in TensorFlow to access this data.

# Running this command requires an internet connection and a few minutes to download all the images.
(X_train, y_train), (X_test, y_test) = tf.contrib.keras.datasets.cifar10.load_data()

print(X_train.shape)
print(y_train.shape)
print(y_train[0,])  # this is a frog

# Plot the 0-th image (a frog)
from PIL import Image
img = Image.fromarray(X_train[0,:,:,:])
plt.imshow(img)

# Ham/Spam Text Data
import requests
import io
from zipfile import ZipFile

# Get/read zip file
zip_url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'
r = requests.get(zip_url)
z = ZipFile(io.BytesIO(r.content))
file = z.read('SMSSpamCollection')
# Format Data
text_data = file.decode()
text_data = text_data.encode('ascii',errors='ignore')
text_data = text_data.decode().split('\n')
text_data = [x.split('\t') for x in text_data if len(x)>=1]
[text_data_target, text_data_train] = [list(x) for x in zip(*text_data)]
print(len(text_data_train))
print(set(text_data_target))
print(text_data_train[1])

# Movie Review Data
import requests
import io
import tarfile

movie_data_url = 'http://www.cs.cornell.edu/people/pabo/movie-review-data/rt-polaritydata.tar.gz'
r = requests.get(movie_data_url)
# Stream data into temp object
stream_data = io.BytesIO(r.content)
tmp = io.BytesIO()
while True:
    s = stream_data.read(16384)
    if not s:
        break
    tmp.write(s)
stream_data.close()
tmp.seek(0)
# Extract tar file
tar_file = tarfile.open(fileobj=tmp, mode="r:gz")
pos = tar_file.extractfile('rt-polaritydata/rt-polarity.pos')
neg = tar_file.extractfile('rt-polaritydata/rt-polarity.neg')
# Save pos/neg reviews
pos_data = []
for line in pos:
    pos_data.append(line.decode('ISO-8859-1').encode('ascii',errors='ignore').decode())
neg_data = []
for line in neg:
    neg_data.append(line.decode('ISO-8859-1').encode('ascii',errors='ignore').decode())
tar_file.close()

print(len(pos_data))
print(len(neg_data))
print(neg_data[0])

# The Works of Shakespeare Data
import requests

shakespeare_url = 'http://www.gutenberg.org/cache/epub/100/pg100.txt'
# Get Shakespeare text
response = requests.get(shakespeare_url)
shakespeare_file = response.content
# Decode binary into string
shakespeare_text = shakespeare_file.decode('utf-8')
# Drop first few descriptive paragraphs.
shakespeare_text = shakespeare_text[7675:]
print(len(shakespeare_text))

# English-German Sentence Translation Data
import requests
import io
from zipfile import ZipFile
sentence_url = 'http://www.manythings.org/anki/deu-eng.zip'
r = requests.get(sentence_url)
z = ZipFile(io.BytesIO(r.content))
file = z.read('deu.txt')
# Format Data
eng_ger_data = file.decode()
eng_ger_data = eng_ger_data.encode('ascii',errors='ignore')
eng_ger_data = eng_ger_data.decode().split('\n')
eng_ger_data = [x.split('\t') for x in eng_ger_data if len(x)>=1]
[english_sentence, german_sentence] = [list(x) for x in zip(*eng_ger_data)]
print(len(english_sentence))
print(len(german_sentence))
print(eng_ger_data[10])


import numpy as np
from netCDF4 import Dataset #read netcdf
import re
from pyearth.system.define_global_variables import *
from pye3sm.tools.mpas.namelist.convert_namelist_to_dict import convert_namelist_to_dict2
from pyearth.toolbox.reader.parse_xml_file import parse_xml_file_atm

def atm_retrieve_forcing_data_info(oCase_in, sVariable_in):
    sWorkspace_simulation_case_run = oCase_in.sWorkspace_simulation_case_run
    sFilename_datm_in = sWorkspace_simulation_case_run + slash + 'datm_in'

    aParameter_datm = convert_namelist_to_dict2(sFilename_datm_in)
    aDict = aParameter_datm['streams']
    dummy = aDict.split(",")
    ndata  = len(dummy)
    for i  in range(ndata):
        s = dummy[i]
        if sVariable_in.lower() in s.lower():
            s = s.strip()
            s = re.sub("[\"\']", "", s)
            dummy = (s.split(" "))[0]
            sFilename = sWorkspace_simulation_case_run + slash + dummy

            #parse xml
            sFolder ,aField, aFilename = parse_xml_file_atm(sFilename,'fileNames')

    return sFolder, aField, aFilename


class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        temp = 0
        total = sum(nums)

        for i in range(len(nums)):
            if nums[i] == total - 2*temp:
                return i
            temp += nums[i]
        return -1


a=input()
nr=0
nr1=0
nr2=0
nr3=0

for i in a:
    if i=='_':
        nr=nr+1
    elif i>='a' and i<='z':
        nr1=nr1+1
    elif i>='A' and i<='Z':
        nr2=nr2+1
    else:
        nr3=nr3+1
print(nr/len(a),nr1/len(a),nr2/len(a),nr3/len(a))


def square_num():
    try:
        num = int(input('Input Integer: '))
    except ValueError:
        print('That is not an integer.')
    else:
        print(num, 'squared is', num**2)

def cube_num():
    num = input('Input Number: ')
    if num.isdigit():
        print(num, 'cubed is', int(num)**3)
    else:
        print('That is not an integer.')


# Yuchen Rong ID:75819508

import othello

def interface_console():
    print('FULL')
    col = othello.get_col()
    row = othello.get_row()
    game = othello.GameState(col,row)
    game._print()
    while game._check_board_full() == False:
        try:
            while True:
                user_input = user_move_input()
                col_num = int(user_input[0])-1
                row = int(user_input[1])-1
                if game._check_a_move(col_num,row) == True:
                        game._change_turn()
                        break
                else:
                        print('INVALID')
                        break
            print('\n')
            game._print()
            print('\n')
        except IndexError and ValueError:
            print('INVALID')
    print("Game Over")
    print('The winner is {}'.format(game._get_winner()))

def user_move_input()->list:
    """Get user's input and split it
    """
    user_input = input()
    try:
        input_list = user_input.split()
        return input_list
    except:
        print('Invalid')

if __name__ == '__main__':
    interface_console()


a = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

def turnOrder():
    while True:
        global currentTurn
        currentTurn = input("Who Will Go First: X or O?\nNote, The Best Way To Play Is If the Loser Of The Last Round Goes First - ")
        if currentTurn == "X" or currentTurn == "x":
            currentTurn = int(1)
            break
        elif currentTurn == "O" or currentTurn == "o":
            currentTurn = int(2)
            break
        print("\nError. Choose Either X or O To Go First\n")

def tttBoard():
    print(a[1] + "|" + a[2] + "|" + a[3])
    print("-+-+-")
    print(a[4] + "|" + a[5] + "|" + a[6])
    print("-+-+-")
    print(a[7] + "|" + a[8] + "|" + a[9])

def playerTurn():
    while True:
        pass
        global currentTurn
        if currentTurn%2 == 1:
            playerMarker = int(input("Player X, Choose A Spot - "))
        elif currentTurn%2 == 0:
            playerMarker = int(input("Player O, Choose A Spot - "))
        else:
            print("What how are you getting this message")

        if playerMarker > 0 and playerMarker <=9 and currentTurn%2 == 1:
            for i in range (len(a)):
                if a[i]== str(playerMarker):
                    a[i]= "X"
                    currentTurn = currentTurn + 1
            break
        elif playerMarker > 0 and playerMarker <=9 and currentTurn%2 == 0:
            for i in range (len(a)):
                if a[i]== str(playerMarker):
                    a[i]= "O"
                    currentTurn = currentTurn + 1
            break
        else:
            print("error")
            playerTurn()

    winCheck()

def winCheck():
    if (a[1] == a[2] and a[2] == a[3] and a[3] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    elif (a[4] == a[5] and a[5] == a[6] and a[6] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    elif (a[7] == a[8] and a[8] == a[9] and a[9] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    elif (a[1] == a[4] and a[4] == a[7] and a[7] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    elif (a[2] == a[5] and a[5] == a[8] and a[8] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    elif (a[3] == a[6] and a[6] == a[9] and a[9] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    elif (a[1] == a[5] and a[5] == a[9] and a[9] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    elif (a[2] == a[5] and a[5] == a[7] and a[7] == ("X" or "O")):
        print("\n" + a[1] + " Wins!")
    else:
        tttBoard()
        playerTurn()

turnOrder()
tttBoard()
playerTurn()


def solve_riddle(riddle, word_length, start_letter, reverse=False):
    # normalizing the given problem to not special-case it later
    problem = riddle[::-1] if reverse else riddle

    for i in range(len(problem)):
        if problem[i] == start_letter:
            return problem[i : (i + word_length)]

    # if no word was found, return an empty string
    return ''

print(solve_riddle('mi1rewopret', 5, 'p', True))


from boggle import Boggle
from flask import Flask, render_template, session, jsonify, request
from flask_debugtoolbar import DebugToolbarExtension

boggle_game = Boggle()

app = Flask(__name__)
app.config['SECRET_KEY'] = "oh-so-secret"

debug = DebugToolbarExtension(app)

@app.route("/")
def show_board():
    board = boggle_game.make_board()
    session['board'] = board
    highscore = session.get('highscore', 0)
    nplays = session.get('nplays', 0)
    context = {'board': board, 'highscore': highscore, 'nplays': nplays }

    return render_template('home.html', **context)

@app.route('/check-word')
def check_word():
    word = request.args['word']
    board = session['board']
    response = boggle_game.check_valid_word(board, word)
    print(response)
    return jsonify({'result': response})

@app.route('/post-score', methods=["POST"])
def update_score():
    last_score = request.json['score']
    highscore = session.get('highscore', 0)
    nplays = session.get('nplays', 0)
    session['nplays'] = nplays +1
    session['highscore'] = max(last_score, highscore)

    return jsonify(broken_record = True if last_score > highscore else False)


import re
class Table:
  __index=None
  __DICT=None
  __INVERT_DICT=None

  def __init__(self):
    self.__index=1
    self.__DICT={}
    self.__INVERT_DICT={}

  def addToTable(self,b):
    if type(b)==set:
      self.__DICT[self.__index]=b
      self.__index+=1

  def invert_index(self):
    for i in self.__DICT[self.__index-1]:
      if i not in self.__INVERT_DICT:
        self.__INVERT_DICT[i]=set()
      if i in self.__INVERT_DICT.keys():
        self.__INVERT_DICT[i].add(self.__index-1)

  def contains(self,find):
    if len(find)==0:
      print('False')
    else:
      newdict={}
      for i in find:
        if i in self.__INVERT_DICT.keys():
          newdict[i]=self.__INVERT_DICT.get(i)
        else:
          print('False')
          return

      temp=set()
      i=0
      for item in newdict.values():
        if i==0:
          temp=item.copy()
        temp.intersection_update(item)
        i+=1

      res=[]
      for i in temp:
        if find ==self.__DICT.get(i):
          res.append(self.__DICT.get(i))

      if len(res)!=0:
        print('True')
      else:
        print('False')

  def scontains(self,find):
    if len(find)==0:
      self.showTable()
    else:
      a=set()
      i=0
      for item in find:
        if all(elem in self.__INVERT_DICT.keys() for elem in find):
          if i==0:
            a=self.__INVERT_DICT.get(item).copy()
          else:
            a.intersection_update(self.__INVERT_DICT.get(item))
          i+=1
        else:
          print ('{}')
          break
      for i in a:
        print(self.__DICT.get(i))

  def intersects(self,find):
    if len(find)==0:
      self.showTable()
    else:
      a=set()
      for i in find:
        if i in self.__INVERT_DICT.keys():
          a.update((self.__INVERT_DICT.get(i)))

      for i in a:
        print(self.__DICT.get(i))

  def contained_by(self,find):
    a=set()
    for i in find:
      if i in self.__INVERT_DICT.keys():
        a.update((self.__INVERT_DICT.get(i)))
    for i in a:
      if self.__DICT.get(i).issubset(find):
        print(self.__DICT.get(i))

  def showTable(self):
    for i in self.__DICT.keys():
      print(f'{i}: {self.__DICT[i]}')

  def showInvertIndex(self):
    for i in self.__INVERT_DICT.keys():
      print(f'{i}: {self.__INVERT_DICT[i]}')

  def get(self):
    return self.__INVERT_DICT
  def getS(self):
    return self.__DICT

class SQL:
  __tablename=None
  __invert_dict=None
  __OBJ=None

  def __init__(self):
    self.__tablenames=[]
    self.__invert_dict={}
    self.d=Table()
    self.__OBJ={}

  def create(self,text):
    template=re.findall(r'\s*(CREATE)\s+(\w+)\s*;\s*',text.upper())
    if len(template)!=0:
      name=re.findall(r'\w\s+(\w+)',text)[0]
      self.__tablenames.append(name)
      self.__OBJ[name]=Table()
    else:
      print('error')

  def insert(self,text):
    template=re.findall(r'\s*(INSERT)\s+(\w+)\s+\{([^}]+)\}\s*\;\s*',text.upper())
    if len(template)!=0:
      name=re.findall(r'\w\s+(\w+)',text)[0]
      if name in self.__tablenames:
        self.__OBJ[name].addToTable(self.__getNumber(list(template[0])[-1]))
        self.__OBJ[name].invert_index()
      else:
        return print('wrong name')
    else:
      return print('error')

  def print_index(self,text):
    template=re.findall(r'\s*(PRINT_INDEX)\s+(\w+)\s*\;\s*',text.upper())
    if len(template)!=0:
      name=re.findall(r'\w\s+(\w+)',text)[0]
      if name in self.__tablenames:
        return self.__OBJ[name].showInvertIndex()
      else:
        return print('wrong name')
    else:
      return print('error')

  def newsearch(self,text):
    template=re.findall(r'\s*(SEARCH)\s+(\w+)\s*(\w+|)\s*(CONTAINS|CONTAINED_BY|INTERSECTS|)\s*(\{([^}]+|)\}|)\s*;\s*',text.upper())
    if len(template)!=0:
      name=re.findall(r'\w\s+(\w+)',text)[0]
      temp=template[0]
      if name in self.__tablenames:
        if temp[0].upper()=='SEARCH' and temp[2].upper()=='WHERE' and temp[3].upper()=='INTERSECTS' and temp[4]!='':
          return self.__OBJ[name].intersects(self.__getNumber(list(template[0])[-1]))
        if temp[0].upper()=='SEARCH' and temp[2].upper()=='WHERE' and temp[3].upper()=='CONTAINED_BY' and temp[4]!='':
          return self.__OBJ[name].contained_by(self.__getNumber(list(template[0])[-1]))
        if temp[0].upper()=='SEARCH' and temp[2].upper()=='WHERE' and temp[3].upper()=='CONTAINS':
          return self.__OBJ[name].scontains(self.__getNumber(list(template[0])[-1]))
        if temp[0].upper()=='SEARCH' and temp[2]=='' and temp[3]=='' and temp[4]=='' and temp[5]=='':
          return self.__OBJ[name].showTable()
        else:
          return print('error3')
      else:
        return print('wrong name')
    else:
      return print('error2')

  def contains(self,text):
    template=re.findall(r'\s*(CONTAINS)\s+(\w+)\s+\{([^}]+|)\}\s*\;\s*',text.upper())
    if len(template)!=0:
      name=re.findall(r'\w\s+(\w+)',text)[0]
      if name in self.__tablenames:
        return self.__OBJ[name].contains(self.__getNumber(list(template[0])[-1]))
      else:
        return print('wrong name')
    else:
      return print('error1')

  def __getNumber(self,text):
    if text.isspace() or len(text)==0:
      return set()
    else:
      try:
        result = [int(item) for item in text.split(',')]
      except:
        return print('')
      return set(result)

  def input_text(self):
    a=[]
    while 1:
      text=input('>>>')
      a.append(text+' ')
      if len(text)!=0 and text[-1]==';':
        break
      elif len(re.findall(r'\s*.EXIT\s*',text.upper()))!=0:
        return None
    e=[]
    t=re.split(r'(?:\\n)',''.join(a))
    for i in t:
      if i != '':
        e.append(i)
    return e

  def parser(self,text):
    temp=re.findall(r'\w+',text)
    if temp[0].upper()=='CREATE':
      self.create(text)
    elif temp[0].upper()=='INSERT':
      self.insert(text)
    elif temp[0].upper()=='PRINT_INDEX':
      self.print_index(text)
    elif temp[0].upper()=='SEARCH':
      self.newsearch(text)
    elif temp[0].upper()=='CONTAINS':
      self.contains(text)
    elif temp[0].upper() not in ['CREATE','INSERT','PRINT_INDEX','SEARCH','CONTAINS']:
      return print('ERROR')

  def process(self):
    print('for exit print ".exit"')
    FLAG=True
    while FLAG:
      LISTTEXT=self.input_text()
      if LISTTEXT==None:
        break
      for i in LISTTEXT:
        self.parser(i)

s=SQL()
s.process()


# Basic CNN (using Keras) following tutorial at
#     https://github.com/ajhalthor/audio-classifier-convNet
# Machine learning library: keras

import numpy as np # following all used by train_simple_keras
import keras
from   keras.layers import Activation, Dense, Dropout, Conv2D, \
                         Flatten, MaxPooling2D
from   keras.models import Sequential
from   keras.models import model_from_json
from   keras import backend as K
import librosa
import librosa.display
import pandas as pd
import random
import warnings
warnings.filterwarnings('ignore')

import os
import sys
sys.path.insert(0, '/home/dgabutler/Work/CMEEProject/Code')
import wavtools # contains custom functions e.g. denoising

from   pydub import AudioSegment # used by: search_file_
from   pydub.utils import make_chunks # used by: search_file_
from   shutil import copyfile # used by: search_file_
from   shutil import rmtree # used by: search_file_
import glob # used by: search_file_, search_folder_, hard_negative_miner
import subprocess # used by: search_file_
import time # used by: search_folder_
import pathlib # used by: search_file_
import csv # used by: search_file_
import matplotlib.pyplot as plt # used by train_simple_
from   sklearn.preprocessing import LabelEncoder # added for single output layer
from   sklearn.preprocessing import scale
from   sklearn.metrics import roc_curve # used in train_simple_
from   sklearn.metrics import auc

import functools    # these three used in as_keras_metric
from   keras import backend as K
import tensorflow as tf

def hard_negative_miner(wav_folder, threshold_confidence, model):

    # list all file names in folder
    wavs = glob.glob(wav_folder+'*.WAV')
    wavs = [os.path.splitext(x)[0] for x in wavs]
    wavs = [os.path.basename(x) for x in wavs]

    for wav in wavs:
        search_file_for_monkeys_ONE_NODE_OUTPUT(wav, threshold_confidence=threshold_confidence, wav_folder=wav_folder, sr=sr, model=model, hnm=True)

# ## NB.! time per file is approx. 1.553 seconds.
# ## resulting in time per folder (~4000 files) of approx. 1.75 hours

# ########################## REMOVE THIS SECTION WHEN I'VE STOPPED EXPERIMENTING WITH IT ####################################################
####### - added so functions and application of functions in same script, preventing having to import updated functions every time ##########

import os
import sys
import csv
import glob
import random
import pickle
import time

sys.path.insert(0, '/home/dgabutler/Work/CMEEProject/Code')
import wavtools   # contains custom functions e.g. denoising

praat_files = sorted(os.listdir('/home/dgabutler/Work/CMEEProject/Data/praat-files'))

# RUN 1 - no preprocessing
# compile dataset
# D_original = compile_dataset('without-preprocessing',sr=44100)

# RUN 2 - denoised only, no augmentations
# compile dataset
# D_denoised = compile_dataset('denoised',sr=44100)

# RUN 3 - denoised, Gaussian noise augmentation added
# compile dataset
# D_denoised_noise_aug = compile_dataset('denoised/noise-aug',sr=44100)

# RUN 4 - denoised, unbalanced classes (all known negatives)
# compile dataset
# D_denoised_noise_aug = compile_dataset('denoised/noise-aug',sr=44100)

# RUN 5 - denoised, random crop augmentation, unbalanced classes (all known negatives)
# compile dataset
# D_denoised_crop_aug_unbalanced = compile_dataset('denoised/crop-aug/unbalanced',sr=44100)

# training for a given run_type:
train_perc = 0.8
batch_size = 16
num_epochs = 10
sr = 44100
# dataset = D_original
name = 'D_original'
# train_simple_keras_ONE_NODE_OUTPUT(dataset,name,train_perc,num_epochs,batch_size,sr)
# train_simple_keras(dataset,name,train_perc,num_epochs,batch_size)

# BELOW WAS WORKFLOW BEFORE I CAME UP WITH COMPILE DATA FUNCTION,
# WHICH IS WHAT I USED ABOVE

# # # method 5: adding hard-negative mined training examples

# # # # hard_negative_miner('/home/dgabutler/Work/CMEEProject/Data/unclipped-whinnies/', 62, model=loaded_model)
# # # D_mined_aug_tb = D_aug_tb
# # # wavtools.add_files_to_dataset(folder='mined-false-positives', dataset=D_mined_aug_tb, example_type=0)

# # # print("\nNumber of samples when hard negatives added: " + str(wavtools.num_examples(D_mined_aug_tb,0)) + \
# # # " negative, " + str(wavtools.num_examples(D_mined_aug_tb,1)) + " positive"))

# # # D_mined_aug_tb_denoised = wavtools.denoise_dataset(D_mined_aug_tb)

# # # method 6: adding selected obvious false positives as training examples

# # D_S_mined_aug_t_denoised = D_aug_t

# # wavtools.add_files_to_dataset(folder='selected-false-positives', dataset=D_S_mined_aug_t_denoised, example_type=0)

# # print("\nNumber of samples when select negatives added: " + str(wavtools.num_examples(D_S_mined_aug_t_denoised,0)) + \
# # " negative, " + str(wavtools.num_examples(D_S_mined_aug_t_denoised,1)) + " positive"))

# # # method 7: adding 'most wrong' false positives as training examples

# # # tried ~100 negatives from Catappa, ~100 positives that I had
# # # DID NOT WORK. great results but background noise between positives and negatives was too different to generalise
# # # workflow was:
# # D_MW_mined = []
# # wavtools.add_files_to_dataset(folder='clipped-whinnies', dataset=D_MW_mined, example_type=1)
# # wavtools.add_files_to_dataset(folder='selected-false-positives/from-unclipped-whinnies', dataset=D_MW_mined, example_type=0)
# # wavtools.add_files_to_dataset(folder='selected-false-positives/catappa2-from-jenna', dataset=D_MW_mined, example_type=0)
# # D_MW_mined_denoised = wavtools.denoise_dataset(D_MW_mined)

# ### VIEWING SPECTROGRAMS
# wav = '../Data/clipped-whinnies/5A3844FE_1.WAV'
# mag_spec = wavtools.load_mag_spec(wav, sr=41000, denoise=False, normalize=False)
# # different mel.spec generating methods
# mel_spec = D_denoised[24][0]
# mel_spec = wavtools.do_augmentation(mag_spec, sr=41000, noise=False, noise_samples=False, roll=False)

def view_mag_spec(mag_spec):
    librosa.display.specshow(librosa.power_to_db(mag_spec),x_axis='time')
    plt.colorbar(format='%+2.0f dB')
    plt.title('Magnitude spectrogram')
    plt.show()

def view_mel_spec(mel_spec):
    librosa.display.specshow(librosa.power_to_db(mel_spec),y_axis='mel',x_axis='time')
    plt.colorbar(format='%+2.0f dB')
    plt.title('Mel spectrogram')
    plt.show()

def view_mag_and_mel(mag_spec, mel_spec):
    # mag
    plt.interactive(True)
    plt.subplot(1,2,1)
    librosa.display.specshow(librosa.power_to_db(mag_spec,ref=np.max),y_axis='linear',x_axis='time', sr=44100, )
    plt.colorbar(format='%+2.0f dB')
    plt.title('Magnitude spectrogram')
    # mel
    plt.subplot(1,2,2)
    mel_spec = np.flip(mel_spec,axis=0)
    plt.imshow(mel_spec)
    # plt.colorbar(format='%+2.0f dB')
    plt.title('Mel spectrogram')
    wavname = os.path.basename(os.path.splitext(wav)[0])
    plt.savefig('../Results/viewing-input-spects/'+wavname+'.png')
    plt.gcf().clear()

# view_mag_and_mel(mag_spec, mel_spec)

# can I cut the input size...
# viewing all spects for the positives we have:
# folder = 'clipped-whinnies'
# data_folder_path = '../Data/'
# files = glob.glob(data_folder_path+folder+'/*.WAV')
# for wav in files:
#     mag_spec = wavtools.load_mag_spec(wav, sr=41000, denoise=False, normalize=False)
#     mel_spec = wavtools.do_augmentation(mag_spec, sr=41000, noise=False, noise_samples=False, roll=False)
#     view_mag_and_mel(mag_spec,mel_spec)

# AVERAGE CALL LENGTH OF ALL CALLS RECORDED

call_durations = []

for wav in praat_files:
    try:
	    start_times = wavtools.whinny_starttimes_from_praatfile('../Data/praat-files/'+wav)[1]
    except FileNotFoundError:
        print("Unable to process file:", wav)
        continue
    end_times = wavtools.whinny_endtimes_from_praatfile('../Data/praat-files/'+wav)[1]

    call_durations.extend([ends-starts for starts,ends in zip(start_times, end_times)])

avg_call_len = sum(call_durations) / float(len(call_durations))
longest_call = np.max(call_durations)
shortest_call = np.min(call_durations)

#################################################################
## MISC. CODE. NOT USED AT PRESENT

def custom_recall(y_true, y_pred, threshold):
    """Recall metric, with threshold option
    """
    y_true = np.ndarray.tolist(y_true)
    y_pred = np.ndarray.tolist(y_pred)
    # flatten list of lists to list
    y_pred = [item for sublist in y_pred for item in sublist]
    # round predictions to 0 or 1
    for idx, val in enumerate(y_pred):
        y_pred[idx] = 0 if val < threshold else 1
    # true positives equals num. that sum to 2 ('1' and '1')
    summed = [x+y for x, y in zip(y_true, y_pred)]
    true_positives = sum(i == 2 for i in summed)
    # loop to find false positives
    possible_positives = sum(i == 1 for i in y_true)
    recall = true_positives/(true_positives+possible_positives)
    return recall

def custom_precision(y_true, y_pred, threshold):
    """Precision metric, with threshold option
    """
    y_true = np.ndarray.tolist(y_true)
    y_pred = np.ndarray.tolist(y_pred)
    # flatten list of lists to list
    y_pred = [item for sublist in y_pred for item in sublist]
    # round predictions to 0 or 1
    for idx, val in enumerate(y_pred):
        y_pred[idx] = 0 if val < threshold else 1
    # true positives equals num. that sum to 2 ('1' and '1')
    summed = [x+y for x, y in zip(y_true, y_pred)]
    true_positives = sum(i == 2 for i in summed)
    # loop to find false positives
    false_positives = 0
    for i in range(len(y_pred)):
        if y_pred[i] == 1 and y_true[i] == 0:
            false_positives += 1
    precision = true_positives/(true_positives+false_positives)
    return precision

def precision_threshold(threshold=0.5):
    def precision(y_true, y_pred):
        """Precision metric.
        Computes the precision over the whole batch using threshold_value.
        Taken from https://stackoverflow.com/questions/42606207
        """
        threshold_value = threshold
        # Adaptation of the "round()" used before to get the predictions. Clipping to make sure that the predicted raw values are between 0 and 1.
        y_pred = K.cast(K.greater(K.clip(y_pred, 0, 1), threshold_value), K.floatx())
        # Compute the number of true positives. Rounding in prevention to make sure we have an integer.
        true_positives = K.round(K.sum(K.clip(y_true * y_pred, 0, 1)))
        # count the predicted positives
        predicted_positives = K.sum(y_pred)
        # Get the precision ratio
        precision_ratio = true_positives / (predicted_positives + K.epsilon())
        return precision_ratio
    return precision

def recall_threshold(threshold = 0.5):
    def recall(y_true, y_pred):
        """Recall metric.
        Computes the recall over the whole batch using threshold_value.
        Taken from: https://stackoverflow.com/questions/42606207
        """
        threshold_value = threshold
        # Adaptation of the "round()" used before to get the predictions. Clipping to make sure that the predicted raw values are between 0 and 1.
        y_pred = K.cast(K.greater(K.clip(y_pred, 0, 1), threshold_value), K.floatx())
        # Compute the number of true positives. Rounding in prevention to make sure we have an integer.
        true_positives = K.round(K.sum(K.clip(y_true * y_pred, 0, 1)))
        # Compute the number of positive targets.
        possible_positives = K.sum(K.clip(y_true, 0, 1))
        recall_ratio = true_positives / (possible_positives + K.epsilon())
        return recall_ratio
    return recall

# following section taken from https://stackoverflow.com/questions/6392739
# in attempt to troubleshoot metric value problems (prev. too high)
def as_keras_metric(method):
    @functools.wraps(method)
    def wrapper(self, args, **kwargs):
        """ Wrapper for turning tensorflow metrics into keras metrics """
        value, update_op = method(self, args, **kwargs)
        K.get_session().run(tf.local_variables_initializer())
        with tf.control_dependencies([update_op]):
            value = tf.identity(value)
        return value
    return wrapper
@as_keras_metric
def auc_pr(y_true, y_pred, curve='PR'):
    return tf.metrics.auc(y_true, y_pred, curve=curve)

#################################################################
# MORE COMPLEX CONVNET WITH SPECIFIC IMPORT STATEMENTS
# from https://github.com/Azure/DataScienceVM/blob/master/Tutorials/DeepLearningForAudio/Deep%20Learning%20for%20Audio%20Part%202b%20-%20Train%20and%20Predict%20on%20UrbanSound%20dataset.ipynb

## NB! CURRENTLY TAKES MORE TIME AND IS LESS ACCURATE
## --- was abandoned early on so code may need edits to run

# # change the seed before anything else
# import numpy as np
# np.random.seed(1)
# import tensorflow as tf
# tf.set_random_seed(1)

# import os
# import time

# import keras
# keras.backend.clear_session()

# import matplotlib.pyplot as plt
# import sklearn

# from keras.models import Sequential
# from keras.layers import Activation
# from keras.layers import Convolution2D, MaxPooling2D, Dropout
# from keras.layers.pooling import GlobalAveragePooling2D
# from keras.optimizers import Adamax
# from keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau
# from keras.regularizers import l2
# from sklearn.metrics import precision_recall_fscore_support, roc_auc_score

# from keras.layers.normalization import BatchNormalization

# def train_complex_keras(dataset, train_perc):
#     """
#     Trains and more complex keras model.

#     Taken from https://github.com/Azure/DataScienceVM/blob/master/Tutorials/DeepLearningForAudio/Deep%20Learning%20for%20Audio%20Part%202b%20-%20Train%20and%20Predict%20on%20UrbanSound%20dataset.ipynb

#     """
#     try:
#         random.shuffle(dataset)
#     except NameError:
#         print('non-existent dataset name provided. check dataset exists and retry')
#         return

    # # use provided training percentage to give num. training samples
    # n_train_samples = int(round(len(dataset)*train_perc))
    # train = dataset[:n_train_samples]
    # # tests on remaining % of total
    # test = dataset[n_train_samples:]

    # X_train, y_train = zip(*train)
    # X_test, y_test = zip(*test)

    # # reshape for CNN input
    # X_train = np.array([x.reshape( (128, 279, 1) ) for x in X_train])
    # X_test = np.array([x.reshape( (128, 279, 1) ) for x in X_test])

    # # one-hot encoding for classes
    # y_train = np.array(keras.utils.to_categorical(y_train, 2))
    # y_test = np.array(keras.utils.to_categorical(y_test, 2))

    # model = Sequential()
    # input_shape=(128, 279, 1)

    # # section 1

    # model.add(Convolution2D(filters=32, kernel_size=5,
    #                         strides=2,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal",
    #                         input_shape=input_shape))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(Convolution2D(filters=32, kernel_size=3,
    #                         strides=1,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(MaxPooling2D(pool_size=(2, 2)))

    # model.add(Dropout(0.3))

    # # section 2
    # model.add(Convolution2D(filters=64, kernel_size=3,
    #                         strides=1,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(Convolution2D(filters=64, kernel_size=3,
    #                         strides=1,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(MaxPooling2D(pool_size=(2, 2)))
    # model.add(Dropout(0.3))

    # # section 3
    # model.add(Convolution2D(filters=128, kernel_size=3,
    #                         strides=1,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(Convolution2D(filters=128, kernel_size=3,
    #                         strides=1,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(Convolution2D(filters=128, kernel_size=3,
    #                         strides=1,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(Convolution2D(filters=128, kernel_size=3,
    #                         strides=1,
    #                         padding="same",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))

    # model.add(MaxPooling2D(pool_size=(2, 2)))
    # model.add(Dropout(0.3))

    # # section 4
    # model.add(Convolution2D(filters=512, kernel_size=3,
    #                         strides=1,
    #                         padding="valid",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))
    # model.add(Dropout(0.5))

    # model.add(Convolution2D(filters=512, kernel_size=1,
    #                         strides=1,
    #                         padding="valid",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))
    # model.add(Dropout(0.5))

    # # section 5
    # model.add(Convolution2D(filters=2, kernel_size=1,
    #                         strides=1,
    #                         padding="valid",
    #                         kernel_regularizer=l2(0.0001),
    #                         kernel_initializer="normal"))
    # model.add(BatchNormalization())
    # model.add(Activation('relu'))
    # model.add(GlobalAveragePooling2D())

    # model.add(Activation('softmax'))

    # model.compile(
    #     optimizer="Adam",
    #     loss="binary_crossentropy",
    #     metrics=['accuracy'])

    # model.fit(
    #     x=X_train,
    #     y=y_train,
    #     epochs=12,
    #     batch_size=128,
    #     validation_data= (X_test, y_test))

    # score = model.evaluate(
    #     x=X_test,
    #     y=y_test)

    # print('Test loss:', score[0])
    # print('Test accuracy:', score[1])

    # serialise model to JSON
    # dataset_name = name
    # save_path = '/home/dgabutler/Work/CMEEProject/Models/'+dataset_name+'/'
    # if not os.path.exists(save_path):
    #     os.makedirs(save_path)
    # model_json = model.to_json()
    # with open(save_path+'e'+str(num_epochs)+'_b'+str(batch_size)+'_model.json', 'w') as json_file:
    #     json_file.write(model_json)
    # # serialise weights to HDF5
    # model.save_weights(save_path+'e'+str(num_epochs)+'_b'+str(batch_size)+'_model.h5')
    # print('\nsaved model '+dataset_name+'/'+'e'+str(num_epochs)+'_b'+str(batch_size)+' to disk')


import math
import torch
import numpy as np

class GSP:
    def __init__(self, n_f=512, n_s=128, device='cpu'):
        self.device = torch.device(device)
        self.n_f = n_f
        self.n_s = n_s

        # 拉普拉斯矩阵
        self.init_Laplace_matrix()

        # GFT 变换矩阵
        self.init_U_matrix()

    def init_Laplace_matrix(self):
        self.L = np.zeros((self.n_f, self.n_f), np.int32)
        # L = D - A
        for i in range(self.n_f):
            for j in range(self.n_f):
                self.L[i, j] = - abs(i - j) # A_ij = |i - j|
            self.L[i, i] = np.abs(np.sum(self.L[i, :])) # 符合定义的D
            # self.L[i, i] = self.n_f - 1 # 论文中的D

    def init_U_matrix(self):
        self.U = np.zeros((self.n_f, self.n_f))
        # 特征分解
        evalues, evectors = np.linalg.eig(self.L)
        # 正交化
        for i in range(len(evalues)):
            self.U[i] = evectors[i]
            for j in range(0, i):
                self.U[i] -= np.dot(evectors[i], self.U[j]) / np.dot(self.U[j], self.U[j]) * self.U[j]
        # 归一化
        for i in range(len(evalues)):
            self.U[i] =  self.U[i] / np.sqrt(np.dot(self.U[i],  self.U[i]))

        self.U = torch.from_numpy(self.U).type(torch.FloatTensor).to(self.device)

    def gft(self, f):
        # F = U * f
        F = self.U @ f.T
        return F

    def igtf(self, F):
        # f = U_t * f
        f = self.U.T @ F.T
        return f

    def stgft(self, input_f):
        """
        Short-time Graph Fourier transform by numpy
        :return: (frames, n_f)
        """
        max_l = len(input_f)
        frames = math.ceil((max_l - self.n_f)/ self.n_s) + 1
        output_F = torch.zeros((frames, self.n_f)).to(self.device)

        # 窗厂n_f， 每次移动n_s
        for i in range(frames):

            if (i * self.n_s + self.n_f) < max_l:
                output_F[i][:] += input_f[i * self.n_s: i * self.n_s + self.n_f]
            else:
                output_F[i][: max_l - i * self.n_s] += input_f[i * self.n_s: max_l]

        # 逐帧GFT
        for i in range(frames):
            output_F[i] = self.gft(output_F[i])

        return output_F

    def istgft(self, input_F):
        """
        Inverse Short-time Graph Fourier transform by torch
        :param input_F: (frames, n_f)
        :return: n_f + (frames - 1) * n_s
        """
        # input_F: frames * self.n_f
        frames = len(input_F)
        output_f = torch.zeros(self.n_f + (frames - 1) * self.n_s).to(self.device)

        # 逐帧IGFT
        for i in range(frames):
            output_f[i * self.n_s: i * self.n_s + self.n_f] += self.igtf(input_F[i])
            # 由于是矩形窗，故重叠部分除以2
            if i != 0:
                output_f[i * self.n_s:  i * self.n_s + (self.n_f - self.n_s)] /= 2

        return output_f

    def ST_GFT(self, input_f):
        """
        :param input_f: (batch_size, n_f + (frames - 1) * n_s)
        :return: (batch_size, frames, n_f)
        """
        frames = math.ceil((input_f.shape[1] - self.n_f) / self.n_s + 1)
        output_F = torch.zeros((input_f.shape[0] , frames, self.n_f)).to(self.device)

        for i in range(input_f.shape[0]):
            output_F[i] = self.stgft(input_f[i])

        return output_F

    def iST_GFT(self, input_F):
        """
        :param input_F: (batch_size, frames, n_f)
        :return: (batch_size, n_f + (frames - 1) * n_s)
        """
        frames = input_F.shape[1]
        output_f = torch.zeros((input_F.shape[0] ,self.n_f + (frames - 1) * self.n_s)).to(self.device)

        for i in range(input_F.shape[0]):
            output_f[i] = self.istgft(input_F[i])

        return output_f

if __name__ == '__main__':
    gsp = GSP(4, 2)
    print(gsp.L)

    a = torch.Tensor([[1,2,3,4,5,6,7,8,9,10,11], [11,12,13,14,15,16,17,18,19,20,21]])
    a_F = gsp.ST_GFT(a)
    a_f = gsp.iST_GFT(a_F)
    print(a_F)
    print(a_f)

    print()


from typing import List

from docx import Document
from docx.enum.table import WD_ALIGN_VERTICAL
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.section import WD_ORIENT, WD_SECTION
from data.students_group import StudentsGroup
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.table import _Cell
from docx.shared import Inches, Cm, Pt

from data.dean import Dean
from data.enrollee import Enrollee
from data.study_direction import StudyDirection
from datetime import datetime

from data.user import User
from docx2pdf import convert as convert_docx2pdf

def create_order_of_admission(filename, enrolls: List[Enrollee], direction: StudyDirection, group_number):
    document = Document()

    upper = document.add_paragraph('Федеральное государственное бюджетное образовательное учреждение\n'
                                   'высшего образования\n'
                                   'Сызранский государственный университет имени Филиппа Лимонадова\n\n')
    upper.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

    header = document.add_paragraph()
    header.add_run('Приказ').bold = True
    header.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
    document.add_paragraph(
        'На основании решения приемной комиссии зачислить на 1 курс очной формы обучения с 01.09.2021 г.\n\n')

    paragraph = document.add_paragraph(f'Факультет “{direction.faculty.name}”\n '
                                       f'Направление “{direction.name}”\n'
                                       f'Номер группы присвоить “{group_number}”\n')
    paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

    table = document.add_table(rows=len(enrolls) + 1, cols=4, style='Table Grid')
    table.cell(0, 0).text = '№'
    table.cell(0, 1).text = 'ФИО'
    table.cell(0, 2).text = 'Основа'
    table.cell(0, 3).text = 'Баллы'

    for row_num, enroll in enumerate(enrolls, 1):
        print(f'{enroll.user.name} was written to table')
        table.cell(row_num, 0).text = str(row_num)
        table.cell(row_num, 1).text = f'{enroll.user.name} {enroll.user.surname} {enroll.user.last_name}'
        table.cell(row_num, 2).text = 'бюджет' if enroll.is_budgetary else 'платная'
        table.cell(row_num, 3).text = str(enroll.get_total_grade)

    document.add_paragraph('\n\n\nРектор\t\t\t\t\t\t\t\t\t Иванов М.Ю.')

    full_file_name = f'{filename}.docx'.replace(' ', '_')
    document.save(full_file_name)
    return full_file_name

def get_tabs(_len, data, field_name):
    data = '  ' + (data if data else ' ')
    return data + int((_len - len(field_name) - len(data)) // 10) * '\t'

def format_datetime(date: datetime):
    if date:
        return date.strftime('%d.%M.%Y')
    return ' '

def create_student_personal_profile(filename, user: User, need_pdf=False):
    document = Document()

    section = document.sections[0]
    section.orientation = WD_ORIENT.LANDSCAPE
    # crutch to rotate
    new_width, new_height = section.page_height, section.page_width
    section.page_width = new_width
    section.page_height = new_height

    upper = document.add_paragraph('Федеральное государственное бюджетное образовательное учреждение '
                                   'высшего образования\n'
                                   'Сызранский государственный университет имени Филиппа Лимонадова\n')
    upper.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
    #
    personal_debt = user.student.card_number

    delo = document.add_paragraph(f'Личное дело №')
    delo.add_run(f'{personal_debt}').underline = True
    delo.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT

    line_len = 108

    par = document.add_paragraph()
    study_direction = user.student.student_group.direction

    par.add_run('Факультет')
    facult = study_direction.faculty.name
    par.add_run(get_tabs(line_len, facult, 'Факультет') + '\n').underline = True
    par.add_run('Специальность')
    spec = study_direction.name
    par.add_run(get_tabs(line_len, spec, 'Специальность') + '\n').underline = True
    par.add_run('Группа')
    group = user.student.student_group.name
    par.add_run(get_tabs(line_len // 1.5, group, 'Группа')).underline = True

    par.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT

    header = document.add_paragraph('Учебная карточка студента\n(очная форма)')
    header.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

    tab = document.add_table(rows=1, cols=2)
    tab.autofit = False
    tab.allow_autofit = False
    # par = document.add_paragraph()
    cur_cell = tab.cell(0, 0)
    cur_cell.width = Inches(6.5)
    par = cur_cell.add_paragraph()
    par.add_run('Основа')  # TODO filing
    small_line = '\t\t\t\t\t'
    par.add_run(small_line).underline = True

    par.add_run(', личное дело №')
    par.add_run(f'{personal_debt}\n').underline = True

    par.add_run('ФИО')
    fio = f'  {user.name} {user.surname} {user.last_name}'
    par.add_run(get_tabs(line_len, fio, 'ФИО') + '\n').underline = True

    par.add_run('Дата рождения')
    par.add_run(get_tabs(line_len, format_datetime(user.enrollee.birthday), 'Дата рождения') + '\n').underline = True

    par.add_run('Место рождения')
    par.add_run(get_tabs(line_len, user.enrollee.birth_place, 'Место рождения') + '\n').underline = True
    par.add_run('Паспорт')
    passport = user.enrollee.passport
    passport_info = f' {passport.series} {passport.number}'
    par.add_run(passport_info).underline = True
    par.add_run(' выдан')

    vidan = passport.who_issued + str(passport.department_code)
    par.add_run(get_tabs(line_len, vidan, passport_info + 'паспорт  выдан') + '\n').underline = True

    small_line_len = 70
    par.add_run('\n\nКонтакты\n').bold = True
    par.add_run('Телефон')
    par.add_run(get_tabs(line_len // 1.5, user.enrollee.phone, 'Телефон') + '\n').underline = True
    par.add_run('E-mail')
    par.add_run(get_tabs(line_len // 1.5, user.email, 'e-mail') + '\n').underline = True
    par.add_run('Адрес по прописке')
    par.add_run(get_tabs(line_len, passport.registration_address, 'e-mail') + '\n').underline = True
    par.add_run(get_tabs(line_len, '', '') + '\t' + '\n').underline = True
    par.add_run(get_tabs(line_len, '', '') + '\t' + '\n').underline = True

    import os.path

    if user.enrollee.photo and os.path.isfile(user.enrollee.photo):
        img_path = user.enrollee.photo
    else:
        img_path = 'default_photo.jpg'

    with open(img_path, 'rb') as img:
        cur_cell = tab.cell(0, 1)
        paragraph = cur_cell.paragraphs[0]
        run = paragraph.add_run()
        picture = run.add_picture(img, width=Inches(1.5), height=Inches(1.5))
        cur_cell.width = Cm(10)

    input_file_path = f'{filename}.docx'
    document.save(input_file_path)

    if need_pdf:
        out_file_path = f'{filename}.pdf'
        import pythoncom
        pythoncom.CoInitializeEx(0)
        convert_docx2pdf(input_file_path, out_file_path)
        return out_file_path

    return input_file_path

def create_student_record_book(filename, users: List[User], need_pdf=False):
    document = Document()

    for ind, user in enumerate(users):
        # section = document.add_section(WD_SECTION.NEW_PAGE)
        section = document.sections[-1]
        section.top_margin = Cm(0.7)
        section.orientation = WD_ORIENT.LANDSCAPE
        # crutch to rotate
        new_width, new_height = section.page_height, section.page_width
        section.page_width = new_width
        section.page_height = new_height

        upper = document.add_paragraph('Зачетная книжка №')
        upper.add_run(f'{user.student.record_book_number}').underline = True
        upper.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

        line_len = 60

        table = document.add_table(rows=1, cols=2)
        table.autofit = False
        table.allow_autofit = False
        # par = document.add_paragraph()
        cur_cell = table.cell(0, 1)
        cur_cell.width = Inches(6.5)
        par = cur_cell.add_paragraph()
        par.add_run('ФИО')
        fio = f'  {user.name} {user.surname} {user.last_name}'
        par.add_run(get_tabs(line_len, fio, 'ФИО') + '\n').underline = True
        par.add_run('Факультет')
        directional = user.enrollee.study_direction
        par.add_run(get_tabs(line_len, directional.faculty.name, 'Факультет') + '\n').underline = True
        par.add_run('Специальность')
        par.add_run(get_tabs(line_len, directional.name, 'Специальность') + '\n').underline = True
        par.add_run(get_tabs(line_len, '', '') + '\n\n').underline = True
        par.add_run(f'Поступил в {user.student.enrollment_date.year} году')

        import os.path
        if user.enrollee.photo and os.path.isfile(user.enrollee.photo):
            img_path = user.enrollee.photo
        else:
            img_path = 'default_photo.jpg'

        with open(img_path, 'rb') as img:
            cur_cell = table.cell(0, 0)
            cur_cell.width = Cm(6.2)
            paragraph = cur_cell.paragraphs[0]
            run = paragraph.add_run()
            picture = run.add_picture(img, width=Inches(1.5), height=Inches(1.5))

        workers = Dean.query.filter_by(faculty_pk=directional.id)

        prorector = workers.filter(Dean.post == 'Проректор').first()
        dean = workers.filter(Dean.post == 'Декан').first()

        paragraph = document.add_paragraph()
        fio = ' ...'
        if prorector:
            fio = f'{prorector.user.surname} {prorector.user.name[:1]}.{prorector.user.last_name[:1]}.'

        paragraph.add_run(f'\n\nПроректор по учебной работе {fio}\n\n')

        fio = ' ...'
        if prorector:
            fio = f'{dean.user.surname} {dean.user.name[:1]}.{dean.user.last_name[:1]}.'
        paragraph.add_run(f'Проректор по учебной работе {fio}\n')

        if ind % 2 == 0:
            paragraph.add_run('-' * 160 + '\n')

    input_file_path = f'{filename}.docx'
    document.save(input_file_path)

    if need_pdf:
        out_file_path = f'{filename}.pdf'
        import pythoncom
        pythoncom.CoInitializeEx(0)
        convert_docx2pdf(input_file_path, out_file_path)
        return out_file_path

    return input_file_path

def create_student_card(filename, users: List[User], need_pdf=False):
    document = Document()

    for (ind, user) in enumerate(users):
        section = document.sections[-1]
        section.top_margin = Cm(0.7)
        section.orientation = WD_ORIENT.LANDSCAPE
        # crutch to rotate
        new_width, new_height = section.page_height, section.page_width
        section.page_width = new_width
        section.page_height = new_height

        table = document.add_table(rows=1, cols=2, style='Table Grid')
        table.autofit = False
        table.allow_autofit = False

        # 1 col
        current_cell = table.cell(0, 0)
        current_cell.width = Cm(11)
        upper = current_cell.add_paragraph('Студенческий билет №')
        upper.add_run(f'{user.student.card_number}').underline = True
        upper.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT

        min_tabble = current_cell.add_table(rows=1, cols=2)
        # min_tabble.autofit = True
        # min_tabble.allow_autofit = True

        # photo
        min_tabble.cell(0, 0).width = Cm(4)
        paragraph = min_tabble.cell(0, 0).paragraphs[0]
        run = paragraph.add_run('\n')
        import os.path
        if user.enrollee.photo and os.path.isfile(user.enrollee.photo):
            img_path = user.enrollee.photo
        else:
            img_path = 'default_photo.jpg'

        with open(img_path, 'rb') as img:
            picture = run.add_picture(img, width=Inches(1.5), height=Inches(1.5))

        paragraph.add_run('\n\nДекан факультета')

        min_tabble.cell(0, 1).width = Cm(5.5)
        paragraph = min_tabble.cell(0, 1).add_paragraph()
        line_len = 35
        paragraph.add_run('Фамилия')
        paragraph.add_run(get_tabs(line_len, user.surname, 'Фамилия') + '\n').underline = True
        paragraph.add_run('Имя')
        paragraph.add_run(get_tabs(line_len, user.name, 'Имя') + '\n').underline = True
        paragraph.add_run('Отчество')
        paragraph.add_run(get_tabs(line_len, user.last_name, 'Отчество  ') + '\n').underline = True
        faculty = user.enrollee.study_direction.faculty.name
        paragraph.add_run('Факультет')
        paragraph.add_run(get_tabs(line_len, faculty, 'Факультет') + '\n').underline = True
        paragraph.add_run('Группа')
        paragraph.add_run(get_tabs(line_len, user.student.student_group.name, 'Группа') + '\n\n').underline = True
        paragraph.add_run('Проректор по учебной работе' + '\n\n')

        # 2 col
        current_cell = table.cell(0, 1)
        start_year = user.student.enrollment_date.year
        digids = start_year % 100
        paragraph = current_cell.add_paragraph('\n')

        font_size = 15
        for i in range(5):
            run = paragraph.add_run(f'В 20')
            run.font.size = Pt(font_size)
            run = paragraph.add_run(f'{digids + i}')
            run.underline = True
            run.font.size = Pt(font_size)

            paragraph.add_run('/')
            run = paragraph.add_run(f'{digids + i + 1} ')
            run.underline = True
            run.font.size = Pt(font_size)
            run = paragraph.add_run('г. является студентом ')
            run.font.size = Pt(font_size)
            run = paragraph.add_run(f'{i + 1}')
            run.underline = True
            run.font.size = Pt(font_size)
            run = paragraph.add_run(' курса\n\n')
            run.font.size = Pt(font_size)

    input_file_path = f'{filename}.docx'
    document.save(input_file_path)
    import os
    print('path', os.path.abspath(os.getcwd()))

    if need_pdf:
        out_file_path = f'{filename}.pdf'
        import pythoncom
        pythoncom.CoInitializeEx(0)
        convert_docx2pdf(input_file_path, out_file_path)
        return out_file_path

    return input_file_path

def create_instruct_table(filename, users: List[User], subject_name, need_pdf=False):
    document = Document()

    header = document.add_paragraph()
    header.add_run(f'Инструктаж по технике безопасности по предмету').bold = True
    header.add_run(f'  {subject_name} ').underline = True
    header.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

    table = document.add_table(cols=4, rows=len(users) + 2, style='Table Grid')

    table.rows[0].cells[0].merge(table.cell(1, 0))
    table.cell(0, 0).add_paragraph('№\nп/п')
    # table.cell(1, 0).width = Cm(1.2)
    # table.cell(2, 0).width = Cm(1.2)
    table.rows[0].cells[1].merge(table.cell(1, 1))
    p = table.cell(0, 1).add_paragraph('ФИО инструктируемого')
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
    table.cell(0, 1).vertical_alignment = WD_ALIGN_VERTICAL.CENTER

    table.rows[0].cells[2].merge(table.cell(0, 3))
    table.cell(0, 2).add_paragraph('Группа')
    table.cell(1, 2).add_paragraph('Дата инструктажа')
    table.cell(1, 3).add_paragraph('Подпись')

    table.autofit = False
    table.allow_autofit = False
    norm_date = lambda x: x.strftime('%d.%m.%Y')

    for ind, user in enumerate(users, 2):
        numb_cell = table.cell(ind, 0)
        numb_cell.width = Cm(1.2)
        p = numb_cell.add_paragraph(str(ind - 1))
        p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
        numb_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER

        fio_cell = table.cell(ind, 1)
        fio_cell.width = Inches(2.6)
        p = fio_cell.add_paragraph(f'{user.name} {user.surname} {user.last_name}')
        p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
        fio_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER

        date_cell = table.cell(ind, 2)
        date_cell.width = Inches(1.3)

        if user.student:
            p = date_cell.add_paragraph(f'{norm_date(user.student.enrollment_date)}')
            p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
            date_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER

        podp_cell = table.cell(ind, 3)
        podp_cell.width = Inches(1.3)

    par = document.add_paragraph("\n\nИнструктаж провел")
    par.add_run('\t\t\t\t\t\t').underline = True
    par.add_run('\t').underline = False
    par.add_run('\t\t\t').underline = True
    par = document.add_paragraph()
    par.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    run = par.add_run('Дата, подпись')
    font = run.font
    font.size = Pt(8)

    input_file_path = f'{filename}.docx'
    document.save(input_file_path)

    if need_pdf:
        out_file_path = f'{filename}.pdf'
        import pythoncom
        pythoncom.CoInitializeEx(0)
        convert_docx2pdf(input_file_path, out_file_path)
        return out_file_path

    return input_file_path

def create_attendance_log(file_name, users: List[User], group: StudentsGroup):
    document = Document()

    section = document.sections[0]
    section.orientation = WD_ORIENT.LANDSCAPE
    # crutch to rotate
    new_width, new_height = section.page_height, section.page_width
    section.page_width = new_width
    section.page_height = new_height

    header = document.add_paragraph()
    header.add_run(f'Журнал посещаемости группы').bold = True
    header.add_run(f'  {group.name} ').underline = True
    header.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

    table = document.add_table(cols=17, rows=len(users) + 2, style='Table Grid')
    table.autofit = False
    table.allow_autofit = False

    table.rows[0].cells[0].merge(table.cell(1, 0))
    table.cell(0, 0).add_paragraph('№\nп/п')
    table.cell(1, 0).width = Cm(1.2)

    table.rows[0].cells[1].merge(table.cell(1, 1))

    p = table.cell(0, 1).add_paragraph('Месяц, число\n\n'
                                       '\\ \n\n'
                                       'ФИО обучающегося \n\n')
    table.cell(0, 1).width = Cm(5)
    table.cell(0, 2).width = Cm(5)
    p.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
    table.cell(0, 1).vertical_alignment = WD_ALIGN_VERTICAL.CENTER
    section = document.sections[-1]
    section.top_margin = Cm(0.7)
    section.left_margin = Cm(0.7)
    section.right_margin = Cm(1.2)

    table.rows[0].cells[2].merge(table.cell(0, 16))
    p = table.cell(0, 3).add_paragraph('Предмет')
    run = p.add_run(group.direction.name)
    run.underline = True

    table.cell(0, 3).height = Cm(15)

    for i in range(2, 17):  # rows
        table.cell(1, i).add_paragraph(f'.     {i - 1}.09\n')
        tc = table.cell(1, i)._tc
        tcPr = tc.get_or_add_tcPr()
        textDirection = OxmlElement('w:textDirection')
        textDirection.set(qn('w:val'), "btLr")  # btLr tbRl
        tcPr.append(textDirection)

    for ind, user in enumerate(users, 2):
        table.cell(ind, 0).add_paragraph(str(ind - 1))
        table.cell(ind, 1).add_paragraph(f'{user.name} {user.surname} {user.last_name}')

    input_file_path = f'{file_name}.docx'
    document.save(input_file_path)

    return input_file_path


#!/usr/bin/env python

import re
import string
import pandas as pd

train_path = '../../../data/train.csv'
train_out_path = '../../../data/processed/train.csv'
test_path = '../../../data/test.csv'
test_out_path = '../../../data/processed/test.csv'
emo_pos = [':-)', ':)', '(:', '(-:', '<3', ':-D', ': D', ' XD', ';-)',
           ';)', ';-D', '; D']
emo_neg = [':-(', ':(', ': (', ':((', 'T_T', ' t_t', ' ><']

def process(line):
    for e in emo_pos:
        line = line.replace(e, ' myemopos ')
    for e in emo_neg:
        line = line.replace(e, ' myemoneg ')
    line = line.strip()
    line = line.lower()
    line = line.replace("'", "")
    line = line.replace("?", " myquestionmark ")
    line = line.replace("!", " myexclamationmark ")
    line = re.sub('@([a-zA-Z0-9]|[_])*', ' ', line)
    line = re.sub('\\s?(f|ht)(tp)(s?)(://)([^\\.]*)[\\.|/](\\S*)', ' ', line)
    line = re.sub('[%s]' % re.escape(string.punctuation), ' ', line)
    line = re.sub('[0-9]+', ' ', line)
    line = re.sub('([a-z])\\1+', '\\1\\1', line)
    line = line.split()
    return ' '.join(line)

def run(inpath, outpath):
    data = pd.read_csv(inpath, sep=',')
    data['tweet'] = data['tweet'].apply(process)
    data.to_csv(outpath, index=False)

if __name__ == '__main__':
    #run(train_path, train_out_path)
    run(test_path, test_out_path)


# mca_mesh_selection_mode_cycle.py.

# Mesh Selection Mode Cycle.

# Version 1.1.

bl_info = {
    "name": "Mesh Selection Mode Cycle",
    "description": "Cycle through mesh selection modes. Works when a single mode is selected.",
    "category": "Mesh",
    "blender": (3, 1, 0),
    "author": "McArcher",
}

import bpy

class MSM:
    # Number of possible mesh selection modes. In Blender 3.1 there are three
    # modes: Vertex, Edge, Face.
    Count = 3

    DirectionPrevious = 'prev'
    DirectionNext = 'next'

    ResultCancel = 'CANCELLED'
    ResultSuccess = 'FINISHED'

    MeshEditMode = 'EDIT_MESH'

    # Arguments for the 'bpy.ops.mesh.select_mode' method.
    SelectModeTypes = {
        1: 'VERT',
        2: 'EDGE',
        3: 'FACE'
    }

class McaMeshSelectionModePrevious(bpy.types.Operator):
    """Mesh Selection Mode Cycle: Previous"""
    bl_idname = "object.mca_mesh_selection_mode_previous"
    bl_label = "Mesh Selection Mode Cycle: Previous"

    @classmethod
    def poll(cls, context):
        return is_mesh_edit_mode_selected()

    def execute(self, context):
        return execute_generic(MSM.DirectionPrevious)

class McaMeshSelectionModeNext(bpy.types.Operator):
    """Mesh Selection Mode Cycle: Next"""
    bl_idname = "object.mca_mesh_selection_mode_next"
    bl_label = "Mesh Selection Mode Cycle: Next"

    @classmethod
    def poll(cls, context):
        return is_mesh_edit_mode_selected()

    def execute(self, context):
        return execute_generic(MSM.DirectionNext)

def register():
    bpy.utils.register_class(McaMeshSelectionModePrevious)
    bpy.utils.register_class(McaMeshSelectionModeNext)

def unregister():
    bpy.utils.unregister_class(McaMeshSelectionModeNext)
    bpy.utils.unregister_class(McaMeshSelectionModePrevious)

# Counts the enabled mesh selection modes. Returns -1 on error. Argument (msm)
# is an array. Documentation:
# https://docs.blender.org/api/current/bpy.types.ToolSettings.html#bpy.types.ToolSettings.mesh_select_mode
def count_enebled_msm(msm):
    # Is everything fine ?
    if len(msm) != MSM.Count:
        return -1

    enabled_msm_count = 0
    for idx in range(MSM.Count):
        if msm[idx] == True:
            enabled_msm_count += 1

    return enabled_msm_count

# Gets the first enabled mode (index).
# Returns -1 on error.
def get_first_selected_msm(msm):
    for idx in range(MSM.Count):
        if msm[idx] == True:
            return idx

    return -1

# Returns the previous mesh selection mode (array index).
def get_previous_msm(cur_mode):
    if cur_mode <= 0:
        return MSM.Count - 1

    return cur_mode - 1

# Returns the next mesh selection mode (array index).
def get_next_msm(cur_mode):
    if cur_mode >= MSM.Count - 1:
        return 0

    return cur_mode + 1

# Selects (applies) the mesh selection mode. This method uses a low level API,
# which is not synchronized with the selection.
def select_msm_ll(msm_idx):
    msm = []
    for i in range(MSM.Count):
        msm.append(False)

    msm[msm_idx] = True

    bpy.context.tool_settings.mesh_select_mode = msm

# Selects (applies) the mesh selection mode. This method uses a high level API,
# which is synchronized with the selection.
def select_msm(msm_idx):
    typeText = MSM.SelectModeTypes[msm_idx + 1]
    bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type=typeText)

# Checks whether the mesh edit mode is selected.
def is_mesh_edit_mode_selected():
    return bpy.context.mode == MSM.MeshEditMode

# A generic method to execute various actions. Cycles through mesh selection
# modes. Changes the selected mode to a next or a previous mode if a single
# mode is selected. Does nothing if more than one mode is selected. Selects the
# first mode if no mode is selected.
def execute_generic(direction):
    if (direction != MSM.DirectionPrevious) and \
            (direction != MSM.DirectionNext):
        return {MSM.ResultCancel}

    msm = bpy.context.tool_settings.mesh_select_mode
    selections_count = count_enebled_msm(msm)

    # Exit on error.
    if selections_count < 0:
        return {MSM.ResultCancel}

    # Exit if more than one mode is selected.
    if selections_count > 1:
        return {MSM.ResultCancel}

    # Select the first mode if no mode is selected.
    if selections_count == 0:
        select_msm(0)
        return {MSM.ResultSuccess}

    # Select the next or previous mode if a single mode is selected.
    selected_idx = get_first_selected_msm(msm)

    new_idx = -1
    if direction == MSM.DirectionPrevious:
        new_idx = get_previous_msm(selected_idx)
    if direction == MSM.DirectionNext:
        new_idx = get_next_msm(selected_idx)

    select_msm(new_idx)
    return {MSM.ResultSuccess}

Mesh Selection Mode Cycle/mca_mesh_selection_mode_cycle.py


"""
The main class of the timer that defines the display, objects, methods, etc.

"""

import pygame
import time
import math
import random
from datetime import datetime, timedelta

import colors
import config as c

from timer import MyTimer
from clock_display import ClockDisplay
from canvas import Canvas
from text_object import TextObject
from button import Button

class SimpleTimer(MyTimer):
    def __init__ (self):
        MyTimer.__init__(self, 'Timer', c.w_x_size, c.w_y_size, c.frame_rate)

        self.start_time = None
        self.pause_time = None
        self.time_over = False
        self.time_delta_to_stop = None
        self.current_delta = None

        self.menu_buttons = []
        self.start_timer = False
        self.is_timer_running = False
        self.pause_timer = False

        self.start_menu_objects = []

    def show_message(self, text, color=colors.GRAY24, font_name='Century', font_size=20, centralized=False):
        message = TextObject(c.w_x_size // 2, c.w_y_size // 2, lambda: text, color, font_name, font_size, True)
        self.draw()
        message.draw(self.surface)
        pygame.display.update()
        time.sleep(c.message_duration)

    # Create canvas
    def create_canvas(self):
        canvas = Canvas(c.margin, c.margin, c.can_x_size, c.can_y_size, c.can_color[self.style_mode])
        self.objects.append(canvas)

    # Create clock display
    def create_clock_display(self):
        clock_display = ClockDisplay(c.clock_x, c.clock_y, c.clock_x_size, c.clock_y_size, c.clock_color[self.style_mode])
        #text =  time.strftime("%H : %M",
        clock_text = TextObject(clock_display.centerx, clock_display.centery, self.text_for_time, c.clock_text_color[self.style_mode], c.clock_font_name, c.clock_font_size, True)
        self.objects.append(clock_display)
        self.objects.append(clock_text)

    def create_mode_display(self):
        mode_display = ClockDisplay(c.mode_x, c.mode_y, c.mode_x_size, c.mode_y_size, c.mode_color[self.style_mode])
        mode_text = TextObject(mode_display.centerx, mode_display.centery, self.text_for_mode, c.mode_text_color[self.style_mode], c.mode_font_name, c.mode_font_size, True)
        self.objects.append(mode_display)
        self.objects.append(mode_text)

    def create_remain_repeate_display(self):
        rr_display = ClockDisplay(c.rr_x, c.rr_y, c.rr_x_size, c.rr_y_size, c.rr_color[self.style_mode])
        rr_text = TextObject(rr_display.centerx, rr_display.centery, self.text_for_rr, c.rr_text_color[self.style_mode], c.rr_font_name, c.rr_font_size, True)
        self.objects.append(rr_display)
        self.objects.append(rr_text)

    def create_start_menu_displays(self):
        display = ClockDisplay(c.start_menu_display_x, c.start_menu_down_display_y, c.start_menu_display_x_size, c.start_menu_display_y_size, c.start_menu_display_color)
        display_text = TextObject(display.centerx, display.centery, lambda: 'Set color mode...', c.start_menu_display_text_color, c.start_menu_display_font_name, c.start_menu_display_font_size, True)
        self.objects.append(display)
        self.objects.append(display_text)
        self.start_menu_objects.append(display)
        self.start_menu_objects.append(display_text)

        display = ClockDisplay(c.start_menu_display_x, c.start_menu_up_display_y, c.start_menu_display_x_size, c.start_menu_display_y_size, c.start_menu_display_color)
        display_text = TextObject(display.centerx, display.centery, lambda: 'Set cycles number...', c.start_menu_display_text_color, c.start_menu_display_font_name, c.start_menu_display_font_size, True)
        self.objects.append(display)
        self.objects.append(display_text)
        self.start_menu_objects.append(display)
        self.start_menu_objects.append(display_text)

    def text_for_rr(self):
        return '{}'.format(self.timer_repeats)

    def text_for_mode(self):
        if self.start_time is None:
            return 'Press Start...'
        else:
            if self.do_time:
                return 'Work time...'
            elif self.rest_time:
                return 'Rest time...'
            else:
                return ''

    def text_for_time(self):
        if self.start_time is None:
            return '{:02d} : 00'.format(self.set_time_in_minutes)
        else:
            if not self.pause_timer:
                self.current_delta = self.time_delta_to_stop.seconds - (datetime.now() - self.start_time).seconds
            return '{:02d} : {:02d}'.format(self.current_delta//60, self.current_delta % 60)

    # Create timer menu
    def create_menu(self):
        def on_start_timer(button):
            self.start_timer = True
            self.is_timer_running = True

        def on_pause_timer(button):
            self.pause_timer = True
            self.is_timer_running = False
            # Set time when pause
            if self.start_time is not None:
                self.pause_time = datetime.now()
            # To catch error when press Pause before start
            if self.start_time is None and not self.start_timer:
                self.pause_timer = False

        def on_end_timer(button):
            self.start_timer = False
            self.is_timer_running = False
            self.pause_timer = False
            self.start_time = None
            self.current_timer_over = True

        actions = {'Start': on_start_timer, 'Pause': on_pause_timer, 'End': on_end_timer}
        for key, value in actions.items():
            but = Button(c.button_params[key][0], c.button_params[key][1], c.button_x_size, c.button_y_size, c.button_color[self.style_mode], str(key), c.button_text_color[self.style_mode], value)
            self.objects.append(but)
            self.menu_buttons.append(but)
            self.mouse_handlers.append(but.handle_mouse_event)

    def new_circle_prep(self):

        self.start_timer = False
        self.pause_timer = False
        self.start_time = None
        self.pause_time = None
        self.time_delta_to_stop = None
        self.current_delta = None
        self.time_over = False

        self.current_timer_over = False

        self.create_objects()

    # Create start menu
    def create_start_menu(self):

        self.is_timer_running = False

        def on_close_start_menu(button):
            self.remove_start_menu()

        def on_light_mode(button):
            self.style_mode = 'light'

        def on_dark_mode(button):
            self.style_mode = 'dark'

        def on_violet_mode(button):
            self.style_mode = 'violet'

        def on_key(button, key):
            self.timer_repeats = key

        # Create start menu canvas
        canvas = Canvas(c.margin, c.margin, c.can_x_size, c.can_y_size, c.start_can_color)
        self.start_menu_objects.append(canvas)
        self.objects.append(canvas)

        self.create_start_menu_displays()

        actions = {'Ok': on_close_start_menu, 'Light': on_light_mode, 'Dark': on_dark_mode, 'Violet': on_violet_mode}
        for key, value in actions.items():
            but = Button(c.start_menu_button_params[key][0], c.start_menu_button_params[key][1], c.start_menu_button_x_size, c.start_menu_button_y_size, c.start_menu_button_color, str(key), c.start_menu_button_text_color, value)
            self.objects.append(but)
            self.start_menu_objects.append(but)
            self.mouse_handlers.append(but.handle_mouse_event)

        for key in [1, 2, 3, 4]:
            but = Button(c.start_menu_n_button_params[key][0], c.start_menu_n_button_params[key][1], c.start_menu_n_button_x_size, c.start_menu_n_button_y_size, c.start_menu_button_color, str(key), c.start_menu_button_text_color, on_key, key)
            self.objects.append(but)
            self.start_menu_objects.append(but)
            self.mouse_handlers.append(but.handle_mouse_event)

    def remove_start_menu(self):
        self.objects = []
        self.new_circle_prep()
        self.create_objects()

    def create_objects(self):
        self.create_canvas()
        self.create_clock_display()
        self.create_menu()
        self.create_mode_display()
        self.create_remain_repeate_display()

    def update(self):

        if not self.is_timer_running:
            return

        if self.start_timer:
            self.start_timer = False
            self.time_over = False
            # Set time when timer begins
            if self.start_time is None:
                self.start_time = datetime.now()
                self.time_delta_to_stop = timedelta(seconds = self.set_time_in_seconds)
            # Handle the end of a pause
            if self.pause_timer == True:
                self.pause_timer = False
                if self.pause_time is not None:
                    self.time_delta_to_stop += datetime.now() - self.pause_time

        if not self.time_over:
            if datetime.now() - self.start_time >= self.time_delta_to_stop:
                self.time_over = True

        super().update()

        if self.time_over:
            self.start_time = None
            self.start_timer = True
            self.current_timer_over = True

    # Main
    def start_timer_run(self):
        while not self.timer_over:
            self.create_start_menu()
            self.run_multiple_intervals()

def main():
    SimpleTimer().start_timer_run()

if __name__ == '__main__':
    main()


import queue
#use Breadth First Search to find the shortest path for unweighted graph

# Basic idea: use queue to store each node, pop one element each time, and detect if connected nodes are visited already or not
# If not visit, push element, assign the distance +1
# Do it until the queue is empty

#Run time analysis: Each node is only added to the queue once, the edges of each node will be detected twice (because this is undirected graph.
#The time complexity is O(|E|+|V|)

def BFS(adj_list,start,end):
    n=len(adj_list)
    distance=[float('inf') for _ in range(n)]
    distance[start]=0
    Q=queue.Queue()
    Q.put(start)
    while not Q.empty():
        p=Q.get()
        for i in adj_list[p]:
            if distance[i]==float('inf'):
                distance[i]=distance[p]+1
                Q.put(i)

    if distance[end]==float('inf'):
        return -1
    else:
        return distance[end]

if __name__=="__main__":
    vertice,edge=list(map(int,input().split()))
    adj_list=[[] for _ in range(vertice)]
    for i in range(edge):
        EDGE=list(map(int,input().split()))
        adj_list[EDGE[0]-1].append(EDGE[1]-1)
        adj_list[EDGE[1]-1].append(EDGE[0]-1)
    start,end=list(map(int,input().split()))
    start,end=start-1,end-1
    print(BFS(adj_list,start,end))