import os
import time
from datetime import datetime
import torch
from torch import nn
from torch.utils.tensorboard import SummaryWriter
import torch.nn.functional as F
from torch.autograd import Variable
import pandas as pd
import warnings
warnings.filterwarnings("ignore")
import random
import numpy as np
import utils.config as config
import matplotlib.pyplot as plt
import os, psutil
import functools
from skimage.measure import label as sk_label
from skimage.measure import regionprops as sk_regions
from skimage.transform import resize
# let all of print can be flush = ture
print = functools.partial(print, flush=True)

#-------- Dataloder --------
# After augmnetation with resize, crop spleen area and than transofermer
class BoxCrop(object):
    '''
    Croping image by bounding box label after augmentation
    input: keys=["image", "label"]
    label:
    [[x1,y1,x2,y2,z1,z2,class]...]
    image:
    [1,x,y,z]
    output dictionary add
        im_info: [x,y,z,scale_x_y,scale_z]
        num_box: 1 (All is one in our data)
    '''
    def __init__(self,keys):
        self.keys = keys

    def __call__(self, data):
        d = dict(data)
        image = d['image']
        label = d['label']
        # only one label
        if type(label) == type(np.array([])):
            label_list = label.tolist()
        else:
        # more than one label
        # select the first label
            label_list = eval(label)[0]
        if label_list[1]>=label_list[3] or label_list[0]>=label_list[2] or label_list[4]>=label_list[5]:
            raise RuntimeError(f"{d['image_meta_dict']['filename_or_obj']} bounding box error")
                #print(f"{d['image_meta_dict']['filename_or_obj']} bounding box error ")
        out_image = image[0, int(label_list[1]):int(label_list[3]), int(label_list[0]):int(label_list[2]), int(label_list[4]):int(label_list[5])]
        d['image'] = np.expand_dims(out_image,axis=0)
        d['label'] = label_list[6]
        #print(d['image'].shape)
        return d

# Dulicated dataset by num_samples
class Dulicated(object):
    '''
    Dulicated data for augmnetation
    '''
    def __init__(self,
                 keys,
                 num_samples: int = 1):
        self.keys = keys
        self.num_samples = num_samples

    def __call__(self, data):
        d = dict(data)
        image = d['image']
        label = d['label']
        results: List[Dict[Hashable, np.ndarray]] = [dict(data) for _ in range(self.num_samples)]

        for key in data.keys():
            for i in range(self.num_samples):
                results[i][key] = data[key]
        return results
        #return d

# True label
class Annotate(object):
    '''
    transform mask to bounding box label after augmentation
    check the image shape to know scale_x_y, scale_z
    input: keys=["image", "label"]
    output dictionary add
        im_info: [x,y,z,scale_x_y,scale_z]
        num_box: 1 (All is one in our data)
    '''
    def __init__(self,keys):
        self.keys = keys

    def __call__(self, data):
        d = dict(data)
        #image = d[self.keys[0]]
        #label = d[self.keys[1]]
        image = d['image']
        label = d['label']
        label = label.squeeze(0)
        annotations = np.zeros((1, 7))
        annotation = mask2boundingbox(label)
        if annotation == 0:
            annotation = annotations
            raise ValueError('Dataloader data no annotations')
            #print("Dataloader data no annotations")
        else:
            # add class label
            cls = d['class']
            annotation = np.array(annotation)
            annotation = np.append(annotation, cls)
            #annotation = np.expand_dims(annotation,0)
        #print(annotation.shape)
        #print(image.shape)
        d['label'] = annotation
        return d

def mask2boundingbox(label):
    if torch.is_tensor(label):
        label = label.numpy()
    sk_mask = sk_label(label)
    regions = sk_regions(label.astype(np.uint8))
    #global top, left, low, bottom, right, height
    #print(regions)
    # check regions is empty
    if not regions:
        return 0

    for region in regions:
        # print('[INFO]bbox: ', region.bbox)
        # region.bbox (x1,y1,z1,x2,y2,z2)
        # top, left, low, bottom, right, height = region.bbox
        y1, x1, z1, y2, x2, z2 = region.bbox
   # return left, top, right, bottom, low, height
    return x1, y1, x2, y2, z1, z2

#-------- Running setting --------
'''
def adjust_learning_rate_by_step(optimizer, epoch, init_lr, decay_rate=.5 ,lr_decay_epoch=40):
    #Sets the learning rate to initial LR decayed by e^(-0.1*epochs)
    lr = init_lr * (decay_rate ** (epoch // lr_decay_epoch))
    for param_group in optimizer.param_groups:
        #param_group['lr'] =  param_group['lr'] * math.exp(-decay_rate*epoch)
        param_group['lr'] = lr
        #lr = init_lr * (0.1**(epoch // lr_decay_epoch))
    #print('LR is set to {}'.format(param_group['lr']))
    return optimizer , lr

def adjust_learning_rate(optimizer, epoch, init_lr, decay_rate=.5):
    #Sets the learning rate to initial LR decayed by e^(-0.1*epochs)
    lr = init_lr * decay_rate
    for param_group in optimizer.param_groups:
        #param_group['lr'] =  param_group['lr'] * math.exp(-decay_rate*epoch)
        param_group['lr'] = lr
        #lr = init_lr * (0.1**(epoch // lr_decay_epoch))
    #print('LR is set to {}'.format(param_group['lr']))
    return optimizer , lr
'''

def train(model, device, data_num, epochs, optimizer, loss_function, train_loader, valid_loader, early_stop, scheduler, check_path):
    # Let ini config file can be writted
    #global best_metric
    #global best_metric_epoch
    #val_interval = 2
    best_metric = -1
    best_metric_epoch = -1
    trigger_times = 0

    #epoch_loss_values = list()

    writer = SummaryWriter()
    for epoch in range(epochs):
        print("-" * 10)
        print(f"epoch {epoch + 1}/{epochs}")
        # record ram memory used
        process = psutil.Process(os.getpid())
        print(f'RAM used:{process.memory_info().rss/ 1024 ** 3} GB')
        model.train()
        epoch_loss = 0
        step = 0

        for batch_data in train_loader:
            step += 1
            inputs, labels = batch_data['image'].to(device), batch_data['label'].long().to(device)
            optimizer.zero_grad()
            #inputs, labels = Variable(inputs), Variable(labels)
            outputs = model(inputs)
            #print(f'outputs:{outputs.size()}')
            #print(f'labels:{labels.size()}')
            loss = loss_function(outputs, labels)
            loss.backward()
            optimizer.step()
            epoch_loss += loss.item()
            epoch_len = data_num // train_loader.batch_size
            print(f"{step}/{epoch_len}, train_loss: {loss.item():.4f}")
            writer.add_scalar("train_loss", loss.item(), epoch_len * epoch + step)
        epoch_loss /= step
        config.epoch_loss_values.append(epoch_loss)
        print(f"epoch {epoch + 1} average loss: {epoch_loss:.4f}")

        # Early stopping & save best weights by using validation
        metric = validation(model, valid_loader, device)
        scheduler.step(metric)

        # checkpoint setting
        if metric > best_metric:
            # reset trigger_times
            trigger_times = 0
            best_metric = metric
            best_metric_epoch = epoch + 1
            torch.save(model.state_dict(), f"{check_path}/{best_metric}.pth")
            print('trigger times:', trigger_times)
            print("saved new best metric model")
        else:
            trigger_times += 1
            print('trigger times:', trigger_times)
            # Save last 3 epoch weight
            if early_stop - trigger_times <= 3 or epochs - epoch <= 3:
                torch.save(model.state_dict(), f"{check_path}/{metric}_last.pth")
                print("save last metric model")
        print(
            "current epoch: {} current accuracy: {:.4f} best accuracy: {:.4f} at epoch {}".format(
                epoch + 1, metric, best_metric, best_metric_epoch
            )
        )
        writer.add_scalar("val_accuracy", metric, epoch + 1)

        # early stop
        if trigger_times >= early_stop:
            print('Early stopping!\nStart to test process.')
            print(f"train completed, best_metric: {best_metric:.4f} at epoch: {best_metric_epoch}")
            return model

    print(f"train completed, best_metric: {best_metric:.4f} at epoch: {best_metric_epoch}")
    config.best_metric = best_metric
    config.best_metric_epoch = best_metric_epoch
    writer.close()
    #print(f'training_torch best_metric:{best_metric}',flush =True)
    #print(f'training_torch config.best_metric:{config.best_metric}',flush =True)
    return model

class AngleLoss_predict(nn.Module):
    def __init__(self, gamma=0):
        super(AngleLoss_predict, self).__init__()
        self.gamma = gamma
        self.it = 1
        self.LambdaMin = 5.0
        self.LambdaMax = 1500.0
        self.lamb = 1500.0

    def forward(self, input, target):
        cos_theta, phi_theta = input
        target = target.view(-1, 1)  # size=(B,1)
        index = cos_theta.data * 0.0  # size=(B, Classnum)
        # index = index.scatter(1, target.data.view(-1, 1).long(), 1)
        #index = index.byte()
        index = index.bool()
        index = Variable(index)
        # index = Variable(torch.randn(1,2)).byte()

        self.lamb = max(self.LambdaMin, self.LambdaMax / (1 + 0.1 * self.it))
        output = cos_theta * 1.0  # size=(B,Classnum)
        output1 = output.clone()
        # output1[index1] = output[index] - cos_theta[index] * (1.0 + 0) / (1 + self.lamb)
        # output1[index1] = output[index] + phi_theta[index] * (1.0 + 0) / (1 + self.lamb)
        output[index] = output1[index]- cos_theta[index] * (1.0 + 0) / (1 + self.lamb)+ phi_theta[index] * (1.0 + 0) / (1 + self.lamb)
        return(output)

def validation(model, val_loader, device):
    #metric_values = list()
    model.eval()
    with torch.no_grad():
        num_correct = 0.0
        metric_count = 0
        for val_data in val_loader:
            val_images, val_labels = val_data['image'].to(device), val_data['label'].to(device)
            val_outputs = model(val_images)
            # base on AngleLoss
            if isinstance(val_outputs, tuple):
                val_outputs = AngleLoss_predict()(val_outputs,val_labels)
            value = torch.eq(val_outputs.argmax(dim=1), val_labels)
            metric_count += len(value)
            num_correct += value.sum().item()
        metric = num_correct / metric_count
        config.metric_values.append(metric)
        #print(f'validation metric:{config.metric_values}',flush =True)
    return metric

def plot_loss_metric(epoch_loss_values,metric_values,save_path):
    plt.figure("train", (12, 6))
    plt.subplot(1, 2, 1)
    plt.title("Epoch Average Loss")
    x = [i + 1 for i in range(len(epoch_loss_values))]
    y = epoch_loss_values
    plt.xlabel("epoch")
    plt.plot(x, y)
    plt.subplot(1, 2, 2)
    plt.title("Val Accuracy")
    x = [i + 1 for i in range(len(metric_values))]
    y = metric_values
    plt.xlabel("epoch")
    plt.plot(x, y)
    plt.savefig(f'{save_path}/train_loss_metric.png')

def kfold_split(file, kfold, seed, type, fold):
    if type == 'pos':
        d = {}
        file_list = ['file']
        file_list.extend([f'pos_split_df_{i}' for i in range(kfold)])
        d['file'] = file
        for i in range(kfold):
            d[f'test_pos_df_{i}'] = d[file_list[i]].groupby(["gender","age_range","spleen_injury_class"],group_keys=False).apply(lambda x: x.sample(frac=1/(kfold-i),random_state=1))
            d[f'pos_split_df_{i}'] = d[file_list[i]].drop(d[f'test_pos_df_{i}'].index.to_list())
        output_file = d[f'test_pos_df_{fold}']

    elif type == 'neg':
        file_list = [f'neg_split_df_{i}' for i in range(kfold)]
        file_list = np.array_split(file.sample(frac=1,random_state=seed), kfold)
        output_file = file_list[fold]

    return output_file

def Data_progressing(pos_file, neg_file, box_df, imbalance_data_ratio, data_split_ratio, seed, fold, save_file = False, cropping = True):
    # Pos data progress
    for index, row in pos_file.iterrows():
        if row['OIS']==row['OIS']:
            pos_file.loc[index,'spleen_injury_grade'] = row['OIS']
        else:
            pos_file.loc[index,'spleen_injury_grade'] = row['R_check']

    new_col= 'age_range'
    new_col_2 = 'spleen_injury_class'
    bins = [0,30,100]
    bins_2 = [0,2,5]
    label_2 = ['OIS 1,2','OIS 3,4,5']
    pos_file[new_col] = pd.cut(x=pos_file.age, bins=bins)
    pos_file[new_col_2] = pd.cut(x=pos_file.spleen_injury_grade, bins=bins_2, labels=label_2)

    # positive need select column and split in kfold
    test_pos_df = kfold_split(pos_file, int(1/data_split_ratio[2]), seed, 'pos', fold)
    train_pos_file = pos_file.drop(test_pos_df.index.to_list())
    valid_pos_df = train_pos_file.groupby(['gender','age_range','spleen_injury_class'],group_keys=False).apply(lambda x: x.sample(frac=data_split_ratio[1]/(1-data_split_ratio[2]),random_state=seed))
    train_pos_df = train_pos_file.drop(valid_pos_df.index.to_list())

    # negative only need split in kfold
    neg_sel_df = neg_file.sample(n=len(pos_file),random_state=seed)
    test_neg_df =  kfold_split(neg_sel_df, int(1/data_split_ratio[2]), seed, 'neg', fold)
    train_neg_file = neg_file.drop(test_neg_df.index.to_list())
    valid_neg_df = train_neg_file.sample(n=len(valid_pos_df),random_state=seed)
    train_neg_df = train_neg_file.drop(valid_neg_df.index.to_list()).sample(n=len(train_pos_df)*imbalance_data_ratio,random_state=seed)

    train_df = pd.concat([train_neg_df,train_pos_df])
    valid_df = pd.concat([valid_neg_df,valid_pos_df])
    test_df = pd.concat([test_neg_df,test_pos_df])

    train_data = box_df[box_df.Path.isin(train_df.source.to_list())]
    valid_data = box_df[box_df.Path.isin(valid_df.source.to_list())]
    test_data = box_df[box_df.Path.isin(test_df.source.to_list())]

    train_df['spleen_injury'] = np.array([0 if i else 1 for i in train_df.spleen_injury_class.isna().tolist()])
    valid_df['spleen_injury'] = np.array([0 if i else 1 for i in valid_df.spleen_injury_class.isna().tolist()])
    test_df['spleen_injury'] = np.array([0 if i else 1 for i in test_df.spleen_injury_class.isna().tolist()])

    if save_file:
        test_df_output = pd.merge(test_data.loc[:,['ID','Path','BBox','Posibility']],test_df,left_on='Path',right_on='source',suffixes = ['','_x'])
        valid_df_output = pd.merge(test_data.loc[:,['ID','Path','BBox','Posibility']],test_df,left_on='Path',right_on='source',suffixes = ['','_x'])
        test_df_output = test_df_output.drop(['ID_x'],axis=1)
        valid_df_output = valid_df_output.drop(['ID_x'],axis=1)
        test_df_output = test_df_output.loc[:,test_df_output.columns[~test_df_output.columns.str.contains('Unnamed')]]
        valid_df_output = valid_df_output.loc[:,valid_df_output.columns[~valid_df_output.columns.str.contains('Unnamed')]]
        valid_df_output.to_csv(f'{save_file}/fold{fold}_valid.csv',index = False)
        test_df_output.to_csv(f'{save_file}/fold{fold}_test.csv',index = False)

    if cropping:
        train_data_dicts = []
        for index,row in train_data.iterrows():
            image = row['Path']
            label = row['BBox']
            train_data_dicts.append({'image':image,'label':label})
        valid_data_dicts = []
        for index,row in valid_data.iterrows():
            image = row['Path']
            label = row['BBox']
            valid_data_dicts.append({'image':image,'label':label})
        test_data_dicts = []
        for index,row in test_data.iterrows():
            image = row['Path']
            label = row['BBox']
            test_data_dicts.append({'image':image,'label':label})
    else:
        train_data_dicts =[
            {"image": image_name, "label": label_name}
            for image_name, label_name in zip([i for i in train_df.source.tolist()], [i for i in train_df.spleen_injury.tolist()] )
        ]
        valid_data_dicts =[
            {"image": image_name, "label": label_name}
            for image_name, label_name in zip([i for i in valid_df_output.source.tolist()], [i for i in valid_df_output.spleen_injury.tolist()] )
        ]
        test_data_dicts =[
            {"image": image_name, "label": label_name}
            for image_name, label_name in zip([i for i in test_df_output.source.tolist()], [i for i in test_df_output.spleen_injury.tolist()] )
        ]

    return train_data_dicts, valid_data_dicts, test_data_dicts

class FocalLoss(nn.Module):
    def __init__(self, class_num, alpha=None, gamma=2, size_average=True):
        """
        focal_loss损失函数, -α(1-yi)**γ *ce_loss(xi,yi)
        步骤详细的实现了 focal_loss损失函数.
        :param alpha:   阿尔法α,类别权重. 当α是列表时,为各类别权重,当α为常数时,类别权重为[α, 1-α, 1-α, ....],常用于 目标检测算法中抑制背景类 , retainnet中设置为0.25
        :param gamma:   伽马γ,难易样本调节参数. retainnet中设置为2
        :param num_classes:     类别数量
        :param size_average:    损失计算方式,默认取均值
        """
        super(FocalLoss, self).__init__()
        if alpha is None:  # alpha 是平衡因子
            self.alpha = Variable(torch.ones(class_num, 1))
        else:
            if isinstance(alpha, list):
                self.alpha = torch.Tensor(alpha)
            else:
                self.alpha = torch.zeros(class_num)
                self.alpha[0] += alpha
                self.alpha[1:] += (1-alpha)
        self.gamma = gamma  # 指数
        self.class_num = class_num  # 类别数目
        self.size_average = size_average  # 返回的loss是否需要mean一下

    def forward(self, preds, labels):
        """
        focal_loss损失计算
        :param preds:   预测类别. size:[B,N,C] or [B,C]  分别对应与检测与分类任务, B 批次, N检测框数, C类别数
        :param labels:  实际类别. size:[B,N] or [B]
        :return:
        """
        # assert preds.dim()==2 and labels.dim()==1
        preds = preds.view(-1,preds.size(-1))
        self.alpha = self.alpha.to(preds.device)
        preds_softmax = F.softmax(preds, dim=1) # 这里并没有直接使用log_softmax, 因为后面会用到softmax的结果(当然你也可以使用log_softmax,然后进行exp操作)
        preds_softmax = preds_softmax.clamp(min=0.0001,max=1.0)  # 避免數值過小 進log後 loss 為nan
        preds_logsoft = torch.log(preds_softmax)
        preds_softmax = preds_softmax.gather(1,labels.view(-1,1))   # 这部分实现nll_loss ( crossempty = log_softmax + nll )
        preds_logsoft = preds_logsoft.gather(1,labels.view(-1,1))
        self.alpha = self.alpha.gather(0,labels.view(-1))
        loss = -torch.mul(torch.pow((1-preds_softmax), self.gamma), preds_logsoft)  # torch.pow((1-preds_softmax), self.gamma) 为focal loss中 (1-pt)**γ
        loss = torch.mul(self.alpha, loss.t())
        if self.size_average:
            loss = loss.mean()
        else:
            loss = loss.sum()
        return loss


# prob_link: https://www.codingninjas.com/codestudio/problems/majority-element-ii_8230738?challengeSlug=striver-sde-challenge&leftPanelTab=0
from math import *
from collections import *
from sys import *
from os import *

def majorityElementII(arr):
    n = len(arr)
    # Write your code here.
    mp = {}
    for x in arr:
        if x not in mp:
            mp[x] = 1
        else:
            mp[x] += 1
    ans = []
    for key, val in mp.items():
        if val > floor(n / 3):
            ans.append(key)
    return ans


from fastapi import FastAPI

app = FastAPI()

COLUMN_NAME = "name"
COLUMN_ID = "id"
FAKE_DB = [
    {"id": 1, "name": "Vladimir"},
    {"id": 2, "name": "Polina"},
    {"id": 3, "name": "Aleksander"}
]

def find_friend_name(friend_id, db_name):
    for row in db_name:
        if row.get(COLUMN_ID) == friend_id:
            return row.get(COLUMN_NAME)

    return None

@app.get("/friends/{friend_id}")
async def get_friend_name(friend_id: int):
    friend_name = find_friend_name(friend_id, FAKE_DB)
    if friend_name is None:
        return {"error": f"No such friend with id {friend_id}"}

    return {"friend_name": friend_name}

@app.get("/")
async def root():
    return {"message": "Hello world!"}


from __future__ import annotations

from pathlib import Path
from typing import Any, cast

import _testutils
import pytest
from lxml.html import (
    HtmlElement as HtmlElement,
    find_class,
    find_rel_links,
    iterlinks,
    make_links_absolute,
    parse,
    resolve_base_href,
    rewrite_links,
)

reveal_type = getattr(_testutils, "reveal_type_wrapper")

def test_input_content_type(h_filepath: Path) -> None:
    fio = open(h_filepath, "rb")
    tree = parse(h_filepath)
    for bad_input in [h_filepath, fio, tree]:
        with pytest.raises(
            AttributeError, match="object has no attribute 'find_rel_links'"
        ):
            _ = find_rel_links(cast(Any, bad_input), "stylesheet")
    fio.close()

    links = find_rel_links(str(h_filepath), "stylesheet")
    reveal_type(links)
    assert links == find_rel_links(tree.getroot(), "stylesheet")
    assert links == find_rel_links(h_filepath.read_text(), "stylesheet")
    assert links == find_rel_links(h_filepath.read_bytes(), "stylesheet")

def test_find_class(h_filepath: Path) -> None:
    elems = find_class(h_filepath.read_text(), "single")
    reveal_type(elems)
    for e in elems:
        reveal_type(e)

def test_iterlinks(h_filepath: Path) -> None:
    results = iterlinks(h_filepath.read_text())
    reveal_type(results)
    for r in results:
        assert len(r) == 4
        reveal_type(r[0])
        reveal_type(r[1])
        reveal_type(r[2])
        reveal_type(r[3])

class TestOutputType:
    BASE = "http://dummy.link"

    def test_make_links_absolute(self, h_filepath: Path) -> None:
        in_data1 = h_filepath.read_bytes()
        with pytest.raises(
            TypeError, match="No base_url given, and the document has no base_url"
        ):
            out_data1 = make_links_absolute(in_data1)
        out_data1 = make_links_absolute(in_data1, self.BASE)
        assert type(in_data1) == type(out_data1)
        in_data2 = h_filepath.read_text()
        with pytest.raises(TypeError, match="Cannot mix str and non-str"):
            out_data2 = make_links_absolute(
                in_data2, cast(Any, self.BASE.encode("ascii"))
            )
        out_data2 = make_links_absolute(in_data2, self.BASE)
        assert type(in_data2) == type(out_data2)
        tree = parse(h_filepath)
        in_data3 = tree.getroot()
        out_data3 = make_links_absolute(in_data3, self.BASE)
        assert type(in_data3) == type(out_data3)

    def test_resolve_base_href(self, h_filepath: Path) -> None:
        in_data1 = h_filepath.read_bytes()
        out_data1 = resolve_base_href(in_data1)
        assert type(in_data1) == type(out_data1)
        in_data2 = h_filepath.read_text()
        out_data2 = resolve_base_href(in_data2)
        assert type(in_data2) == type(out_data2)
        tree = parse(h_filepath)
        in_data3 = tree.getroot()
        out_data3 = resolve_base_href(in_data3)
        assert type(in_data3) == type(out_data3)

    def test_rewrite_links(self, h_filepath: Path) -> None:
        in_data1 = h_filepath.read_bytes()
        out_data1 = rewrite_links(in_data1, lambda _: self.BASE)
        assert type(in_data1) == type(out_data1)
        in_data2 = h_filepath.read_text()
        with pytest.raises(TypeError, match="can only concatenate str"):
            out_data2 = rewrite_links(
                in_data2, lambda _: cast(Any, self.BASE.encode("ASCII"))
            )
        out_data2 = rewrite_links(in_data2, lambda _: self.BASE)
        assert type(in_data2) == type(out_data2)
        tree = parse(h_filepath)
        in_data3 = tree.getroot()
        out_data3 = rewrite_links(in_data3, lambda _: None)
        assert type(in_data3) == type(out_data3)


# Check if a given parentheses string is valid
#
# Input: par: string
# Output: true or false: bool
#
# We need a stack to store opening braces
# We need a map to store types of braces
#
# Check the length of the string, if the length is odd, return False
# Loop through the list, for each char,
# - If it is an opening brace, add it to the stack
# - If it is not an opening brace,
# -- If the stack is empty, return False
# -- Pop the stack and get the next opening brace
# --- If char is not equal to value at map with key opening brace , return False
# Return whether the stack is empty:
# - if there's an item left when the loop is done, the string is clearly unbalanced
def is_valid(par_str: str) -> bool:
    stack = []
    par_map = {"{": "}", "[": "]", "(": ")"}

    if len(par_str) % 2 != 0:
        return False

    for char in par_str:
        if char in par_map.keys():
            stack.append(char)
        else:
            if len(stack) == 0:
                return False
            open_brac = stack.pop()
            if char != par_map[open_brac]:
                return False
    return stack == []


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
===================================
Timer --- Create a timer decorator.
===================================

Largely this module was simply practice on writing decorators.

Might need to review logging best practices. I don't want the logger from
this module to emit anything, but it seems tedious to place that burden
on any module that imports from here.

.. seealso::

    :mod:`cProfile`
    :mod:`pstats`
    :mod:`timeit`
    :magic:`timeit`

"""
import datetime
import functools
import logging
from os import scandir
from runpy import run_path
import time
from timeit import Timer

from IPython.core.getipython import get_ipython

# noinspection PyProtectedMember
from IPython.core.magics.execution import _format_time as format_delta

logging.basicConfig(level=logging.INFO)

def timer(func):
    """Print the runtime of the decorated function.

    Utilizes `time.perf_counter`.

    .. todo:: Begin using the :mod:`timeit` module.

        There are more specialized ways of profiling things in
        other modules; however, this works for a rough estimate.

    Parameters
    ----------
    func : function
        Function to profile

    Returns
    -------
    value : float
        Output of function :func:`time.perf_counter()`.

    """

    @functools.wraps(func)
    def wrapper_timer(*args, **kwargs):
        start_time = time.perf_counter()
        value = func(*args, **kwargs)
        end_time = time.perf_counter()
        run_time = end_time - start_time
        logging.info(f"Finished {func.__name__!r} in {run_time:.4f} secs")
        return value

    return wrapper_timer

# class ModuleTimer()
# I mean while we're practicing decorators throw this in the mix
def debug(func):
    """Print the function signature and return value"""

    @functools.wraps(func)
    def wrapper_debug(*args, **kwargs):
        args_repr = [repr(a) for a in args]  # 1
        kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]  # 2
        signature = ", ".join(args_repr + kwargs_repr)  # 3
        print(f"Calling {func.__name__}({signature})")
        value = func(*args, **kwargs)
        print(f"{func.__name__!r} returned {value!r}")  # 4
        return value

    return wrapper_debug

def exc_timer(statement, setup=None):
    """A non-decorator implementation that uses `timeit`."""
    t = Timer(stmt=statement, setup=setup)  # outside the try/except
    try:
        return t.timeit()
    except Exception:  # noqa E722
        t.print_exc()

class ArgReparser:
    """Class decorator that echoes out the arguments a function was called with."""

    def __init__(self, func):
        """Initialize the reparser with the function it wraps."""
        self.func = func

    def __call__(self, *args, **kwargs):
        print("entering function " + self.func.__name__)
        i = 0
        for arg in args:
            print("arg {0}: {1}".format(i, arg))
            i = i + 1

        return self.func(*args, **kwargs)

def time_dir(directory=None):
    """How long does it take to exec(compile(file)) every file in the startup dir?"""
    if directory is None:
        directory = get_ipython().startup_dir
    result = []
    for i in scandir("."):
        if i.name.endswith(".py"):
            file = i.name
            print(file)
            print(time.time())
            start_time = time.time()
            exec(compile(open(file).read(), "timer", "exec"))
            end = time.time()
            diff = end - start_time
            print(f"{diff}")
            result.append((file, diff))

    return result

class LineWatcher:
    """Class that implements a basic timer.

    Registers the `start` and `stop` methods with the IPython events API.
    """

    def __init__(self):
        """Define the classes start_time parameter."""
        self.start_time = self.start()

    def start(self):
        """Return `time.time`."""
        return time.time()

    def __repr__(self):
        return f"{self.__class__.__name__} {self.start_time}"

    def stop(self):
        """Determine the difference between start time and end time."""
        stop_time = time.time()

        diff = abs(stop_time - self.start_time)
        print("time: {}".format(format_delta(diff)))
        return diff

def load_ipython_extension(ip=None, line_watcher=None):
    """Initialize a `LineWatcher` and register start and stop with IPython."""
    if ip is None:
        ip = get_ipython()
    if ip is None:
        return

    if line_watcher is None:
        line_watcher = LineWatcher()
    ip.events.register("pre_run_cell", line_watcher.start)
    ip.events.register("post_run_cell", line_watcher.stop)

def unload_ipython_extension(ip=None, line_watcher=None):
    if ip is None:
        ip = get_ipython()
    if ip is None:
        return

    if line_watcher is None:
        line_watcher = LineWatcher()
    ip.events.unregister("pre_run_cell", line_watcher.start)
    ip.events.unregister("post_run_cell", line_watcher.stop)


"""
file structure:
flip_labels_and_scans.py
	 scan_directrory - raw scans folder
	 label_directrory - labels folder
	 save_dir_scan - flipped scans folder (where they will be saved)
	 save_dir_labels - flipped labels folder (where they will be saved)

This script flips nii (nifti) labels and scans along the sagittal plane. The plane flipping occurs on can be modified by changing the transformation matrix in the flip3dlabel and flip3dscan functions.

This script assumes the following file naming conventions:
scans: "scanIdentifier_somesuffix.nii"
labels: "scanIdentifier_50um_segmentation_IE-label.nii", the suffix can be modified by altering "label_name" in the "scan_flip_iterator" function.
note that scanIdentifier should be unique.
"""

#imports
import numpy as np
import SimpleITK as sitk
import os

#specify directory of scans you would like to flip
scan_directrory = 'scan_154um'

#specify directory of where labelmaps are
label_directrory = 'lab'

#specify directory where you want to save flipped scans
save_dir_scan = 'scan_save'

#specify directory where you want to save flipped labels
save_dir_labels = 'lab_save'

def get_center(img):
	"""
	This function returns the physical center point of a 3d sitk image
	:param img: The sitk image we are trying to find the center of
	:return: The physical center point of the image
	"""

	width, height, depth = img.GetSize()
	return img.TransformIndexToPhysicalPoint((int(np.ceil(width/2)),
		                          int(np.ceil(height/2)),
		                          int(np.ceil(depth/2))))

def flip3dlabel(img):
	"""
	This function flips the sitk label passeed to it with NN interpolation
	:param img: An sitk labelmap
	:return: The flipped label
	"""

	affineTrans = sitk.AffineTransform(3)
	image_center = get_center(img)
	affineTrans.SetMatrix([-1,0,0,0,1,0,0,0,1])
	affineTrans.SetCenter(image_center)
	flipped = sitk.Resample(img, affineTrans,sitk.sitkNearestNeighbor)
	return flipped

def flip3dscan(img,lab):
	"""
	This function flips the sitk image passeed to it with BSpline interpolation
	:param img: An sitk image
	:param lab: An sitk label associated with the given image - used to maintain alignment
	:return: The flipped image
	"""

	affineTrans = sitk.AffineTransform(3)
	image_center = get_center(lab)
	affineTrans.SetMatrix([-1,0,0,0,1,0,0,0,1])
	affineTrans.SetCenter(image_center)
	interpolator = sitk.sitkBSpline
	flipped = sitk.Resample(img, img, affineTrans,
		     interpolator, -2000)
	return flipped

def label_flip_iterator(file):
	"""
	This function is called each time a label is flipped. Naming and saving is done here.
	:param file: filename of label
	"""

	prefix = file.split("_")[0] #get the sample prefix IE '1932L'
	name_without_filetype = file.split(".nii")[0] #file name before the extension (.nii)
	newname = name_without_filetype+"_flipped.nii"

	lab = sitk.ReadImage(label_directrory+'/'+file)
	flipped_lab = flip3dlabel(lab)
	sitk.WriteImage(flipped_lab,save_dir_labels+"/"+newname)#labels are saved with _flipped appended to their original names

def scan_flip_iterator(file):
	"""
	This function is called each time a scan is flipped. Naming and saving is done here.
	:param file: filename of scan
	"""

	prefix = file.split("_")[0] #get the scan prefix IE '1932L'
	name_without_filetype = file.split(".nii")[0] #everything before the extension (.nii)
	newname = name_without_filetype+"_flipped.nii"
	label_name = prefix+"_50um_segmentation_IE-label_flipped.nii" #labels corresponding to scans need this naming convention following prefix

	im = sitk.ReadImage(scan_directrory+"/"+file)
	lab = sitk.ReadImage(save_dir_labels+'/'+label_name)
	flipped_im = flip3dscan(im,lab) #flip the image with respect to its already flipped label
	sitk.WriteImage(flipped_im,save_dir_scan+"/"+newname) #scans are saved with _flipped appended to their original names

dir=os.listdir(label_directrory)
for i in range(0,len(dir)): #iterate through the directory of labels
	label_flip_iterator(dir[i])

dir=os.listdir(scan_directrory)
for i in range(0,len(dir)): #iterate through the directory of raw scans
	scan_flip_iterator(dir[i])


import json

# Set file paths
basePath = 'D:\\NTCIR-12_MathIR_arXiv_Corpus\\'
inputPath = basePath + "output_FeatAna\\"
index_file = 'inverse_semantic_index_formula_catalog(physics_all).json'
#basePath = 'D:\\NTCIR-12_MathIR_Wikipedia_Corpus\\'
#inputPath = basePath + "output_RE\\"
#index_file = 'inverse_semantic_index_formula_catalog(Wikipedia).json'

# Load inverse index
with open(inputPath + index_file,'r',encoding='utf8') as f:
    formula_index = json.load(f)

# Load example queries
with open('../examples_list/formula_examples.json', 'r', encoding='utf8') as f:
    example_queries = json.load(f)

results = {}
for example_query in example_queries:
    GoldID = example_query['GoldID']
    FormulaName = example_query['formula_name']
    # retrieve only results that are common in all query word results
    common_results = {}
    for query_word in FormulaName.split():
        try:
            for formula in formula_index[query_word].items():
                try:
                    common_results[formula[0]] += 1
                except:
                    common_results[formula[0]] = 1
        except:
            pass
    ranking = {}
    for common_result in common_results.items():
        if True: #common_result[1] == len(FormulaName.split()):
            for query_word in FormulaName.split():
                try:
                    ranking[common_result[0]] += formula_index[query_word][common_result[0]]
                except:
                    try:
                        ranking[common_result[0]] = formula_index[query_word][common_result[0]]
                    except:
                        pass

    result = {k: v for k, v in sorted(ranking.items(), key=lambda item: item[1],reverse=True)}
    results[GoldID] = (FormulaName,result)

# output to csv
csv_list = []
csv_list.append("GoldID\tName\tFormula\t(Score,Rank)\tDCG\tnDCG\n")
for result in results.items():
    # display only first hits or ranking cutoff
    displayed = False
    counter = 0
    for formula in result[1][1].items():
        if counter < 10: # True: #displayed == False:
            csv_list.append(result[0] + "\t" + result[1][0] + "\t"
                            + formula[0].replace("\t","").replace("\n","") + "\t\t\t\n")
            displayed = True
            counter += 1

with open("inverse_formula_index_results.csv", 'w', encoding='utf8') as f:
    f.writelines(csv_list)

print("end")


"""

Source: https://www.geeksforgeeks.org/dynamic-programming-set-13-cutting-a-rod/

Given a rod of length n inches and an array of prices that contains prices of all pieces of size smaller than n.
Determine the maximum value obtainable by cutting up the rod and selling the pieces.
For example, if length of the rod is 8 and the values of different pieces are given as following,
then the maximum obtainable value is 22 (by cutting in two pieces of lengths 2 and 6)

length   | 1   2   3   4   5   6   7   8
--------------------------------------------
price    | 1   5   8   9  10  17  17  20

And if the prices are as following, then the maximum obtainable value is 24 (by cutting in eight pieces of length 1)

length   | 1   2   3   4   5   6   7   8
--------------------------------------------
price    | 3   5   8   9  10  17  17  20

"""

priceDict = {1:3, 2:5, 3:8, 4:9, 5:10, 6:17, 7:17, 8:20}
solutions = {}

def optOfN(n):
    if n in solutions.keys():
        return solutions[n]
    if n == 1:
        solutions[1] = priceDict[1]
        return solutions[1]

    opt = [priceDict[n]]
    for i in range(1,int(n/2)+1):
        opt.append(optOfN(i)+optOfN(n-i))

    solutions[n] = max(opt)
    return solutions[n]

optOfN(8)
print(solutions)


import sys
input = sys.stdin.readline

dx = [1, 0]
dy = [0, 1]

T = int(input())
for tc in range(T):
    M, N, K = map(int, input().split())

    original = []
    stack = []
    for _ in range(K):
        x, y = map(int, input().split())
        original.append((x, y))
        stack.append((x, y))

    for nx, ny in original:
        for i in range(2):
            if 0 <= nx+dx[i] < M and 0 <= ny+dy[i] < N:
                if (nx+dx[i], ny+dy[i]) in original:
                    if (nx+dx[i], ny+dy[i]) in stack:
                        idx = stack.index((nx+dx[i], ny+dy[i]))
                        stack.pop(idx)
                    else:
                        if (nx, ny) in stack:
                            idx = stack.index((nx, ny))
                            stack.pop(idx)
    print(stack)
    print(len(stack))


import numpy as np
from scipy.spatial.distance import cdist
import matplotlib.pyplot as plt

def has_converged(centers, new_centers):
    return set([tuple(a) for a in centers]) == set([tuple(a) for a in new_centers])

def kmeans(X, K):
    # centroids = X[np.random.choice(X.shape[0], K, replace=False)]
    centroids = np.array([[1.0, 1.0], [5.0, 7.0]])
    it = 0
    while True:
        it += 1
        D = cdist(X, centroids)
        labels = np.argmin(D, axis=1)
        new_centroids = np.zeros((K, X.shape[1]))
        for k in range(K):
            new_centroids[k, :] = np.mean(X[labels == k, :], axis=0)
        display(X, K, labels)
        plt.show()
        if has_converged(centroids, new_centroids):
            break

        centroids = new_centroids

    return labels, centroids

def display(X, K, labels):
    for i in range(K):
        X0 = X[labels == i, :]
        plt.plot(X0[:, 0], X0[:, 1], '.')

def error(X, K, labels):
    sum = 0
    for i in range(K):
        X0 = X[labels == i, :]
        sum += np.std(X0)
    print(sum / K)

def random_data():
    for i in range(6):
        mean = 200 * np.random.random_sample((1, 2))
        X0 = np.random.multivariate_normal(mean[0], [[10, 0], [0, 10]], np.random.randint(20, 50))
        if i == 0:
            X = X0
        else:
            X = np.concatenate((X, X0))
    return X

from sklearn.cluster import KMeans

A = np.array([[1.0, 1.5, 3.0, 5.0, 3.5, 4.5, 3.5]])
B = np.array([[1.0, 2.0, 4.0, 7.0, 5.0, 5.0, 4.5]])
X = np.append(A.T, B.T, axis=1)
# X = random_data()
for K in range(2, 10):
    (labels, centroids) = kmeans(X, K)
    display(X, K, labels)
    plt.show()
    error(X, K, labels)
    cls = KMeans(n_clusters=K, random_state=0)
    cls.fit(X)
    lbl = cls.labels_
    display(X, K, lbl)
    plt.show()


""" Internet Validators

- ValidateEmail
- ValidateIP
- ValidateURL
"""

import socket
import re
from email_validator import validate_email, EmailNotValidError
from flask_validator import Validator

class ValidateEmail(Validator):
    """ Validate Email type.

    Check if the new value is a valid e-mail.
    Using this library to validate https://github.com/JoshData/python-email-validator

    Args:
        field: SQLAlchemy column to validate
        allow_null: (bool) Allow null values
        allow_smtputf8: (bool) Set to False to prohibit internationalized addresses that would require the SMTPUTF8.
        check_deliverability: (bool) Set to False to skip the domain name resolution check.
        allow_empty_local (bool) Set to True to allow an empty local part (i.e. @example.com),
            e.g. for validating Postfix aliases.
        allow_null: (bool) Allow null values
        throw_exception: (bool) Throw a ValidateError if the validation fails

    """

    allow_smtputf8 = True
    check_deliverability = True
    allow_empty_local = False

    def __init__(self, field, allow_smtputf8=True,check_deliverability=True, allow_empty_local=False,
                 allow_null=True, throw_exception=False, message=None):

        self.allow_smtputf8 = allow_smtputf8
        self.check_deliverability = check_deliverability
        self.allow_empty_local = allow_empty_local

        Validator.__init__(self, field, allow_null, throw_exception, message)

    def check_value(self, value):
        try:
            validate_email(value,
                           allow_smtputf8=self.allow_smtputf8,
                           check_deliverability=self.check_deliverability,
                           allow_empty_local=self.allow_empty_local)
            return True
        except EmailNotValidError:
            return False

class ValidateIP(Validator):
    """ Validate Regex

    Compare a value against a regular expresion

    Args:
        field: SQLAlchemy column to validate
        ipv6: Match against IPV6
        allow_null: (bool) Allow null values
        throw_exception: (bool) Throw a ValidateError if the validation fails
    """
    ipv6 = None

    def __init__(self, field, ipv6=False, allow_null=True, throw_exception=False, message=None):
        self.ipv6 = ipv6

        Validator.__init__(self, field, allow_null, throw_exception, message)

    def check_value(self, value):
        try:
            if not self.ipv6:
                socket.inet_pton(socket.AF_INET, value)
            else:
                socket.inet_pton(socket.AF_INET6, value)

            return True
        except socket.error:
            return False

class ValidateURL(Validator):
    """ Validate URL

    Check if the values is a valid URL

    Args:
        field: SQLAlchemy column to validate
        allow_null: (bool) Allow null values. Default True
        throw_exception: (bool) Throw a ValidateError if the validation fails

    """

    regex = r'^[a-z]+://(?P<host>[^/:]+)(?P<port>:[0-9]+)?(?P<path>\/.*)?$'

    def check_value(self, value):
        if re.match(self.regex, value):
            return True
        else:
            return False


# Imports
from math import pi
pi * 71 / 223
from math import sin
sin(pi/2)

# Function values
max
max(3, 4)
f = max
f
f(3, 4)
max = 7
f(3, 4)
f(3, max)
f = 2
# f(3, 4)

# User-defined functions
from operator import add, mul
add(2, 3)
mul(2, 3)

def square(x):
    return mul(x, x)

square(21)


import Adafruit_DHT
from main.SQL import TempHandler
import time

class Temps():
    def __init__(self):
        self.sensor = Adafruit_DHT.DHT11
        self.pin = 17
        self.humidity = 0
        self.temperature = 0

    def getDHT(self):
        self.humidity, self.temperature = Adafruit_DHT.read_retry(self.sensor, self.pin)
        dic ={}
        if self.humidity is not None and self.temperature is not None:
            dic['temperature'] = self.temperature
            dic['humidity'] = self.humidity
            temp = TempHandler.Temp(temperature=self.temperature, humidity=self.humidity)
            TempHandler.insert(temp)
            return dic
        else:
            return 'Failed to get reading. Try again!'


"""
题目介绍：剑指 Offer 48. 最长不含重复字符的子字符串
请从字符串中找出一个最长的不包含重复字符的子字符串，计算该最长子字符串的长度
"""
def length_of_longest_substring(s):
    dic = {}
    res = tmp = 0
    for j in range(len(s)):
        # 获取索引i
        i = dic.get(s[j], -1)
        # 更新哈希表
        dic[s[j]] = j
        tmp = tmp + 1 if tmp < j - i else j - i
        res = max(res, tmp)
    return res

s='abcdefgabcdabcikghjk'
print(length_of_longest_substring(s))

def singleNumber(nums):
    counts = [0] * 32
    for num in nums:
        for j in range(32):
            counts[j] += num & 1
            num >>= 1
    res, m = 0, 3
    for i in range(32):
        res <<= 1
        res |= counts[31 - i] % m
    return res if counts[31] % m == 0 else ~( res ^ 0xffffffff)


import datetime as dt
import matplotlib.pyplot as plt
import numpy as np
import os

class HobsHeader(object):
    sim_head = '"SIMULATED EQUIVALENT"'
    obs_head = '"OBSERVED VALUE"'
    obs_name = '"OBSERVATION NAME"'
    date = 'DATE'
    dyear = 'DECIMAL_YEAR'

    header = {sim_head: None,
              obs_head: None,
              obs_name: None,
              date: None,
              dyear: None}

class HobsOut(dict):
    """
    Reads output data from Hobs file and prepares it for post processing.

    Class sets observations to an ordered dictionary based on observation name

    If observation name is consistant for a site, a time series is created
    for plotting!

    Parameters
    ----------
    filename : str
        hobs filename
    strip_after : str
        flag to indicate a character to strip the hobs label after for
        grouping wells.

        Example:  OBS_1
                  OBS_2
        strip_after could be set to "_" and then all OBS observations will
        be stored under the OBS key. This is extremely useful for plotting
        and calculating statistics

    """

    def __init__(self, filename, strip_after=""):
        super(HobsOut, self).__init__()
        self.name = filename
        self._strip_after = strip_after
        self._dataframe = None
        self.__read_hobs_output()

    def __read_hobs_output(self):
        """
        Method to read a hobs output file. Dynamically sets header information
        and reads associated values.

        Sets values to HobsOut dictionary
        """

        with open(self.name) as hobout:
            for ix, line in enumerate(hobout):
                if ix == 0:
                    self.__set_header(line)

                else:
                    self.__set_dictionary_values(line)

    def __set_dictionary_values(self, line):
        """
        Method to set incoming hobs line to dictionary data values

        Args:
            line: (str)
        """
        t = line.strip().split()
        obsname = t[HobsHeader.header[HobsHeader.obs_name]]
        dict_name = obsname
        if self._strip_after:
            dict_name = obsname.split(self._strip_after)[0]
        simval = float(t[HobsHeader.header[HobsHeader.sim_head]])
        obsval = float(t[HobsHeader.header[HobsHeader.obs_head]])
        residual = simval - obsval
        date = self.__set_datetime_object(t[HobsHeader.header[HobsHeader.date]])
        decimal_date = float(t[HobsHeader.header[HobsHeader.dyear]])

        if dict_name in self:
            self[dict_name]['simval'].append(simval)
            self[dict_name]['obsval'].append(obsval)
            self[dict_name]['date'].append(date)
            self[dict_name]['decimal_date'].append(decimal_date)
            self[dict_name]['residual'].append(residual)
            self[dict_name]["obsname"].append(obsname)
        else:
            self[dict_name] = {"obsname": [obsname], "date": [date],
                               "decimal_date": [decimal_date],
                               "simval": [simval], "obsval": [obsval],
                               "residual": [residual]}

    def __set_header(self, line):
        """
        Reads header line and sets header index

        Parameters
        ----------
        line : str
            first line of the HOB file

        """
        n = 0
        s = ""

        for i in line:
            s += i
            if s in HobsHeader.header:
                HobsHeader.header[s] = n
                n += 1
                s = ""

            elif s in (" ", "\t", "\n"):
                s = ""

            else:
                pass

        for key, value in HobsHeader.header.items():
            if value is None:
                raise AssertionError("HobsHeader headings must be updated")

    def __set_datetime_object(self, s):
        """
        Reformats a string of YYYY-mm-dd to a datetime object

        Parameters
        ----------
        s : str
            string of YYYY-mm-dd

        Returns
        -------
            datetime.date
        """
        return dt.datetime.strptime(s, "%Y-%m-%d")

    def __get_date_string(self, date):
        """

        Parmaeters
        ----------
            date: datetime.datetime object

        Returns
        -------
            string

        """
        return date.strftime("%Y/%m/%d")

    @property
    def obsnames(self):
        """
        Return a list of obsnames from the HobsOut dictionary
        """
        return self.keys()

    def to_dataframe(self):
        """
        Method to get a pandas dataframe object of the
        HOBs data.

        Returns
        -------
            pd.DataFrame
        """
        import pandas as pd

        if self._dataframe is None:
            df = None
            for hobsname, d in self.items():
                t = pd.DataFrame(d)
                if df is None:
                    df = t
                else:
                    df = pd.concat([df, t], ignore_index=True)
            self._dataframe = df

        return self._dataframe

    def get_sum_squared_errors(self, obsname):
        """
        Returns the sum of squared errors from the residual

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            float: sum of square error
        """
        return sum([i**2 for i in self[obsname]['residual']])

    def get_rmse(self, obsname):
        """
        Returns the RMSE from the residual

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            float: rmse
        """
        return np.sqrt(np.mean([i**2 for i in self[obsname]['residual']]))

    def get_number_observations(self, obsname):
        """
        Returns the number of observations for an obsname

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            int
        """
        return len(self[obsname]['simval'])

    def get_maximum_residual(self, obsname):
        """
        Returns the datetime.date and maximum residual value

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            tuple: (datetime.date, residual)
        """
        data = self[obsname]['residual']
        index = data.index(max(data))
        date = self[obsname]['date'][index]
        return date, max(data)

    def get_minimum_residual(self, obsname):
        """
        Returns the datetime.date, minimum residual value

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            tuple: (datetime.date, residual)
        """
        data = self[obsname]['residual']
        index = data.index(min(data))
        date = self[obsname]['date'][index]
        return date, min(data)

    def get_mean_residual(self, obsname):
        """
        Returns the datetime.date, minimum residual value

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            tuple: (datetime.date, residual)
        """
        data = self[obsname]['residual']
        return np.mean(data)

    def get_median_residual(self, obsname):
        """
        Returns the datetime.date, minimum residual value

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            tuple: (datetime.date, residual)
        """
        data = self[obsname]['residual']
        return np.median(data)

    def get_maximum_residual_heads(self, obsname):
        """
        Returns the datetime.date, simulated, and observed
        heads at the maximum residual value

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            tuple: (datetime.date, simulated head, observed head)
        """
        resid = self[obsname]['residual']
        index = resid.index(max(resid))
        observed = self[obsname]['obsval'][index]
        simulated = self[obsname]['simval'][index]
        date = self[obsname]['date'][index]
        return date, simulated, observed

    def get_minimum_residual_heads(self, obsname):
        """
        Returns the datetime.date, simulated, and observed
        heads at the maximum residual value

        Parameters
        ----------
        obsname : str
            observation name

        Returns
        -------
            tuple: (datetime.date, simulated head, observed head)
        """
        resid = self[obsname]['residual']
        index = resid.index(min(resid))
        observed = self[obsname]['obsval'][index]
        simulated = self[obsname]['simval'][index]
        date = self[obsname]['date'][index]
        return date, simulated, observed

    def get_residual_bias(self, filter=None):
        """
        Method to determine the bias of measurements +-
        by checking the residual. Returns fraction of residuals
        > 0.

        Parameters
        ----------
        filter: (str, list, tuple, or function)
            filtering criteria for writing statistics.
            Function must return True for filter out, false to use

        Returns
        -------
            (float) fraction of residuals greater than zero
        """
        nobs = 0.
        ngreaterzero = 0.

        for obsname, meta_data in self.items():
            if self.__filter(obsname, filter):
                continue

            residual = np.array(meta_data['residual'])
            rgreaterzero = sum((residual > 0))

            nobs += residual.size
            ngreaterzero += rgreaterzero

        try:
            bias = ngreaterzero / nobs
        except ZeroDivisionError:
            raise ZeroDivisionError("No observations found!")

        return bias

    def write_dbf(self, dbfname, filter=None):
        """
        Method to write a dbf file from a the HOBS dictionary

        Parameters
        ----------
        dbfname : str
            dbf file name
        filter: (str, list, tuple, or function)
            filtering criteria for writing statistics.
            Function must return True for filter out, false for write to file

        """
        import shapefile
        data = []
        for obsname, meta_data in self.items():

            if self.__filter(obsname, filter):
                continue

            for ix, val in enumerate(meta_data['simval']):
                data.append([obsname,
                             self.__get_date_string(meta_data['date'][ix]),
                             val,
                             meta_data['obsval'][ix],
                             meta_data['residual'][ix]])

        try:
            # traps for pyshp 1 vs. pyshp 2
            w = shapefile.Writer(dbf=dbfname)
        except Exception:
            w = shapefile.Writer()

        w.field("HOBSNAME", fieldType="C")
        w.field("HobsDate", fieldType="D")
        w.field("HeadSim", fieldType='N', decimal=8)
        w.field("HeadObs", fieldType="N", decimal=8)
        w.field("Residual", fieldType="N", decimal=8)

        for rec in data:
            w.record(*rec)

        try:
            w.save(dbf=dbfname)
        except AttributeError:
            w.close()

    def write_min_max_residual_dbf(self, dbfname, filter=None):
        """
        Method to write a dbf of transient observations
        using observation statistics

        Parameters
        ----------
        dbfname : str
            dbf file name
        filter: (str, list, tuple, or function)
            filtering criteria for writing statistics.
            Function must return True for filter out, false for write to file

        """
        import shapefile
        data = []
        for obsname, meta_data in self.items():
            if self.__filter(obsname, filter):
                continue

            max_date, resid_max = self.get_maximum_residual(obsname)
            min_date, resid_min = self.get_minimum_residual(obsname)
            simval_max, obsval_max = self.get_maximum_residual_heads(obsname)[1:]
            simval_min, obsval_min = self.get_minimum_residual_heads(obsname)[1:]

            data.append([obsname,
                         self.get_number_observations(obsname),
                         self.__get_date_string(max_date), resid_max,
                         self.__get_date_string(min_date), resid_min,
                         simval_max, obsval_max, simval_min, obsval_min])

        try:
            # traps for pyshp 1 vs. pyshp 2
            w = shapefile.Writer(dbf=dbfname)
        except Exception:
            w = shapefile.Writer()

        w.field("HOBSNAME", fieldType="C")
        w.field("FREQUENCY", fieldType="N")
        w.field("MaxDate", fieldType="C")
        w.field("MaxResid", fieldType='N', decimal=8)
        w.field("MinDate", fieldType="C", decimal=8)
        w.field("MinResid", fieldType="N", decimal=8)
        w.field("MaxHeadSim", fieldType="N", decimal=8)
        w.field("MaxHeadObs", fieldType="N", decimal=8)
        w.field("MinHeadSim", fieldType="N", decimal=8)
        w.field("MinHeadObs", fieldType="N", decimal=8)

        for rec in data:
            w.record(*rec)

        try:
            w.save(dbf=dbfname)
        except AttributeError:
            w.close()

    def __filter(self, obsname, filter):
        """
        Boolean filetering method, checks if observation name
        is in the filter.

        Parameters
        ----------
        obsname : str
            observation name
        filter: (str, list, tuple, or function)
            filtering criteria for writing statistics.
            Function must return True for filter out, false for write to file

        Returns
        -------
            bool: True if obsname in filter

        """
        if filter is None:
            return False

        elif isinstance(filter, list) or isinstance(filter, tuple):
            if obsname in list:
                return True

        elif isinstance(filter, str):
            if obsname == filter:
                return True

        elif callable(filter):
            if filter(obsname):
                return True

        else:
            raise Exception("Filter is not an appropriate type")

        return False

    def write_summary_statistics_csv(self, csvname, filter=None):
        """
        Method to write summary calibration statistics to a
        CSV file for analysis and reports

        Parameters
        ----------
        csvname : str
            csv file name
        filter: (str, list, tuple, or function)
            filtering criteria for writing statistics.
            Function must return True for filter out, false for write to file

        """
        data = []
        header = ["Well name", "Average", "Median",
                  "Minimum", "Maximum", "RMSE ft", "Frequency"]

        for obsname, meta_data in sorted(self.items()):

            if self.__filter(obsname, filter):
                continue

            resid_mean = self.get_mean_residual(obsname)
            resid_median = self.get_median_residual(obsname)
            resid_max = self.get_maximum_residual(obsname)[-1]
            resid_min = self.get_minimum_residual(obsname)[-1]
            rmse = self.get_rmse(obsname)
            frequency = self.get_number_observations(obsname)
            data.append((obsname, resid_mean, resid_median,
                         resid_min, resid_max, rmse, frequency))

        data = np.array(data, dtype=[('id', 'O'), ('mean', float),
                                     ('med', float), ('min', float),
                                     ('max', float), ('rmse', float),
                                     ('num', np.int)])

        with open(csvname, "w") as foo:
            foo.write(",".join(header) + "\n")
            np.savetxt(foo, data, fmt="%15s,%.2f,%2f,%2f,%2f,%2f,%d")

    def plot(self, obsname, *args, **kwargs):
        """
        Plotting functionality from the hobs dictionary

        Parameters
        ----------
        obsname: str
            hobs package observation name
        *args: matplotlib args
        **kwargs: matplotlib kwargs

        Returns
        -------
            matplotlib.pyplot.axes object

        """
        simulated = True
        if "observed" in kwargs:
            simulated = False
            kwargs.pop('observed')

        observed = True
        if "simulated" in kwargs:
            observed = False
            kwargs.pop('simulated')

        if obsname not in self:
            raise AssertionError("Obsname {}: not valid".format(obsname))

        axes = False
        if 'ax' in kwargs:
            ax = kwargs.pop('ax')
            axes = True

        if not axes:
            ax = plt.subplot(111)

        obsval = self[obsname]['obsval']
        simval = self[obsname]['simval']
        date = self[obsname]['date']

        if observed:
            kwargs['label'] = "Observed"
            kwargs['color'] = 'r'

            ax.plot(date, obsval, *args, **kwargs)

        if simulated:
            kwargs['label'] = "Simulated"
            kwargs['color'] = 'b'

            ax.plot(date, simval, *args, **kwargs)

        return ax

    def plot_measured_vs_simulated(self, filter=None, **kwargs):
        """
        Plots measured vs. simulated data along a 1:1 profile.

        Parameters
        ----------
        filter: (str, list, tuple, or function)
            filtering criteria for writing statistics.
            Function must return True for filter out, false for write to file
        **kwargs: matplotlib.pyplot plotting kwargs

        Returns
        -------
            matplotlib.pyplot.axes object

        """
        axes = plt.subplot(111)

        for obsname, meta_data in self.items():

            if self.__filter(obsname, filter):
                continue

            simulated = meta_data['simval']
            observed = meta_data['obsval']

            axes.plot(observed, simulated, 'bo', markeredgecolor='k')

        return axes

    def plot_simulated_vs_residual(self, filter=None,
                                   histogram=False, **kwargs):
        """
        Creates a matplotlib plot of simulated heads vs residual

        Parameters
        ----------
        filter: (str, list, tuple, or function)
            filtering criteria for writing statistics.
            Function must return True for filter out, false for write to file
        histogram: (bool)
            Boolean variable that defines either a scatter plot (False)
            or a histogram (True) of residuals

        **kwargs: matplotlib.pyplot plotting kwargs

        Returns
        -------
            matplotlib.pyplot.axes object

        """
        axes = plt.subplot(111)

        if not histogram:
            for obsname, meta_data in self.items():

                if self.__filter(obsname, filter):
                    continue

                residual = meta_data['residual']
                observed = meta_data['obsval']

                axes.plot(observed, residual, 'bo', markeredgecolor="k")

        else:
            bins = np.arange(-25, 26, 5)

            d = {}
            for ix, abin in enumerate(bins):
                frequency = 0
                for obsname, meta_data in self.items():
                    if self.__filter(obsname, filter):
                        continue

                    for residual in meta_data['residual']:
                        if ix == 0:
                            if residual < abin:
                                frequency += 1

                        elif ix == (len(bins) - 1):
                            if residual > abin:
                                frequency += 1

                        else:
                            if bins[ix - 1] <= residual < abin:
                                frequency += 1

                if ix == 0:
                    name = "Less than {}".format(abin)

                elif ix == (len(bins) - 1):
                    name = "Greater than {}".format(abin)

                else:
                    name = "{} to {}".format(bins[ix - 1] + 1, abin)

                d[ix + 1] = {'name': name,
                             'frequency': frequency}

            tick_num = []
            tick_name = []
            for index, meta_data in sorted(d.items()):
                axes.bar(index, meta_data['frequency'], width=0.8,
                         **kwargs)
                tick_num.append(index)
                tick_name.append(meta_data['name'])

            plt.xticks(tick_num, tick_name, rotation=45, fontsize=10)
            plt.xlim([0.5, len(tick_num) + 1])
            plt.subplots_adjust(left=0.12, bottom=0.22,
                                right=0.90, top=0.90,
                                wspace=0.20, hspace=0.20)
            plt.ylabel("Frequency")

        return axes

if __name__ == "__main__":
    ws = r'C:\Users\jlarsen\Desktop\Lucerne\Lucerne_OWHM\V0_initial_from_MODOPTIM\output'
    hobs_name = "hobs.out"

    tmp = HobsOut(os.path.join(ws, hobs_name))
    tmp.plot("04N01W01R04S", "o-")
    plt.legend(loc=0, numpoints=1)
    plt.show()
    print('break')


import array
import struct
import sys
from collections import namedtuple

import plotly.express as px
import numpy as np
from scipy.ndimage import uniform_filter1d
from statsmodels.nonparametric.smoothers_lowess import lowess
import matplotlib.pyplot as plt
from math import degrees, atan
import scipy.signal

TYPE_DIGITAL = 0
TYPE_ANALOG = 1
expected_version = 0

AnalogData = namedtuple('AnalogData', ('begin_time', 'sample_rate', 'downsample', 'num_samples', 'samples'))

def parse_analog(f):
    # Parse header
    identifier = f.read(8)
    if identifier != b"<SALEAE>":
        raise Exception("Not a saleae file")

    version, datatype = struct.unpack('=ii', f.read(8))

    if version != expected_version or datatype != TYPE_ANALOG:
        raise Exception("Unexpected data type: {}".format(datatype))

    # Parse analog-specific data
    begin_time, sample_rate, downsample, num_samples = struct.unpack('=dqqq', f.read(32))

    # Parse samples
    samples = array.array("f")
    samples.fromfile(f, num_samples)

    return AnalogData(begin_time, sample_rate, downsample, num_samples, samples)

if __name__ == '__main__':
    times = []
    volts = []
    anchor = 0
    filename = sys.argv[1]
    print("Opening " + filename)

    with open(filename, 'rb') as f:
        data = parse_analog(f)

    # Print out all analog data
    print("Begin time: {}".format(data.begin_time))
    print("Sample rate: {}".format(data.sample_rate))
    print("Downsample: {}".format(data.downsample))
    print("Number of samples: {}".format(data.num_samples))
    j = 0
    for idx, voltage in enumerate(data.samples):
        sample_num = idx * data.downsample
        #thing/(thing/sec) = thing*(sec/thing) = sec
        time = data.begin_time + (float(sample_num) / data.sample_rate)
        times.append(time)
        volts.append(min(voltage,1.3345))
        j = j + 1

    volts = scipy.ndimage.median_filter(volts, int((data.sample_rate/data.downsample)*.002)+1)

    #volts = uniform_filter1d(volts, size=int((data.sample_rate/data.downsample)*.002))

    """
    filtered = lowess(volts, times, frac=0.0005)
    plt.plot(filtered[:, 0], filtered[:, 1], 'r-', linewidth=3)
    plt.show()
    """

    upper_bound = lower_bound = volts[0]
    for i in range(0,int(data.num_samples*.2)):
        upper_bound = max(upper_bound, volts[i])
        lower_bound = min(lower_bound, volts[i])

    v_noise = .0
    sample_size = .3
    slope_range = int(data.num_samples*.05)

    temp_threshold = 0.0
    angle_threshold = 30.0
    tslope_range = 10
    """
    for s in range(100,11000,100):
        i = 0
        while i < int(data.num_samples*sample_size):
            l_b = max(i-s,0)
            r_b = min(i+s,data.num_samples)
            v_noise = volts[r_b] - volts[l_b]
            if temp_threshold <= abs(degrees(atan(v_noise/((times[r_b]-times[l_b]))))):
                temp_threshold = abs(degrees(atan(v_noise/((times[r_b]-times[l_b])))))
                print("({},{})({},{})".format(times[l_b], volts[l_b], times[r_b], volts[r_b]))
            i = i + 1
        print("Temp Threshold: {}".format(temp_threshold))
        if temp_threshold < angle_threshold:
            angle_threshold = temp_threshold
            slope_range = s
            """
    print("Angle Threshold: {}".format(angle_threshold))
    start = 0
    state = 0
    #red is horizontal, b is rise, green is fall
    colors = ['r','b','g']
    i = 1
    angle_threshold = 1
    slope_range = int(data.num_samples*.002)
    while i < data.num_samples:
        l_b = max(i-slope_range,0)
        r_b = min(i+slope_range,data.num_samples-1)
        v_noise = volts[r_b] - volts[l_b]
        angle = degrees(atan(v_noise/((times[r_b]-times[l_b]))))
        if abs(angle) <= angle_threshold and state != 0:
            #print("Horizontal line detected: {}\n".format(angle))
            plt.plot(times[start:i], volts[start:i], colors[state])
            state = 0
            start = i
        elif angle > angle_threshold and state != 1:
            #print("Rise detected: {}\n".format(angle))
            plt.plot(times[start:i], volts[start:i], colors[state])
            state = 1
            start = i
        elif angle < -angle_threshold and state != 2:
            #print("Descent detected: {}\n".format(angle))
            plt.plot(times[start:i], volts[start:i], colors[state])
            state = 2
            start = i
        i = i + 1

    plt.plot(times[start:i], volts[start:i], colors[state])
    #plt.plot(times, volts)
    plt.show()


import numpy as np
import tensorflow as tf
import cv2

def colormap_jet(img):
    color_image = cv2.applyColorMap(np.uint8(img), cv2.COLORMAP_JET)
    return color_image

def color_disparity(disparity):
    with tf.variable_scope('color_disparity'):
        batch_size = disparity.shape[0]
        color_maps = []
        for i in range(batch_size):
            color_disp = tf.py_func(colormap_jet, [-disparity[i]], tf.uint8)
            color_maps.append(color_disp)
        color_batch = tf.stack(color_maps, axis=0)
        return color_batch

def count_text_lines(file_path):
    f = open(file_path, 'r')
    lines = f.readlines()
    f.close()
    return len(lines)


#condig=utf-8

###二叉树
#树节点
class Node(object):
    def __init__(self,elem=-1, lchild =None,rchild = None):
        #节点的值
        self.elem = elem
        #左节点
        self.lchild = lchild
        #右节点
        self.rchild = rchild
#树
class Tree(object):
    def __init__(self):
        self.root = Node()
        self.myQueue = []
    def add(self,elem):
        node = Node(elem)
        if self.root.elem == -1:  # 如果树是空的，则对根节点赋值
            self.root = node
            self.myQueue.append(self.root)
        else:
            treeNode = self.myQueue[0] #为此节点的左右子节点赋值
            if treeNode.lchild == None:
                treeNode.lchild = node
                self.myQueue.append(treeNode.lchild)
            else:
                print(list(f.elem for f in self.myQueue))
                treeNode.rchild=node
                self.myQueue.append(treeNode.rchild)
                self.myQueue.pop(0)  # 如果该结点存在右子树，将此结点丢弃。

    ###先序遍历
    def front_digui(self,root):
        if root == None:
            return
        print(root.elem)
        self.front_digui(root.lchild)
        self.front_digui(root.rchild)

    ###递归中序遍历
    def middle_digui(self,root):
        if root == None:
            return
        self.middle_digui(root.lchild)
        print(root.elem)
        self.middle_digui(root.rchild)

    ###递归后序遍历
    def lafter_digui(self,root):
        if root == None:
            return
        self.middle_digui(root.lchild)
        self.middle_digui(root.rchild)
        print(root.elem)
    ###队列层次遍历
    def level_queue(self,root):
        if root == None:
            return
        myQueue=[]
        node = root
        myQueue.append(node)
        while myQueue:
            node=myQueue.pop(0)
            print(node.elem)
            if node.lchild != None:
                myQueue.append(node.lchild)
            if node.rchild != None:
                myQueue.append(node.rchild)
    ###堆栈先序遍历

    def front_stack(self,root):
        if root == None:
            return
        myStack = []
        node = root
        while node or myStack:
            while node:
                print(node.elem)
                myStack.append(node)
                node = node.lchild
            node = myStack.pop()
            node =  node.rchild

        ###堆栈中序遍历
        def middle_stack(self, root):
            if root == None:
                return
            myStack = []
            node = root
            while node or myStack:
                while node:   #从根节点开始，一直找它的左子树
                    print(node.elem)
                    myStack.append(node)
                    node = node.lchild
                node = myStack.pop()  #while结束表示当前节点node为空，即前一个节点没有左子树了
                node = node.rchild   #开始查看它的右子树

        ##堆栈后序遍历
        def later_stack(self,root):
            if root == None:
                return
            myStack1 = []
            myStack2 = []
            node = root
            myStack1.append(node)
            while myStack1:  #这个while循环的功能是找出后序遍历的逆序，存在myStack2里面
                node = myStack1.pop()
                if node.lchild:
                    myStack1.append(node.lchild)
                if node.rchild:
                    myStack1.append(node.rchild)
                myStack2.append(node)
            while myStack2:    #将myStack2中的元素出栈，即为后序遍历次序
                print(myStack2.pop().elem)

if __name__ == '__main__':
    pass
    # """主函数"""
    # elems = range(10)           #生成十个数据作为树节点
    # tree = Tree()          #新建一个树对象
    # for elem in elems:
    #     tree.add(elem)           #逐个添加树的节点
    #
    #
    # print ('\n\n递归实现先序遍历:')
    # # tree.front_digui(tree.root)
    # # tree.level_queue(tree.root)
    # # print(tree.root.elem)
    # # print(list(f.elem for f in tree.myQueue))
    # tree.middle_digui(tree.root)


##
# The model uses elements from both the Transformer Encoder as introduced in
# “Attention is All You Need” (https://arxiv.org/pdf/1706.03762.pdf) and the
# Message Passing Neural Network (MPNN) as described in "Neural Message Passing
# for Quantum Chemistry" paper (https://arxiv.org/pdf/1704.01212.pdf) .
#
# The overall architecture most closely resembles the Transformer Encoder with
# stacked encoder blocks and layers connected through residual connections with
# layer norm. In this case however the encoder blocks are build up of two
# message passing layers, followed by three different types of attention layers
# with a final pointwise feed-forward network.
#
# Both message passing layers use a slightly modified version of the edge
# networks as detailed in the MPNN paper. The first layer allows message passing
# between bonded atoms, whereas the second layer does so for the atom pairs for
# which we need to predict the scalar coupling constant. Unlike the attention
# layers the message passing layers' parameters are tied across blocks.
#
# The three attention layers are:
#   1. distance based gaussian attention
#   2. graph distance based attention
#   3. scaled dot product self attention
#
# Although the final layers in the block resemble the encoder blocks of the
# Transformer model, there are several additional layers designed specifically
# to capture the structure and relationships among atoms in a molecule.
#
# Much of the code is adopted from the annotated version of the Transformer
# paper, which can be found here
# (http://nlp.seas.harvard.edu/2018/04/03/attention.html).

import math
import copy
import torch
import torch.nn as nn
import torch.nn.functional as F
from fcnet import FullyConnectedNet, hidden_layer
from scatter import scatter_mean
from layernorm import LayerNorm

def clones(module, N):
    """Produce N identical layers."""
    return torch.nn.ModuleList([copy.deepcopy(module) for _ in range(N)])

class SublayerConnection(nn.Module):
    """
    A residual connection followed by a layer norm.
    Note for code simplicity the norm is first as opposed to last.
    """
    def __init__(self, size, dropout):
        super().__init__()
        self.norm = LayerNorm(size)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, sublayer):
        """Apply residual connection to any sublayer with the same size."""
        return x + self.dropout(sublayer(self.norm(x)))

def _gather_nodes(x, idx, sz_last_dim):
    idx = idx.unsqueeze(-1).expand(-1, -1, sz_last_dim)
    return x.gather(1, idx)

class ENNMessage(nn.Module):
    """
    The edge network message passing function from the MPNN paper. Optionally
    adds and additional cosine angle based attention mechanism over incoming
    messages.
    """
    PAD_VAL = -999

    def __init__(self, d_model, d_edge, kernel_sz, enn_args={}, ann_args=None):
        super().__init__()
        assert kernel_sz <= d_model
        self.d_model, self.kernel_sz = d_model, kernel_sz
        self.enn = FullyConnectedNet(d_edge, d_model*kernel_sz, **enn_args)
        if ann_args: self.ann = FullyConnectedNet(1, d_model, **ann_args)
        else: self.ann = None

    def forward(self, x, edges, pairs_idx, angles=None, angles_idx=None, t=0):
        """Note that edges and pairs_idx raw inputs are for a unidirectional
        graph. They are expanded to allow bidirectional message passing."""
        if t==0:
            self.set_a_mat(edges)
            if self.ann: self.set_attn(angles)
            # concat reversed pairs_idx for bidirectional message passing
            self.pairs_idx = torch.cat([pairs_idx, pairs_idx[:,:,[1,0]]], dim=1)
        return self.add_message(torch.zeros_like(x), x, angles_idx)

    def set_a_mat(self, edges):
        n_edges = edges.size(1)
        a_vect = self.enn(edges)
        a_vect = a_vect / (self.kernel_sz ** .5) # rescale
        mask = edges[:,:,0,None].expand(a_vect.size())==self.PAD_VAL
        a_vect = a_vect.masked_fill(mask, 0.0)
        self.a_mat = a_vect.view(-1, n_edges, self.d_model, self.kernel_sz)
        # concat a_mats for bidirectional message passing
        self.a_mat = torch.cat([self.a_mat, self.a_mat], dim=1)

    def set_attn(self, angles):
        angles = angles.unsqueeze(-1)
        self.attn = self.ann(angles)
        mask = angles.expand(self.attn.size())==self.PAD_VAL
        self.attn = self.attn.masked_fill(mask, 0.0)

    def add_message(self, m, x, angles_idx=None):
        """Add message for atom_{i}: m_{i} += sum_{j}[attn_{ij} A_{ij}x_{j}]."""
        # select the 'x_{j}' feeding into the 'm_{i}'
        x_in = _gather_nodes(x, self.pairs_idx[:,:,1], self.d_model)

        # do the matrix multiplication 'A_{ij}x_{j}'
        if self.kernel_sz==self.d_model: # full matrix multiplcation
            ax = (x_in.unsqueeze(-2) @ self.a_mat).squeeze(-2)
        else: # do a convolution
            x_padded = F.pad(x_in, self.n_pad)
            x_unfolded = x_padded.unfold(-1, self.kernel_sz, 1)
            ax = (x_unfolded * self.a_mat).sum(-1)

        # apply atttention
        if self.ann:
            n_pairs = self.pairs_idx.size(1)
            # average all attn(angle_{ijk}) per edge_{ij}.
            # i.e.: attn_{ij} = sum_{k}[attn(angle_{ijk})] / n_angles_{ij}
            ave_att = scatter_mean(self.attn, angles_idx, num=n_pairs, dim=1,
                                   out=torch.ones_like(ax))
            ax = ave_att * ax

        # sum up all 'A_{ij}h_{j}' per node 'i'
        idx_0 = self.pairs_idx[:,:,0,None].expand(-1, -1, self.d_model)
        return m.scatter_add(1, idx_0, ax)

    @property
    def n_pad(self):
        k = self.kernel_sz
        return (k // 2, k // 2 - int(k % 2 == 0))

class MultiHeadedDistAttention(nn.Module):
    """Generalizes the euclidean and graph distance based attention layers."""
    def __init__(self, h, d_model):
        super().__init__()
        self.d_model, self.d_k, self.h = d_model, d_model // h, h
        self.attn = None
        self.linears = clones(nn.Linear(d_model, d_model), 2)

    def forward(self, dists, x, mask):
        batch_size = x.size(0)
        x = self.linears[0](x).view(batch_size, -1, self.h, self.d_k)
        x, self.attn = self.apply_attn(dists, x, mask)
        x = x.view(batch_size, -1, self.h * self.d_k)
        return self.linears[-1](x)

    def apply_attn(self, dists, x, mask):
        attn = self.create_raw_attn(dists, mask)
        attn = attn.transpose(-2,-1).transpose(1, 2)
        x = x.transpose(1, 2)
        x = torch.matmul(attn, x)
        x = x.transpose(1, 2).contiguous()
        return x, attn

    def create_raw_attn(self, dists, mask):
        pass

class MultiHeadedGraphDistAttention(MultiHeadedDistAttention):
    """Attention based on an embedding of the graph distance matrix."""
    MAX_GRAPH_DIST = 10
    def __init__(self, h, d_model):
        super().__init__(h, d_model)
        self.embedding = nn.Embedding(self.MAX_GRAPH_DIST+1, h)

    def create_raw_attn(self, dists, mask):
        emb_dists = self.embedding(dists)
        mask = mask.unsqueeze(-1).expand(emb_dists.size())
        emb_dists = emb_dists.masked_fill(mask==0, -1e9)
        return F.softmax(emb_dists, dim=-2).masked_fill(mask==0, 0)

class MultiHeadedEuclDistAttention(MultiHeadedDistAttention):
    """Attention based on a parameterized normal pdf taking a molecule's
    euclidean distance matrix as input."""
    def __init__(self, h, d_model):
        super().__init__(h, d_model)
        self.log_prec = nn.Parameter(torch.Tensor(1, 1, 1, h))
        self.locs = nn.Parameter(torch.Tensor(1, 1, 1, h))
        nn.init.normal_(self.log_prec, mean=0.0, std=0.1)
        nn.init.normal_(self.locs, mean=0.0, std=1.0)

    def create_raw_attn(self, dists, mask):
        dists = dists.unsqueeze(-1).expand(-1, -1, -1, self.h)
        z = torch.exp(self.log_prec) * (dists - self.locs)
        pdf = torch.exp(-0.5 * z ** 2)
        return pdf / pdf.sum(dim=-2, keepdim=True).clamp(1e-9)

def attention(query, key, value, mask=None, dropout=None):
    """Compute 'Scaled Dot Product Attention'."""
    d_k = query.size(-1)
    scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)
    if mask is not None: scores = scores.masked_fill(mask==0, -1e9)
    p_attn = F.softmax(scores, dim=-1).masked_fill(mask==0, 0)
    if dropout is not None: p_attn = dropout(p_attn)
    return torch.matmul(p_attn, value), p_attn

class MultiHeadedSelfAttention(nn.Module):
    """Applies self-attention as described in the Transformer paper."""
    def __init__(self, h, d_model, dropout=0.1):
        super().__init__()
        self.d_model, self.d_k, self.h = d_model, d_model // h, h
        self.attn = None
        self.linears = clones(nn.Linear(d_model, d_model), 4)
        self.dropout = nn.Dropout(p=dropout) if dropout > 0.0 else None

    def forward(self, x, mask):
        # Same mask applied to all h heads.
        mask = mask.unsqueeze(1)
        batch_size = x.size(0)

        # 1) Do all the linear projections in batch from d_model => h x d_k
        query, key, value = [
            l(x).view(batch_size, -1, self.h, self.d_k).transpose(1, 2)
            for l in self.linears[:3]
        ]

        # 2) Apply attention on all the projected vectors in batch.
        x, self.attn = attention(query, key, value, mask, self.dropout)

        # 3) "Concat" using a view and apply a final linear.
        x = x.transpose(1, 2).contiguous()
        x = x.view(batch_size, -1, self.d_model)
        return self.linears[-1](x)

class AttendingLayer(nn.Module):
    """Stacks the three attention layers and the pointwise feedforward net."""
    def __init__(self, size, eucl_dist_attn, graph_dist_attn, self_attn, ff,
                 dropout):
        super().__init__()
        self.eucl_dist_attn = eucl_dist_attn
        self.graph_dist_attn = graph_dist_attn
        self.self_attn = self_attn
        self.ff = ff
        self.subconns = clones(SublayerConnection(size, dropout), 4)
        self.size = size

    def forward(self, x, eucl_dists, graph_dists, mask):
        eucl_dist_sub = lambda x: self.eucl_dist_attn(eucl_dists, x, mask)
        x = self.subconns[0](x, eucl_dist_sub)
        graph_dist_sub = lambda x: self.graph_dist_attn(graph_dists, x, mask)
        x = self.subconns[1](x, graph_dist_sub)
        self_sub = lambda x: self.self_attn(x, mask)
        x = self.subconns[2](x, self_sub)
        return self.subconns[3](x, self.ff)

class MessagePassingLayer(nn.Module):
    """Stacks the bond and scalar coupling pair message passing layers."""
    def __init__(self, size, bond_mess, sc_mess, dropout, N):
        super().__init__()
        self.bond_mess = bond_mess
        self.sc_mess = sc_mess
        self.linears = clones(nn.Linear(size, size), 2*N)
        self.subconns = clones(SublayerConnection(size, dropout), 2*N)

    def forward(self, x, bond_x, sc_pair_x, angles, mask, bond_idx, sc_idx,
                angles_idx, t=0):
        bond_sub = lambda x: self.linears[2*t](
            self.bond_mess(x, bond_x, bond_idx, angles, angles_idx, t))
        x = self.subconns[2*t](x, bond_sub)
        sc_sub = lambda x: self.linears[(2*t)+1](
            self.sc_mess(x, sc_pair_x, sc_idx, t=t))
        return self.subconns[(2*t)+1](x, sc_sub)

class Encoder(nn.Module):
    """Encoder stacks N attention layers and one message passing layer."""
    def __init__(self, mess_pass_layer, attn_layer, N):
        super().__init__()
        self.mess_pass_layer = mess_pass_layer
        self.attn_layers = clones(attn_layer, N)
        self.norm = LayerNorm(attn_layer.size)

    def forward(self, x, bond_x, sc_pair_x, eucl_dists, graph_dists, angles,
                mask, bond_idx, sc_idx, angles_idx):
        """Pass the inputs (and mask) through each block in turn. Note that for
        each block the same message passing layer is used."""
        for t, attn_layer in enumerate(self.attn_layers):
            x = self.mess_pass_layer(x, bond_x, sc_pair_x, angles, mask,
                                     bond_idx, sc_idx, angles_idx, t)
            x = attn_layer(x, eucl_dists, graph_dists, mask)
        return self.norm(x)

# After N blocks of message passing and attending, the encoded atom states are
# transferred to the head of the model: a customized feed-forward net for
# predicting the scalar coupling (sc) constant.

# First the relevant pairs of atom states for each sc constant in the batch
# are selected, concatenated and stacked. Also concatenated to the encoded
# states are a set of raw molecule and sc pair specific features. These states
# are fed into a residual block comprised of a dense layer followed by a type
# specific dense layer of dimension 'd_ff' (the same as the dimension used for
# the pointwise feed-forward net).

# The processed states are passed through to a relatively small feed-forward
# net, which predicts each sc contribution seperately plus a residual.
# Ultimately, the predictions of these contributions and the residual are summed
# to predict the sc constant.

def create_contrib_head(d_in, d_ff, act, dropout=0.0, layer_norm=True):
    layers = hidden_layer(d_in, d_ff, False, dropout, layer_norm, act)
    layers += hidden_layer(d_ff, 1, False, 0.0) # output layer
    return nn.Sequential(*layers)

class ContribsNet(nn.Module):
    """The feed-forward net used for the sc contribution and final sc constant
    predictions."""
    N_CONTRIBS = 5
    CONTIB_SCALES = [1, 250, 45, 35, 500] # scales used to make the 5 predictions of similar magnitude

    def __init__(self, d_in, d_ff, vec_in, act, dropout=0.0, layer_norm=True):
        super().__init__()
        contrib_head = create_contrib_head(d_in, d_ff, act, dropout, layer_norm)
        self.blocks = clones(contrib_head, self.N_CONTRIBS)

    def forward(self, x):
        ys = torch.cat(
            [b(x)/s for b,s in zip(self.blocks, self.CONTIB_SCALES)], dim=-1)
        return torch.cat([ys[:,:-1], ys.sum(dim=-1, keepdim=True)], dim=-1)

class MyCustomHead(nn.Module):
    """Joins the sc type specific residual block with the sc contribution
    feed-forward net."""
    PAD_VAL = -999
    N_TYPES = 8

    def __init__(self, d_input, d_ff, d_ff_contribs, pre_layers=[],
                 post_layers=[], act=nn.ReLU(True), dropout=3*[0.], norm=False):
        super().__init__()
        fc_pre = hidden_layer(d_input, d_ff, False, dropout[0], norm, act)
        self.preproc = nn.Sequential(*fc_pre)
        fc_type = hidden_layer(d_ff, d_input, False, dropout[1], norm, act)
        self.types_net = clones(nn.Sequential(*fc_type), self.N_TYPES)
        self.contribs_net = ContribsNet(
            d_input, d_ff_contribs, d_ff, act, dropout[2], layer_norm=norm)

    def forward(self, x, sc_types):
        # stack inputs with a .view for easier processing
        x, sc_types = x.view(-1, x.size(-1)), sc_types.view(-1)
        mask =  sc_types != self.PAD_VAL
        x, sc_types = x[mask], sc_types[mask]

        x_ = self.preproc(x)
        x_types = torch.zeros_like(x)
        for i in range(self.N_TYPES):
            t_idx = sc_types==i
            if torch.any(t_idx): x_types[t_idx] = self.types_net[i](x_[t_idx])
            else: x_types = x_types + 0.0 * self.types_net[i](x_) # fake call (only necessary for distributed training - to make sure all processes have gradients for all parameters)
        x = x + x_types
        return self.contribs_net(x)

class Transformer(nn.Module):
    """Molecule transformer with message passing."""
    def __init__(self, d_atom, d_bond, d_sc_pair, d_sc_mol, N=6, d_model=512,
                 d_ff=2048, d_ff_contrib=128, h=8, dropout=0.1, kernel_sz=128,
                 enn_args={}, ann_args={}):
        super().__init__()
        assert d_model % h == 0
        self.d_model = d_model
        c = copy.deepcopy
        bond_mess = ENNMessage(d_model, d_bond, kernel_sz, enn_args, ann_args)
        sc_mess = ENNMessage(d_model, d_sc_pair, kernel_sz, enn_args)
        eucl_dist_attn = MultiHeadedEuclDistAttention(h, d_model)
        graph_dist_attn = MultiHeadedGraphDistAttention(h, d_model)
        self_attn = MultiHeadedSelfAttention(h, d_model, dropout)
        ff = FullyConnectedNet(d_model, d_model, [d_ff], dropout=[dropout])

        message_passing_layer = MessagePassingLayer(
            d_model, bond_mess, sc_mess, dropout, N)
        attending_layer = AttendingLayer(
            d_model, c(eucl_dist_attn), c(graph_dist_attn), c(self_attn), c(ff),
            dropout
        )

        self.projection = nn.Linear(d_atom, d_model)
        self.encoder = Encoder(message_passing_layer, attending_layer, N)
        self.write_head = MyCustomHead(
            2 * d_model + d_sc_mol, d_ff, d_ff_contrib, norm=True)

    def forward(self, atom_x, bond_x, sc_pair_x, sc_mol_x, eucl_dists,
                graph_dists, angles, mask, bond_idx, sc_idx, angles_idx,
                sc_types):
        x = self.encoder(
            self.projection(atom_x), bond_x, sc_pair_x, eucl_dists, graph_dists,
            angles, mask, bond_idx, sc_idx, angles_idx
        )
        # for each sc constant in the batch select and concat the relevant pairs
        # of atom  states.
        x = torch.cat(
            [_gather_nodes(x, sc_idx[:,:,0], self.d_model),
             _gather_nodes(x, sc_idx[:,:,1], self.d_model),
             sc_mol_x], dim=-1
        )
        return self.write_head(x, sc_types)


import os
import re
import asyncio
import time
from pyrogram import *
from pyrogram.types import *
from random import choice
from Heroku import cloner, ASSUSERNAME, BOT_NAME
from Heroku.config import API_ID, API_HASH
IMG = ["https://telegra.ph/file/cefd3211a5acdcd332415.jpg", "https://telegra.ph/file/30d743cea510c563af6e3.jpg", "https://telegra.ph/file/f7ae22a1491f530c05279.jpg", "https://telegra.ph/file/2f1c9c98452ae9a958f7d.jpg"]
MESSAGE = "Heya! I'm a music bot hoster/Cloner\n\nI can Host Your Bot On My Server within seconds\n\nTry /clone Token from @botfather"

@cloner.on_message(filters.private & filters.command("dkkdej"))
async def hello(client, message: Message):
    buttons = [
           [
                InlineKeyboardButton("✘ ᴜᴘᴅᴀᴛᴇꜱ ᴄʜᴀɴɴᴇʟ", url="t.me/TheUpdatesChannel"),
            ],
            [
                InlineKeyboardButton("✘ ꜱᴜᴘᴘᴏʀᴛ ɢʀᴏᴜᴘ", url="t.me/TheSupportChat"),
            ],
            ]
    reply_markup = InlineKeyboardMarkup(buttons)
    await client.send_photo(message.chat.id, f"{choice(IMG)}", caption=MESSAGE, reply_markup=reply_markup)

##Copy from here

# © By Itz-Zaid Your motherfucker if uh Don't gives credits.
@cloner.on_message(filters.private & filters.command("clone"))
async def clone(bot, msg: Message):
    chat = msg.chat
    text = await msg.reply("Kullanım:\n\n /clone token")
    cmd = msg.command
    phone = msg.command[1]
    try:
        await text.edit("Yükleniyor... ")
                   # change this Directry according to ur repo
        client = Client(":memory:", API_ID, API_HASH, bot_token=phone, plugins={"root": "Heroku.modules"})
        await client.start()
        user = await client.get_me()
        await msg.reply(f"Başarıyla Başlatıldı @{user.username}! ✅ \n\n Şimdi Botunuzu ve Asistanı @{ASSUSERNAME} Grubunuza Ekleyin!")
    except Exception as e:
        await msg.reply(f"**HATA:** `{str(e)}`\nYeniden /start veriniz.")
#End
##This code fit with every pyrogram Codes just import then @Client Xyz!


from PIL import Image

imgx = 512
imgy = 512

image = Image.new("RGB",(imgx,imgy))

for x in range(imgx):
	for y in range(imgy):
		if ((x//64)%2 == 1) or ((x//64)%2 == 2) and (y//64)%2 == 1 or ((y//64)%2 == 2):
			image.putpixel ((x,y), (0,0,0) )
		else:
			image.putpixel ((x,y), (250,0,0) )

image.save("demo_image.png", "PNG")