import os, sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
import pathlib
import models.simple_model
import models.alexnet
from data_loader.loader import get_dataloaders
from utility.util import DataSetName
from utility.learning_utils import *
from byol_pytorch import BYOL
import wandb
import torchvision.models as predefined_models

def freeze_convs(soft_model, sig_model):
    freeze(soft_model)
    freeze(sig_model)
    return soft_model, sig_model

def freeze(model):
    for module in model.layer1:
        freeze_module(module)
    for module in model.layer2:
        freeze_module(module)
    for module in model.layer3:
        freeze_module(module)
    for module in model.layer4:
        freeze_module(module)

def freeze_module(module):
    for param in module.parameters():
        param.requires_grad = False

def get_summary(dist):
    summary = []
    for key, val in dist.items():
        avg = np.mean(np.array(val), axis=0)
        summary.append((key, avg))
    return summary

def get_models(dataset_name):
    model = None
    if dataset_name.name == DataSetName.CIFAR100.name:
        model = models.simple_model.ConvModel(100, in_chanel=3)
    elif dataset_name.name == DataSetName.CIFAR10.name:
        # model = models.simple_model.ConvModel(10, in_chanel=3)
        # model = models.alexnet.AlexNet()
        model = predefined_models.vgg16(pretrained=True, num_classes=10)
    elif dataset_name.name == DataSetName.MNIST.name or dataset_name.name == DataSetName.FashionMNIST.name:
        model = models.simple_model.ConvModel(10, in_chanel=1)

    model.name = 'soft_model'
    return model

def perform_cil_tasks(tasks, model, dataset_name, epochs=2, lr=0.0001, report_step=20, approach='dll'):
    pre_testset, pre_valset = None, None
    is_first_task = True
    test_stat, validation_stat, running_losses = list(), list(), list()
    classes_avg = dict()
    for task in tasks:
        print(f"{Color.RED.value}task: {task}{Color.END.value}")

        device = 'cuda' if torch.cuda.is_available() else 'cpu'
        if device == 'cuda':
            num_workers = 2
        else:
            num_workers = 0

        trainloader, validationloader, testloader, pre_testset, pre_valset = get_dataloaders(dataset_name=dataset_name,
                                                                                pre_testset=pre_testset,
                                                                                pre_valset=pre_valset,
                                                                                task_lbls=task, num_workers=num_workers)

        if type(model) == type(None):
            model = get_models(dataset_name=dataset_name)

        if approach == 'dll':
            test_stat, validation_stat, running_losses, model = train(model, trainloader, validationloader,
                                                                      testloader,task, test_stat, validation_stat,
                                                                      running_losses, epochs=epochs, lr=lr,
                                                                      report_step=report_step)

        if approach == 'dll':
            preds, y_true, (acc, f1, p) = predict(model, testloader, task)
            print(f'acc:{acc:.4f}, f1:{f1:.4f}, p:{p:.4f}')
            # plot_confusion_matrix(y_true, preds, classes=task, task=task)

            # plt.plot(range(1, len(running_losses) + 1), running_losses, alpha=.6)
            # plt.xlabel("steps")
            # plt.ylabel("loss")
            # plt.title("Running Loss")
            # plt.show()

    plt.plot(range(1, len(validation_stat) + 1), validation_stat, alpha=.6)
    plt.xlabel("steps")
    plt.ylabel("accuracy")
    plt.title("Validation Accuracy")
    plt.show()

    plt.plot(range(1, len(running_losses) + 1), running_losses, alpha=.6)
    plt.xlabel("epochs")
    plt.ylabel("accuracy")
    plt.title("Test Accuracy")
    plt.show()

    if approach == 'dll':
        running_losses = np.array(running_losses)
    validation_stat = np.array(validation_stat)
    test_stat = np.array(test_stat)

    path = os.path.join(f'../results/batch_cil/{dataset_name}')
    pathlib.Path(path).mkdir(parents=True, exist_ok=True)
    if approach == 'dll':
        running_losses_path = os.path.join(path, 'running_losses')
        np.save(running_losses_path, running_losses)

    validation_stat_path = os.path.join(path, 'validation_stat')
    test_stat_path = os.path.join(path, 'test_stat')
    np.save(validation_stat_path, validation_stat)
    np.save(test_stat_path, test_stat)

def perform_ssl_cil_tasks(tasks, model, dataset_name, epochs=2, lr=0.0001, report_step=20, ssl_dict = {}, ratio = 1.0, batch_size = 16):
    pre_testset, pre_valset = None, None
    is_first_task = True
    test_stat, validation_stat, running_losses = list(), list(), list()
    classes_avg = dict()

    task_validation_loaders = []
    task_test_loaders = []
    tasks_classes_seen = []
    task_cum_accs = []

    for i, task in enumerate(tasks):
        print(f"{Color.RED.value}task: {task}{Color.END.value}")

        tasks_classes_seen.append(task)

        device = 'cuda' if torch.cuda.is_available() else 'cpu'
        if device == 'cuda':
            num_workers = 0
        else:
            num_workers = 0

        trainloader, validationloader, testloader, _, _ = get_dataloaders(dataset_name=dataset_name,
                                                                                pre_testset=None,
                                                                                pre_valset=None,
                                                                                task_lbls=task, num_workers=num_workers, train_batch_size=batch_size)
        task_validation_loaders.append(validationloader)
        task_test_loaders.append(validationloader)

        test_stat, validation_stat, running_losses, model = train_ssl(model, trainloader, validationloader,
                                                                      testloader,(i,task), test_stat, validation_stat,
                                                                      running_losses, epochs=epochs, lr=lr,
                                                                      report_step=report_step, ssl_dict = ssl_dict, ratio = ratio)

        accs = []
        for task_id, task_loader in enumerate(task_validation_loaders):

            preds, y_true, (acc, f1, p) = predict(model, task_loader, (task_id,tasks_classes_seen[task_id]))
            print('Task {d} val acc : {acc}'.format(d=task_id+1, acc = acc))
            accs.append(acc)
            # plot_confusion_matrix(y_true, preds, classes=task, task=task)

        # plt.plot(range(1, len(running_losses) + 1), running_losses, alpha=.6)
        # plt.xlabel("steps")
        # plt.ylabel("loss")
        # plt.title("Running Loss")
        # plt.show()

        cum_acc = np.array(accs).mean()

        #wandb logging
        wandb.log(
            {
                "Cumulative accuracy": cum_acc,
                "Task ID": task_id,
            }
                )

        task_cum_accs.append(cum_acc)

        print('Cumulative accuracy: {cum_acc}'.format(cum_acc=cum_acc))

    plt.plot(range(len(task_cum_accs)), task_cum_accs)
    plt.xlabel("steps")
    plt.ylabel("accuracy")
    plt.title("Validation Accuracy")
    plt.show()

    running_losses = np.array(running_losses)
    validation_stat = np.array(validation_stat)
    test_stat = np.array(test_stat)

    path = os.path.join(f'../results/batch_cil/{dataset_name}')
    pathlib.Path(path).mkdir(parents=True, exist_ok=True)

    running_losses_path = os.path.join(path, 'running_losses')
    np.save(running_losses_path, running_losses)

    validation_stat_path = os.path.join(path, 'validation_stat')
    test_stat_path = os.path.join(path, 'test_stat')
    np.save(validation_stat_path, validation_stat)
    np.save(test_stat_path, test_stat)


from tkinter import E
import numpy as np
import torch
import pickle
import torch.nn as nn
from model_paras_count.pre_process import *
from model_paras_count.count import *
from network.plain_network import *
from network.batchcrypt import BatchCrypt
from socket import socket, AF_INET, SOCK_STREAM
from train_params import *
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import math
import time
import logging

def server_func():
    logging.basicConfig(level=LOG_LEVEL,
                        format='%(asctime)s %(filename)s %(levelname)s %(message)s',
                        datefmt='%a %d %b %Y %H:%M:%S',
                        filename="{}/server.log".format(LOG_DIR),
                        filemode="w")
    logging.debug("Debug mode")

    """ Init public key """
    model = PlainNetwork(encry_method=BACKEND)
    encryptor = model.encryptor

    """ Init socket """

    ServerSocket = socket()
    ServerSocket.bind((SERVER_IP, SERVER_PORT))
    ServerSocket.listen(5)

    leader_conn, address = ServerSocket.accept()

    """ Send public key """
    sendData = pickle.dumps(encryptor)
    connections = {}
    for _ in range(CLIENT_NUM):
        conn, address = ServerSocket.accept()
        conn_id = pickle.loads(conn.recv(BUFF_SIZE))
        connections[conn_id] = conn
        conn.send(sendData)

    """** Local Anchors **"""

    logging.debug("init success")
    logging.info("Params: MODE:{} CLIENTS:{} K:{} SPARSE:{} DATASET:{}".\
                 format(SWITCH_MODE,CLIENT_NUM,K,SPARSE,DATASET))
    protocol_switch = '1'
    switch_back = False
    batch_num = math.ceil(TRAINDATA_SIZE // CLIENT_NUM / BATCH_SIZE) if CLIENT_NUM < 50 else math.ceil(TRAINDATA_SIZE // 25 / BATCH_SIZE)
    model_chose = np.zeros((EPOCH,batch_num,CLIENT_NUM)) # save distribution of selected clients
    for epoch in range(EPOCH):
        for b in range(batch_num):
            logging.debug("epoch: {} batch seq: {}".format(epoch,b))
            print("epoch:",epoch,"step:",b)

            '''recv switch from leader and broadcast'''
            # recv switch info from leader
            protocol_switch = pickle.loads(leader_conn.recv(BUFF_SIZE))

            #leader_conn.send(pickle.dumps(COMP_MSG))
            logging.debug("receive switch from leader")
            time.sleep(0.5)

            # broadcast switch info to clients
            for i in range(1,CLIENT_NUM):
                conn = connections[i]
                conn.send(pickle.dumps(protocol_switch))

            logging.debug("broadcast switch")
            #time.sleep(0.8)

            '''trans process'''
            if PARASCOUNT and protocol_switch == '2':
                # recieve hash of anchors
                hash_list = None
                for i in range(CLIENT_NUM):
                    conn = connections[i]
                    remain = pickle.loads(conn.recv(BUFF_SIZE))
                    conn.send(pickle.dumps(READY_MSG))
                    recvData = b""
                    while remain > 0:
                        each_data = conn.recv(BUFF_SIZE)
                        remain -= len(each_data)
                        recvData += each_data
                    hashs = pickle.loads(recvData)
                    if i == 0:
                        hash_list = hashs
                    else:
                        hash_list = np.vstack((hash_list,hashs))

                logging.debug("receive clients' hash list")

                # Model Paras Count
                hash_list = hash_list.T
                alloc,selected,sparse_index = pick_represents(hash_list,K,SPARSE,ENCRYPT_BATCHSIZE)
                alloc_num = len(alloc.keys())
                selected_index = []
                selected_info = dict()
                selected_freq = np.zeros(CLIENT_NUM).astype(int)
                begin,end = 0,0
                for c in selected:
                    begin = end
                    end = begin + len(alloc[c])
                    selected_info[c] = {'index':alloc[c],'begin':begin,'end':end}
                    selected_index += alloc[c]
                    selected_freq[c] = len(alloc[c])
                print([(key,len(selected_info[key]['index'])) for key in selected_info.keys()])
                selected_index += sparse_index
                restore_mask = np.argsort(np.array(selected_index))
                selected_vec = one_hot(selected,CLIENT_NUM)
                model_chose[epoch,b,:] = selected_freq
                # If only one client in selection, switch back to protocol 1
                if SWITCH_MODE == 'thre' and len(selected) <= 1:
                    switch_back = True
                    print("switch back to protocol 1")
                else:
                    switch_back = False

                np.save(CHOICE_PATH,model_chose)

                # send indexes to selected clients
                # send selected info to clients 2022.4.26
                for i in range(CLIENT_NUM):
                    if selected_vec[i]:
                        sendData = pickle.dumps({'switch_back':switch_back,'selected':True,'remask':restore_mask,'sparse':sparse_index,'selected_info':selected_info[i]})
                    else:
                        sendData = pickle.dumps({'switch_back':switch_back,'selected':False,'remask':restore_mask,'sparse':sparse_index})
                    remain = len(sendData)
                    conn = connections[i]
                    conn.send(pickle.dumps(remain))
                    resp = conn.recv(BUFF_SIZE)
                    if pickle.loads(resp) != READY_MSG:
                        raise ValueError("receive unrecognized message!")
                    conn.send(sendData)

                logging.debug("send alloc info to clients")

                if not switch_back:
                    # receive anchors and residues
                    grad_anchors,grad_residues = None, None
                    first_conn = min(list(alloc.keys()))
                    for i in range(CLIENT_NUM):
                        conn = connections[i]
                        if selected_vec[i]:
                            # recv anchors
                            remain = pickle.loads(conn.recv(BUFF_SIZE))
                            conn.send(pickle.dumps(READY_MSG))
                            recvData = b""
                            while remain > 0:
                                each_data = conn.recv(BUFF_SIZE)
                                remain -= len(each_data)
                                recvData += each_data

                            anch_res = pickle.loads(recvData)
                            if i == first_conn:
                                grad_anchors = np.array(anch_res['anchors'])
                                grad_residues = np.array(anch_res['residues'])
                            else:
                                grad_anchors += np.array(anch_res['anchors'])
                                grad_residues += np.array(anch_res['residues'])
                            #conn.send(pickle.dumps(COMP_MSG))

                    logging.debug("receive local anchors & residues from clients")

                    # broadcast global gradients
                    grad_residues /= alloc_num
                    global_grads = [grad_anchors,grad_residues]
                    sendData = pickle.dumps(global_grads)
                    remain = len(sendData)
                    for i in range(CLIENT_NUM):
                        conn = connections[i]
                        conn.send(pickle.dumps(remain))
                        resp = conn.recv(BUFF_SIZE)
                        if pickle.loads(resp) != READY_MSG:
                            raise ValueError("receive unrecognized message!")
                        conn.send(sendData)
                    logging.debug("send global anchors & residues to clients")
                else:
                    logging.debug("switch back to protocol 1 in epoch {} step {}".format(epoch,b))
            elif protocol_switch == '1':
                # receive local encrypted gradients
                agg_grad = None
                for i in range(CLIENT_NUM):
                    conn = connections[i]
                    remain = pickle.loads(conn.recv(BUFF_SIZE))
                    conn.send(pickle.dumps(READY_MSG))
                    recvData = b""
                    while remain > 0:
                        each_data = conn.recv(BUFF_SIZE)
                        remain -= len(each_data)
                        recvData += each_data
                    grads = pickle.loads(recvData)
                    if i == 0:
                        agg_grad = np.array(grads)
                    else:
                        agg_grad += np.array(grads)
                logging.debug("receive local gradients from clients")

                # send global encrypted gradients
                sendData = pickle.dumps(agg_grad)
                remain = len(sendData)
                for i in range(CLIENT_NUM):
                    conn = connections[i]
                    conn.send(pickle.dumps(remain))
                    resp = conn.recv(BUFF_SIZE)
                    if pickle.loads(resp) != READY_MSG:
                        raise ValueError("receive unrecognized message!")
                    conn.send(sendData)
                logging.debug("send global gradients to clients")
            else:
                print('unknown protocol!')
                assert 0


# encoding: utf-8
"""
Tests for the increment_int_list module.

@author: Ofir Picazo - ofirpicazo@gmail.com
@date: March 2012
"""

import unittest

from numbers.increment_int_list import increment

class IncrementIntListTest(unittest.TestCase):
    def test_increment(self):
        input_list = [8, 7, 9, 9]
        expected_list = [8, 8, 0, 0]
        self.assertEquals(expected_list, increment(input_list))

    def test_increment_all9s(self):
        input_list = [9, 9, 9, 9, 9]
        expected_list = [1, 0, 0, 0, 0, 0]
        self.assertEquals(expected_list, increment(input_list))


from fastapi                 import FastAPI, WebSocket
from fastapi.staticfiles     import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from app.routers.user        import router as user_router
from app.routers.category    import router as category_router
from app.routers.blog        import router as blog_router
from app.routers.content     import router as content_router
from app.routers.image       import router as image_router
from app.home                import router as home_router
from app.config              import *
import os

os.makedirs("static", exist_ok=True)

app = FastAPI(docs_url=DOCS_URL, redoc_url=None)
app.add_middleware(
    middleware_class=CORSMiddleware,
    allow_origins=ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"]
)
app.mount(path="/images",    app=StaticFiles(directory="static"),    name="static")
app.mount(path="/templates", app=StaticFiles(directory="templates"), name="templates")

websocket_clients = set()
messages = []

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()

    websocket_clients.add(websocket)

    try:
        while True:
            text = await websocket.receive_text()

            messages.append(text)

            for client in websocket_clients:
                # for message in messages:
                #     print(message)
                await client.send_json({"message": messages})
    except Exception as e:
        print(e)
    finally:
        websocket_clients.remove(websocket)

app.include_router(home_router,     prefix="",                 tags=["Home"])
app.include_router(user_router,     prefix="/api/v1/user",     tags=["Users"])
app.include_router(category_router, prefix="/api/v1/category", tags=["Category"])
app.include_router(blog_router,     prefix="/api/v1/blog",     tags=["Blog"])
app.include_router(content_router,  prefix="/api/v1/content",  tags=["Content"])
app.include_router(image_router,    prefix="/api/v1/upload",   tags=["Image"])


# Given two strings s and t of lengths m and n respectively, return the minimum window
# substring of s such that every character in t (including duplicates) is included in
# the window. If there is no such substring, return the empty string "".

# The testcases will be generated such that the answer is unique.

# Constraints:
#   m == s.length
#   n == t.length
#   1 <= m, n <= 105
#   s and t consist of uppercase and lowercase English letters.

# Follow up: Could you find an algorithm that runs in O(m + n) time?

from collections import Counter

TEST_CASES = [
  ["ADOBECODEBANC", "ABC", "BANC"],
  ["a", "a", "a"],
  ["a", "aa", ""],
]

def all_letters_used(substr, target):
    # could be improved with uniqueness
    for char in target:
        if substr.count(char) < target.count(char):
            return False

    return True

def minimum_window_substring(s, t):
    ans = ""

    if len(s) < len(t):
        return ans

    left = 0
    right = len(t)

    # build first window
    substr = s[left:right]

    # check if all letters present
    if all_letters_used(substr, t):
        # case where t is the leftmost substr of s
        return substr

    right += 1
    while right <= len(s):
        substr = s[left:right]
        while all_letters_used(substr, t):
            if len(substr) <= len(ans) or ans == "":
                ans = substr

            left += 1
            substr = s[left:right]

        right += 1

    # return answer
    return ans

def minimum_window_substring_optimized(s, t):
    # print("Start:", s, t)
    ans = ""

    if len(s) < len(t):
        return ans

    # m
    characters_needed = Counter(t)
    # print('A:', characters_needed)

    left = 0
    right = 0
    while right < len(s):
        # expand window right until all characters are accounted for
        while not all(count <= 0 for count in characters_needed.values()):
            char = s[right]
            if char in characters_needed:
                characters_needed[char] -= 1

            right += 1

        # shrink window from the left until we need more characters
        while all(count <= 0 for count in characters_needed.values()):
            substr = s[left:right]

            # check if shortest valid substr so far
            if len(substr) < len(ans) or ans == "":
                # if so, replace ans
                ans = substr

            char = s[left]
            if char in characters_needed:
                characters_needed[char] += 1
            left += 1

    return ans

# def minimum_window_substring_best(s, t):
#     tDict = defaultdict(int)
#     for c in t:
#         tDict[c]+=1
#     print(tDict)
#     m,n=len(s),len(t)
#     need=len(tDict)
#     have=0
#     l,r=0,0
#     mS=""
#     ma=float('inf')
#     sDict=defaultdict(int)
#     for r in range(m):
#         # print(sDict)
#         sDict[s[r]]+=1
#         if tDict.get(s[r],-1) == sDict[s[r]]:
#             have+=1
#             # print(have)
#         while have==need and l<=r:
#             if r-l+1<ma:
#                 ma=r-l+1
#                 mS=s[l:r+1]
#             if sDict[s[l]]==tDict.get(s[l],-1):
#                 have-=1
#             sDict[s[l]]-=1
#             l+=1
#     return mS

def minimum_window_substring_best(s, t):
    # count letters in t; this is O(n)
    t_counts = Counter(t)

    n = len(s)
    # how many character counts have to match, or, how many unique characters in t
    need = len(t_counts)

    # how many character counts we currently have that do match
    have = 0

    # initialize left and right pointers
    left, right = 0,0

    ans = ""
    len_ans = float('inf')
    s_counts = Counter()

    # for character in s; this is O(m)
    for right in range(n):
        # expand the window to the right
        right_char = s[right]
        s_counts[right_char] += 1

        # if adding that character makes our count for that character match the target
        if t_counts.get(right_char, -1) == s_counts[right_char]:
            # increment the number of matching character counts
            have += 1

        # whenever we have as many matching character counts as we need,
        # we have a valid substring solution
        while have == need and left <= right:
            # check if current substr len is better than ans len
            substring_len = right - left + 1
            if substring_len < len_ans:
                # if so, update the answer
                len_ans = substring_len
                ans = s[left:right+1]

            left_char = s[left]

            # if the character we're removing is one in the target,
            if s_counts[left_char] == t_counts.get(left_char,-1):
                # decrement the number of matching character counts
                have -= 1

            # shorten the window from the left
            s_counts[left_char] -= 1
            left += 1

    return ans

if __name__ == "__main__":
    for case in TEST_CASES:
        print(minimum_window_substring_best(case[0], case[1]) == case[2])


import urllib2
import re

urlInfo = "localhost:8000"

r = re.compile("<li><a\shref=\"[^\"\.]*\.py\">(([A-Za-z])+\.py)<\/a>")

try:
	req = urllib2.Request('http://localhost:8000')
	response = urllib2.urlopen(req)
	the_page = response.read()

	for mo in r.finditer(the_page):
		print(mo.group(0))
		print(mo.group(1))

except Exception as e:
	print ("Error -> ",e)


from socket import *

socked=socket(AF_INET,SOCK_DGRAM)

socked.bind(("127.0.0.1",8888))

while True:
    data,addr=socked.recvfrom(1024)
    print("收到信息为：",data.decode())
    socked.sendto(b"Thanks",addr)

socked.close()


from enum import Enum
import numpy as np
import pandas as pd
import requests
from collections import Counter

class Criterion(Enum):
    MISCLASSIFICATION_RATE = 1
    GINI_IMPURITY = 2
    ENTROPY = 3

class Node(object):
    def __init__(self, X, Y, depth=0, is_leaf=False, node_type="root", rule=""):
        self.X = X
        self.Y = Y
        self.n = len(self.Y)
        self.depth = depth
        self.split_feature = None
        self.split_value = None
        self.is_leaf = is_leaf
        self.left = None
        self.right = None
        self.node_type = node_type
        self.rule = rule
        self.counts = Counter(Y)
        counts_sorted = list(
            sorted(self.counts.items(), key=lambda item: item[1]))

        yhat = None
        if len(counts_sorted) > 0:
            yhat = counts_sorted[-1][0]
        self.yhat = yhat

    def gini(self):

        counts = Counter(self.Y)
        c1, c2 = counts.get(
            0, 0), counts.get(1, 0)

        return self.gini_impurity(c1, c2)

    def gini_impurity(self, count_of_class_1, count_of_class_2):
        if count_of_class_1 is None:
            count_of_class_1 = 0

        if count_of_class_2 is None:
            count_of_class_2 = 0

        if count_of_class_1 + count_of_class_2 == 0:
            return 0.0

        probability_of_class_1 = count_of_class_1 / (count_of_class_1 + count_of_class_2)
        probability_of_class_2 = count_of_class_2 / (count_of_class_1 + count_of_class_2)

        gini_impurity = 1 - (probability_of_class_1 ** 2 + probability_of_class_2 ** 2)

        return gini_impurity

    def average(self, x: np.array, window: int) -> np.array:
        cumsum = np.cumsum(x)
        cumsum[window:] = cumsum[window:] - cumsum[:-window]
        return cumsum[window - 1:] / window

class decision_tree_classifier(object):

    def __init__(self, criterion: Criterion =Criterion.GINI_IMPURITY, min_samples_split=10,  max_depth=5, min_samples_leaf=1):
        self.root = None
        self.min_samples_split = min_samples_split
        self.min_samples_leaf = min_samples_leaf
        self.max_depth = max_depth
        self.criterion = criterion
        self.X = None
        self.y = None
        self.features = None

    def fit(self, X, y):

        self.X = X
        self.Y = y
        self.features = list(self.X.columns)
        self.root = self.initialize_tree()

        return self.root

    def initialize_tree(self):
        root_node = Node(self.X, self.Y, depth=0,
                         is_leaf=False)
        self.grow_tree(root_node)
        return root_node

    def entropy(self, counts):
        total = sum(counts.values())
        entropy = 0
        for count in counts.values():
            probability = count / total
            if probability > 0:
                entropy += -probability * np.log2(probability)
        return entropy

    def best_split(self, node):

        d = node.X.copy()
        d['Y'] = node.Y

        b_gini = node.gini()

        m_gain = 0

        b_feature = None
        b_value = None

        for f in self.features:

            df = d.dropna().sort_values(f)

            m = node.average(df[f].unique(), 2)

            for v in m:

                left_d = df[df[f] < v]
                right_d = df[df[f] >= v]

                lc = left_d['Y'].value_counts().to_dict()
                rc = right_d['Y'].value_counts().to_dict()

                y0l, y1l = lc.get(0, 0), lc.get(1, 0)
                y0r, y1r = rc.get(0, 0), rc.get(1, 0)

                gini_l = node.gini_impurity(y0l, y1l)
                gini_r = node.gini_impurity(y0r, y1r)

                n_l = len(left_d)
                n_r = len(right_d)

                w_l = n_l / (n_l + n_r)
                w_r = n_r / (n_l + n_r)

                w_gini = w_l * gini_l + w_r * gini_r

                g_gain = b_gini - w_gini

                if g_gain > m_gain:
                    b_feature = f
                    b_value = v

                    m_gain = g_gain

        return b_feature, b_value

    def grow_tree(self, node):

        df = node.X.copy()
        df['Y'] = node.Y

        if (node.depth < self.max_depth) and (node.n >= self.min_samples_split) :

            best_feature, best_value = None, None

            if self.criterion == Criterion.MISCLASSIFICATION_RATE:
                best_feature, best_value = self.misclassification_split(node)
            elif self.criterion == Criterion.GINI_IMPURITY:
                best_feature, best_value = self.best_split(node)
            elif self.criterion == Criterion.ENTROPY:
                best_feature, best_value = self.best_split_entropy(node)

            if best_feature is not None:

                left_df, right_df = df[df[best_feature] <= best_value].copy(
                ), df[df[best_feature] > best_value].copy()

                if(left_df.shape[0]>=self.min_samples_leaf and right_df.shape[0]>=self.min_samples_leaf):

                    node.split_feature = best_feature
                    node.split_value = best_value
                    left = Node(

                        left_df[self.features],
                        left_df['Y'].values.tolist(),
                        depth=node.depth + 1,
                        node_type='left_node',
                        rule=f"{best_feature} <= {round(best_value, 3)}"
                    )

                    node.left = left
                    self.grow_tree(node.left)

                    right = Node(
                        right_df[self.features],
                        right_df['Y'].values.tolist(),
                        depth=node.depth + 1,
                        node_type='right_node',
                        rule=f"{best_feature} > {round(best_value, 3)}"
                    )

                    node.right = right
                    self.grow_tree(node.right)

    def print_node(self, node, width=4):

        # Defining the number of spaces
        const = int(node.depth * width ** 1.5)
        spaces = "-" * const

        if node.node_type == 'root':
            print("Root")
        else:
            print(f"{'-' * const} Split rule: {node.rule}")

    def print_tree(self, node):
        self.print_node(node)

        if node.left is not None:
            self.print_tree(node.left)

        if node.right is not None:
            self.print_tree(node.right)

    def predict(self, X: pd.DataFrame):
        predictions = []

        for _, x in X.iterrows():
            values = {}
            for feature in self.features:
                values.update({feature: x[feature]})

            # print(values)
            predictions.append(self.predict_obs(values))

        return np.array(predictions)

    def predict_obs(self, values: dict) -> int:
        cur_node = self.root
        while cur_node.depth < self.max_depth:

            if cur_node.n < self.min_samples_split:
                break

            if (cur_node.split_feature == None):
                break

            best_feature = cur_node.split_feature

            best_value = cur_node.split_value

            if (values.get(best_feature) < best_value):
                if cur_node.left is not None:
                    cur_node = cur_node.left
            else:
                if cur_node.right is not None:
                    cur_node = cur_node.right

        return cur_node.yhat

    def best_split_entropy(self, node):
        df = node.X.copy()
        df['Y'] = node.Y

        max_gain = 0
        best_feature = None
        best_value = None

        for feature in self.features:
            Xdf = df.dropna().sort_values(feature)

            xmeans = node.average(Xdf[feature].unique(), 2)

            for value in xmeans:

                left_counts = Counter(Xdf[Xdf[feature] < value]['Y'])
                right_counts = Counter(Xdf[Xdf[feature] >= value]['Y'])

                entropy_gain = self.entropy_gain(
                    left_counts, right_counts, node)

                if entropy_gain > max_gain:
                    max_gain = entropy_gain
                    best_feature = feature
                    best_value = value

        return best_feature, best_value

    def entropy_gain(self, left_counts, right_counts, node):

        n_left = sum(left_counts.values())
        n_right = sum(right_counts.values())
        n_total = n_left + n_right

        weighted_entropy = (n_left / n_total) * self.entropy(left_counts) + \
                           (n_right / n_total) * self.entropy(right_counts)

        entropy_gain = self.entropy(node.counts) - weighted_entropy
        return entropy_gain

    def misclassification_split(self, node):

        df = node.X.copy()
        df['Y'] = node.Y

        misclassification_base = 1 - max(node.counts.values()) / len(node.Y)

        max_reduction = 0

        best_feature = None
        best_value = None

        for feature in self.features:

            Xdf = df.dropna().sort_values(feature)

            xmeans = node.average(Xdf[feature].unique(), 2)

            for value in xmeans:

                left_counts = Counter(Xdf[Xdf[feature] < value]['Y'])
                right_counts = Counter(Xdf[Xdf[feature] >= value]['Y'])

                y0_left, y1_left, y0_right, y1_right = left_counts.get(0, 0), left_counts.get(
                    1, 0), right_counts.get(0, 0), right_counts.get(1, 0)

                misclassification_left = 1 - \
                    max(y0_left, y1_left) / (y0_left + y1_left + 0.000000001)
                misclassification_right = 1 - \
                    max(y0_right, y1_right) / (y0_right + y1_right + 0.000000001)

                n_left = y0_left + y1_left
                n_right = y0_right + y1_right

                w_left = n_left / (n_left + n_right)
                w_right = n_right / (n_left + n_right)

                wMisclassification = w_left * (misclassification_base - misclassification_left) + w_right * (
                    misclassification_base - misclassification_right)

                if wMisclassification > max_reduction:
                    max_reduction = wMisclassification
                    best_feature = feature
                    best_value = value

        if best_feature is None or best_value is None:
            return None, None

        return best_feature, best_value

if __name__ == '__main__':
    # Reading data
    d = pd.read_csv(
        "data/train.csv")[['Age', 'Sex', 'Fare', 'Pclass', 'Survived']].dropna()
    d = d.assign(Sex=d.Sex.eq('male').astype(int))

    # Constructing the X and Y matrices
    X = d[['Age', 'Sex', 'Fare', 'Pclass']]
    Y = d['Survived'].values.tolist()

    # Initiating the Node

    dt = decision_tree_classifier(
        Criterion.ENTROPY, min_samples_split=10, max_depth=5, min_samples_leaf=1)
    root_node = dt.fit(X, Y)
    dt.print_tree(root_node)

    # Predicting
    Xsubset = X.copy()
    Xsubset['yhat'] = dt.predict(Xsubset)
    yhat = Xsubset['yhat'].values

    # print(Xsubset)
    same = 0
    for i in range(len(yhat)):
        if yhat[i] == Y[i]:
            same += 1

    print("ACCURACY: ", same/len(yhat))

# References
# https://gist.github.com/Eligijus112/4c47be2f7566299bb8c4f97c107d82c6#file-best_split_rf-py
# https://gist.github.com/Eligijus112/a28967462cd025de0d7d61dac3cb566c


from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_pagedown import PageDown
from config import config
from flask_admin import Admin, BaseView, expose
from flask_admin.contrib.fileadmin import FileAdmin
from flask_babelex import Babel
import os.path as op

bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
pagedown = PageDown()
flas_admin = Admin(name='smart-iiot')
babel = Babel()

# flask-login
login_manager = LoginManager()
login_manager.login_view = 'auth.login'

# flask-admin add views
from app.admin import UserAdminView, TestAdminView, UserModelView, LoraGatewayModelView, LoraNodeModelView, NodeMqttTransFuncModelView, PowerIoModelView, RelayCurrentRs485FuncModelView, \
GrainStorehouseModelView, GrainBarnModelView, GrainTempModelView, AlarmLevelSettingModelView, AlarmStatusModelView, AlarmTypesModelView, AlarmRecordsModelView

#
# flas_admin.add_view(UserAdminView(name='UserAdmin', category='UserAdmin'))
# flas_admin.add_view(TestAdminView(name='test', endpoint='test', category='UserAdmin'))

flas_admin.add_view(GrainStorehouseModelView(db.session, name='GrainStorehouse', endpoint='grain_storehouse', category='GrainAdmin'))
flas_admin.add_view(GrainBarnModelView(db.session, name='GrainBarn', endpoint='grain_barn', category='GrainAdmin'))
flas_admin.add_view(GrainTempModelView(db.session, name='GrainTemp', endpoint='grain_temps', category='GrainAdmin'))

flas_admin.add_view(LoraGatewayModelView(db.session, name='LoraGateway', endpoint='lora_gateway', category='LoraAdmin'))
flas_admin.add_view(LoraNodeModelView(db.session, name='LoraNode', endpoint='lora_node', category='LoraAdmin'))
flas_admin.add_view(NodeMqttTransFuncModelView(db.session, name='NodeMqttTransFunc', endpoint='node_mqtt_trans_func', category='LoraAdmin'))
flas_admin.add_view(PowerIoModelView(db.session, name='PowerIo', endpoint='power_io', category='LoraAdmin'))
flas_admin.add_view(RelayCurrentRs485FuncModelView(db.session, name='RelayCurrentRs485Func', endpoint='relay_current_rs485_func', category='LoraAdmin'))

flas_admin.add_view(AlarmStatusModelView(db.session, name='AlarmStatus', endpoint='alarm_status', category='AlarmAdmin'))
flas_admin.add_view(AlarmTypesModelView(db.session, name='AlarmTypes', endpoint='alarm_types', category='AlarmAdmin'))
flas_admin.add_view(AlarmRecordsModelView(db.session, name='AlarmRecords', endpoint='alarm_records', category='AlarmAdmin'))
flas_admin.add_view(AlarmLevelSettingModelView(db.session, name='AlarmLevelSetting', endpoint='alarm_level_setting', category='AlarmAdmin'))

flas_admin.add_view(UserModelView(db.session, name='User', endpoint='user', category='UserAdmin'))

path = op.join(op.dirname(__file__), 'static')
print(path)
flas_admin.add_view(FileAdmin(path, '/static/', name='Static Files'))

def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)
    # bable config for i18n
    app.config['BABEL_DEFAULT_LOCALE'] = 'zh_CN'

    if app.config['SSL_REDIRECT']:
        from flask_sslify import SSLify
        sslify = SSLify(app)

    configure_extensions(app)

    register_blueprints(app)

    return app

def configure_extensions(app):
    """configure flask extensions
    """
    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)
    login_manager.init_app(app)
    pagedown.init_app(app)
    babel.init_app(app)
    flas_admin.init_app(app)

def register_blueprints(app):
    """register all blueprints for application
    """

    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)
    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/auth')
    from .api import api as api_blueprint
    app.register_blueprint(api_blueprint, url_prefix='/api/v1')


from sklearn.datasets import make_regression
from mllib.supervised.LinearRegression.regressions import *
from mllib.utilities.losses import *
from mllib.utilities.lr_schedulers import *
np.random.seed(1)
plt.rcParams['axes.grid'] = True

if __name__=='__main__':
    N_FEATURES = 1
    ANIMATE = True
    LEARNING_RATE = 1e-1
    assert (not ANIMATE and N_FEATURES>=1) or (ANIMATE and N_FEATURES==1), 'Cannot animate more than 1 dimension'

    X, y = make_regression(n_features=N_FEATURES, noise=15)
    X = np.array([[0.05, 0.1,0.5,0.51,0.51,0.52,0.8, 0.9, 1.2, 1.3,1.55,1.75,1.76,2.1,2.25,2.4,2.5,2.8,2.9, 2.9]]).reshape(-1,1)
    y = np.array([0.25,1.1,1.05,2.25,0.8,1.1,2.5,0.6,1.4,1.2,2.2,1.05,0.95,1.5,2,1.5,1.85,1.4,2.5,2.6])

    # X = 2*np.array(range(0, 11)) + np.random.normal()
    # X = X.reshape(-1,1)
    # y = 2*X**2 - 2*X
    # y = y.flatten()
    # sc = StandardScaler()
    # X = sc.fit_transform(X)
    scheduler = configure_power_scheduler(lr0=LEARNING_RATE, every=30)
    # scheduler = configure_exponential_decay(lr0=LEARNING_RATE, every=50)

    # lr = LinearRegression(loss=MSE, learning_rate=LEARNING_RATE, scheduler_fn=None)
    # lr.fit(X, y, epochs=30,animate=ANIMATE)

    lr = LassoRegression(loss=MSE, alpha=0.9, learning_rate=LEARNING_RATE, scheduler_fn=None)
    lr.fit(X, y, epochs=30,animate=ANIMATE)

    # lasso = Lasso(alpha=0.9)
    # lr = PolynomialRegression(loss=MSE,  learning_rate=LEARNING_RATE, scheduler_fn=scheduler, degree=6, penalty=lasso)
    # lr.fit(X, y, epochs=200,animate=ANIMATE)

    # nr = NormalEquation(alpha=0.9)
    # nr.fit(X, y, animate=ANIMATE)
    # ridge_reg = ElasticNetRegression(l1_ratio=0.9, alpha=0.9, learning_rate=1e-1, loss=MSE)
    # ridge_reg.fit(X,y,epochs=2000, animate=ANIMATE)

    # lr = ElasticNetRegression(loss=MSE, alpha=0.9, learning_rate=LEARNING_RATE, scheduler_fn=scheduler, l1_ratio=0.9)
    # lr.fit(X, y, epochs=50, animate=True)


# 20220128 - Python - For Loop
# 10 - Odd and Even Sums - judge: https://judge.softuni.org/Contests/Compete/Index/2417#9

nums_to_enter = int(input())
odd_sum, even_sum, diff = 0, 0, 0

for i in range(nums_to_enter):
    if i % 2 == 0:
        odd_sum += int(input())
    else:
        even_sum += int(input())

diff = abs(odd_sum - even_sum)

if odd_sum == even_sum:
    print("Yes")
    print(f"Sum = {odd_sum}")
else:
    print("No")
    print(f"Diff = {diff}")