# -*- coding:UTF-8 -*-

import tushare as ts
import numpy as np
import pandas as pd
import os
import shutil
import time
import datetime
import sys
import math
import hashlib
import preprocess
sys.path.append("..")
from common import base_common
from common import np_common
from common.const_def import *

pro = ts.pro_api()

# preprocess_ref_days = 100

reload(sys)
sys.setdefaultencoding('utf-8')
# max_predict_day_count = 10  # 决定train_data 和 test_data 的predict_day_count
# predict_day_count = 2  # 预测未来几日的数据
# referfence_feature_count = 1
# test_acture_data_with_feature = False
# train_a_stock_min_data_num = 400
# train_a_stock_max_data_num = 1000000

# pp_data_start_date = '20000101'
# stocks_list_end_date = '20000101'
# train_data_start_date = '20120101'
# train_data_end_date = '20170101'
# test_data_start_date = '20170101'
# test_data_end_date = '20190414'
# train_test_date = '20190414'
# predict_date = '20181225'

ts.set_token('230c446ae448ec95357d0f7e804ddeebc7a51ff340b4e6e0913ea2fa')

pd.set_option('display.width', 150)  # 设置字符显示宽度
pd.set_option('display.max_rows', 20)  # 设置显示最大行

def TradeDateLowLevel(end_date):
    file_name = './data/trade_date/trade_date_%s.npy' % str(end_date)
    if os.path.exists(file_name):
        date_np = np.load(file_name)
        return date_np
    else:
        df_trade_cal = pro.trade_cal(exchange = 'SSE', start_date = '19800101', end_date = str(end_date))
        df_trade_cal = df_trade_cal.sort_index(ascending = False)
        df_trade_cal = df_trade_cal[df_trade_cal['is_open'] == 1]
        date_np = df_trade_cal['cal_date'].values
        base_common.MKFileDirs(file_name)
        np.save(file_name, date_np)
    return date_np

def TradeDateList(end_date, trade_day_num):
    date_np = TradeDateLowLevel(end_date)
    return date_np[:trade_day_num].copy().tolist()

def TradeDateListRange(start_date, end_date):
    date_np = TradeDateLowLevel(end_date)
    pos = date_np.astype(np.int64) >= int(start_date)
    return date_np[pos].copy().tolist()

def StockCodeFilter(ts_code, code_filter_list):
    for it in code_filter_list:
        if ts_code[0:len(it)] == it:
            return True
    return False

def StockCodesName(release_end_date, industry_filter, code_filter, sample_step, sample_start_offset=0):
    file_name = './data/' + 'stock_code' + '.csv'
    if os.path.exists(file_name):
        load_df = pd.read_csv(file_name)
    else:
        load_df = pro.stock_basic(exchange = '', list_status = 'L', fields = 'ts_code,symbol,name,area,industry,list_date')
        base_common.MKFileDirs(file_name)
        load_df.to_csv(file_name)

    if release_end_date > 0:
        load_df = load_df[load_df['list_date'] <= int(release_end_date)]
    if sample_step > 1:
        # load_df[::2] # 奇数行
        # load_df[1::2] # 偶数行
        load_df = load_df[sample_start_offset::sample_step]
    load_df = load_df.copy()
    load_df = load_df.reset_index(drop=True)

    industry_filter_en = False
    code_filter_en = False
    circ_mv_filter_en = False
    if industry_filter != '':
        industry_list = industry_filter.split(',')
        industry_filter_en = True
    if code_filter != '':
        code_filter_list = code_filter.split(',')
        code_filter_en = True

    code_valid_list = []
    for iloop in range(0, len(load_df)):
        temp_code = load_df['ts_code'][iloop]
        temp_code_valid = True
        if industry_filter_en:
            if not load_df['industry'][iloop] in industry_list:
                temp_code_valid = False
        if code_filter_en:
            if not StockCodeFilter(temp_code, code_filter_list):
                temp_code_valid = False

        code_valid_list.append(temp_code_valid)
    load_df = load_df[code_valid_list]
    # print(load_df)
    code_list = load_df['ts_code'].values
    name_list = load_df['name'].values
    return code_list, name_list

def StockCodes(release_end_date, industry_filter, code_filter, sample_step):
    code_list, name_list = StockCodesName(release_end_date, industry_filter, code_filter, sample_step)
    return code_list

def StockNames(release_end_date, industry_filter, code_filter, sample_step):
    code_list, name_list = StockCodesName(release_end_date, industry_filter, code_filter, sample_step)
    for iloop in range(0, len(code_list)):
        if code_list[iloop] == ts_code:
            return name_list[iloop]
    return 'unknow'

def UpdatePPDataMTFunc(param, msg):
    param.UpdateStockPPData(msg)

class DataSource():
    def __init__(self,
                 release_end_date,
                 industry_filter,
                 code_filter,
                 sample_step,
                 start_date,
                 end_date,
                 use_daily_basic=False,
                 use_money_flow=False,
                 use_adj_factor=True,
                 adj_mode='f'):
        self.release_end_date = release_end_date
        self.industry_filter = industry_filter
        self.code_filter = code_filter
        self.sample_step = sample_step
        self.start_date = start_date
        self.end_date = end_date
        self.use_daily_basic = use_daily_basic
        self.use_money_flow = use_money_flow
        self.use_adj_factor = use_adj_factor
        self.adj_mode = adj_mode
        self.pp_data_daily_update = False
        self.UpdateSettings()
        print('DataSource.__init__: ts_code num: %u' % len(self.code_list))
        # self.ShowStockCodes()

    def UpdateSettings(self):
        if self.pp_data_daily_update:
            setting_end_date = self.end_date_daily_update
        else:
            setting_end_date = self.end_date
        self.download_data_name_list = ['daily', 'daily_basic', 'moneyflow', 'adj_factor']
        self.setting_name_stock_pp = '%s_%u_%u_%u_%s' % (\
                            str(setting_end_date), \
                            int(self.use_daily_basic), \
                            int(self.use_money_flow), \
                            int(self.use_adj_factor), \
                            self.adj_mode)
        self.setting_name_stock = '%s_%s' % (\
                            str(self.start_date), \
                            self.setting_name_stock_pp)
        if self.code_filter == '':
            self.code_filter_setting_name = ''
        else:
            md5 = hashlib.md5()
            md5.update(self.code_filter.encode('utf-8'))
            self.code_filter_setting_name = str(md5.hexdigest())
        self.setting_name = '%s_%s_%s_%u_%s' % (\
                            str(self.release_end_date), \
                            self.industry_filter, \
                            self.code_filter_setting_name, \
                            self.sample_step, \
                            self.setting_name_stock)
        self.code_list, self.name_list = StockCodesName(self.release_end_date,
                                                        self.industry_filter,
                                                        self.code_filter,
                                                        self.sample_step)
        self.date_list = TradeDateListRange(self.start_date, setting_end_date)
        self.code_index_map = base_common.ListToIndexMap(self.code_list, False)
        code_list_int = []
        for iloop in range(len(self.code_list)):
            code_list_int.append(int(self.code_list[iloop][0:6]))
        self.code_index_map_int = base_common.ListToIndexMap(code_list_int)
        # date_index_map: int -> int
        self.date_index_map = base_common.ListToIndexMap(self.date_list, True)
        self.index_codes = ['000001.SH', '000300.SH', '000016.SH']
        self.code_name_map = base_common.ListToMap(self.code_list, self.name_list)

    def ShowStockCodes(self):
        print('%-7s%s' % ('index', 'ts_code'))
        print('-' * 32)
        for iloop in range(len(self.code_list)):
            print('%-7u%-14s%s' % (iloop, self.code_list[iloop], self.name_list[iloop]))
        print('\n')

    def StockName(self, ts_code):
        if ts_code in self.code_list:
            return self.code_name_map[ts_code]
        else:
            return '--'

    def FileNameStockDownloadData(self, ts_code):
        name_list = []
        for data_name in self.download_data_name_list:
            name_list.append('./data/%s/%s_%s.csv' % (data_name, ts_code, str(self.end_date)))
        return name_list

    def FileNameTradeDayDownloadData(self, trade_date):
        name_list = []
        for data_name in self.download_data_name_list:
            name_list.append('./data/%s/trade_date_%s.csv' % (data_name, str(trade_date)))
        return name_list

    def FileNameStockPPData(self, ts_code):
        temp_file_name = './data/preprocessed/%s_%s.npy' %(\
            ts_code, \
            self.setting_name_stock_pp)
        return temp_file_name

    # def FileNameDSPPDataset(self, end_date):
    #     return './data/dataset/dspp_%s_%u.npy' % (self.setting_name_stock, end_date)

    def InvalidFileClean(self, file_name):
        if os.path.exists(file_name):
            temp_df = pd.read_csv(file_name)
            if len(temp_df) == 0:
                os.remove(file_name)
                return True
        return False

    def StockDataMerge(self, df_1, df_2):
        df_2.drop(['ts_code'],axis=1,inplace=True)
        df_merge = pd.merge(df_1, df_2, left_on='trade_date', right_on='trade_date')
        return df_merge

    def TradeDayDataMerge(self, df_1, df_2):
        df_2.drop(['trade_date'],axis=1,inplace=True)
        df_merge = pd.merge(df_1, df_2, left_on='ts_code', right_on='ts_code')
        return df_merge

    def PrintProcess(self, ts_code):
        if ts_code in self.code_list:
            sys.stdout.write("%-4d : %s 100%%\n" % (self.code_index_map[ts_code], ts_code))
        else:
            sys.stdout.write("%-4s : %s 100%%\n" % ('--', ts_code))

    def DownloadStockData(self, ts_code):
        if ts_code in self.index_codes:
            self.DownloadIndexData(ts_code)
            return
        start_date = '19000101'
        end_date = self.end_date
        name_list = self.FileNameStockDownloadData(ts_code)
        download_flag = False
        while True:
            file_name = name_list[0]
            if not os.path.exists(file_name):
                download_flag = True
                df = pro.daily(ts_code = ts_code, start_date = start_date, end_date = end_date)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue

            file_name = name_list[1]
            if not os.path.exists(file_name) and self.use_daily_basic:
                download_flag = True
                df = pro.daily_basic(ts_code = ts_code, start_date = start_date, end_date = end_date)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue

            file_name = name_list[2]
            if not os.path.exists(file_name) and self.use_money_flow:
                download_flag = True
                df = pro.moneyflow(ts_code = ts_code, start_date = start_date, end_date = end_date)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue

            file_name = name_list[3]
            if not os.path.exists(file_name) and self.use_adj_factor:
                download_flag = True
                df = pro.adj_factor(ts_code = ts_code, trade_date='')
                col_date = df['trade_date'].copy()
                col_date = pd.to_numeric(col_date)
                df = df[col_date <= int(end_date)].copy().reset_index(drop=True)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue
            break
        if download_flag:
            self.PrintProcess(ts_code)

    def DownloadData(self):
        for code_index in range(0, len(self.code_list)):
            ts_code = self.code_list[code_index]
            self.DownloadStockData(ts_code)
        for ts_code in self.index_codes:
            self.DownloadStockData(ts_code)

    def LoadDownloadStockData(self, ts_code):
        download_name_list = self.FileNameStockDownloadData(ts_code)
        # daily
        if not os.path.exists(download_name_list[0]):
            return pd.DataFrame()
        merge_df = pd.read_csv(download_name_list[0])

        # daily basic
        if self.use_daily_basic:
            if not os.path.exists(download_name_list[1]):
                return pd.DataFrame()
            df_daily_basic = pd.read_csv(download_name_list[1])
            df_daily_basic.drop(['close'],axis=1,inplace=True)
            merge_df = self.StockDataMerge(merge_df, df_daily_basic)

        # money flow
        if self.use_money_flow:
            if not os.path.exists(download_name_list[2]):
                return pd.DataFrame()
            df_money_flow = pd.read_csv(download_name_list[2])
            merge_df = self.StockDataMerge(merge_df, df_money_flow)

        # adj factor
        if self.use_adj_factor:
            if not os.path.exists(download_name_list[3]):
                return pd.DataFrame()
            df_adj_factor = pd.read_csv(download_name_list[3])
            merge_df = self.StockDataMerge(merge_df, df_adj_factor)

        return merge_df

    def UpdateStockPPData(self, ts_code):
        if ts_code in self.index_codes:
            self.UpdateIndexPPData(ts_code)
            return
        stock_pp_file_name = self.FileNameStockPPData(ts_code)
        if not os.path.exists(stock_pp_file_name):
            merge_df = self.LoadDownloadStockData(ts_code)
            pp_data = preprocess.StockDataPreProcess(merge_df, self.adj_mode)
            if len(pp_data) > 0:
                base_common.MKFileDirs(stock_pp_file_name)
                np.save(stock_pp_file_name, pp_data)
            else:
                print("UpdateStockPPData error: %s" % ts_code)
                return
            self.PrintProcess(ts_code)

    def LoadStockPPData(self, ts_code, cut_from_start_date=False):
        if self.pp_data_daily_update:
            return self.LoadStockPPDataDailyUpdate(ts_code, cut_from_start_date)
        else:
            stock_pp_file_name = self.FileNameStockPPData(ts_code)
            if not os.path.exists(stock_pp_file_name):
                return []
            pp_data = np.load(stock_pp_file_name)
            if cut_from_start_date:
                pp_data = pp_data[pp_data[:,PPI_trade_date] >= int(self.start_date)]
            return pp_data

    def UpdatePPData(self):
        base_common.ListMultiThread(UpdatePPDataMTFunc, self, 8, self.code_list)
        # for code_index in range(0, len(self.code_list)):
        #     ts_code = self.code_list[code_index]
        #     self.UpdateStockPPData(ts_code)
        #     print("%-4d : %s 100%%" % (code_index, ts_code))
        for ts_code in self.index_codes:
            self.UpdateStockPPData(ts_code)

    def ShowStockPPData(self, ts_code, start_date, data_num=10):
        pp_data = self.LoadStockPPData(ts_code)
        date_index = -1
        for iloop in reversed(range(len(pp_data))):
            if pp_data[iloop][PPI_trade_date] >= int(start_date):
                date_index = iloop
                break
        print("%-10s%-10s%-10s%-10s%-10s%-10s" % ('date', 'pre_close', 'open', 'close', 'open_inc', 'vol'))
        print('-' * 60)
        data_cnt = 0
        while date_index >= 0:
            temp_data = pp_data[date_index]
            print("%-10u%-10.2f%-10.2f%-10.2f%-10.2f%-10.2f" % (
                int(temp_data[PPI_trade_date]),
                temp_data[PPI_pre_close],
                temp_data[PPI_open],
                temp_data[PPI_close],
                temp_data[PPI_open_increase],
                temp_data[PPI_vol]
            ))
            date_index -= 1
            data_cnt += 1
            if data_cnt >= data_num:
                break

    def ShowAvgVol(self, threshold):
        avg_vol_list = []
        cnt = 0
        for ts_code in self.code_list:
            pp_data = self.LoadStockPPData(ts_code)
            avg_vol = np.mean(pp_data[:, PPI_vol])
            avg_vol_list.append(avg_vol)
            if avg_vol > threshold:
                cnt += 1
                print("%-4d  %s %.0f" % (cnt, ts_code, avg_vol))
        # np_common.ShowHist(np.array(avg_vol_list), step=10000)

    def DownloadTradeDayData(self, trade_date):
        name_list = self.FileNameTradeDayDownloadData(trade_date)
        download_flag = False
        while True:
            file_name = name_list[0]
            if not os.path.exists(file_name):
                download_flag = True
                df = pro.daily(trade_date = trade_date)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue

            file_name = name_list[1]
            if not os.path.exists(file_name) and self.use_daily_basic:
                download_flag = True
                df = pro.daily_basic(trade_date = trade_date)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue

            file_name = name_list[2]
            if not os.path.exists(file_name) and self.use_money_flow:
                download_flag = True
                df = pro.moneyflow(trade_date = trade_date)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue

            file_name = name_list[3]
            if not os.path.exists(file_name) and self.use_adj_factor:
                download_flag = True
                df = pro.adj_factor(ts_code='', trade_date=trade_date)
                base_common.MKFileDirs(file_name)
                df.to_csv(file_name)
                if self.InvalidFileClean(file_name):
                    continue
            break
        if download_flag:
            sys.stdout.write("%s : 100%%\n" % trade_date)

    def LoadDownloadTradeDayData(self, trade_date):
        download_name_list = self.FileNameTradeDayDownloadData(trade_date)
        # daily
        if not os.path.exists(download_name_list[0]):
            return pd.DataFrame()
        merge_df = pd.read_csv(download_name_list[0])

        # daily basic
        if self.use_daily_basic:
            if not os.path.exists(download_name_list[1]):
                return pd.DataFrame()
            df_daily_basic = pd.read_csv(download_name_list[1])
            df_daily_basic.drop(['close'],axis=1,inplace=True)
            merge_df = self.TradeDayDataMerge(merge_df, df_daily_basic)

        # money flow
        if self.use_money_flow:
            if not os.path.exists(download_name_list[2]):
                return
            df_money_flow = pd.read_csv(download_name_list[2])
            merge_df = self.TradeDayDataMerge(merge_df, df_money_flow)

        # adj factor
        if self.use_adj_factor:
            if not os.path.exists(download_name_list[3]):
                return
            df_adj_factor = pd.read_csv(download_name_list[3])
            merge_df = self.TradeDayDataMerge(merge_df, df_adj_factor)

        return merge_df

    def DownloadIndexData(self, ts_code):
        start_date = '19000101'
        end_date = self.end_date
        name_list = self.FileNameStockDownloadData(ts_code)
        if not os.path.exists(name_list[0]):
            while True:
                file_name = name_list[0]
                if not os.path.exists(file_name):
                    df = pro.index_daily(ts_code=ts_code,
                                        start_date=start_date,
                                        end_date=end_date)
                    base_common.MKFileDirs(file_name)
                    df.to_csv(file_name)
                    if self.InvalidFileClean(file_name):
                        continue
                break
                sys.stdout.write("%-4s : %s 100%%\n" % ('--', ts_code))

    def LoadDownloadIndexData(self, ts_code):
        end_date = self.end_date
        name_list = self.FileNameStockDownloadData(ts_code)
        if not os.path.exists(name_list[0]):
            return []
        load_df = pd.read_csv(name_list[0])
        return load_df

    def UpdateIndexPPData(self, ts_code):
        stock_pp_file_name = self.FileNameStockPPData(ts_code)
        if not os.path.exists(stock_pp_file_name):
            df = self.LoadDownloadIndexData(ts_code)
            pp_data = preprocess.StockDataPreProcess(df, '')
            if len(pp_data) > 0:
                base_common.MKFileDirs(stock_pp_file_name)
                np.save(stock_pp_file_name, pp_data)
            else:
                print("UpdateStockPPData error: %s" % ts_code)
                return
            sys.stdout.write("%-4s : %s 100%%\n" % ('--', ts_code))

    # # 含 create
    # def LoadDSPPOriginDataset(self):
    #     file_name = self.FileNameDSPPDataset(self.end_date)
    #     if not os.path.exists(file_name):
    #         dataset = np.zeros((len(self.date_list), len(self.code_list), PPI_NUM))
    #         for ts_code in self.code_list:
    #             pp_data = self.LoadStockPPData(ts_code, True)
    #             s_index = self.code_index_map[ts_code]
    #             for iloop in range(len(pp_data)):
    #                 d_index = self.date_index_map[int(pp_data[iloop][PPI_trade_date])]
    #                 dataset[d_index][s_index] = pp_data[iloop]
    #         np.save(file_name, dataset)
    #     return np.load(file_name)

    # # 含 create
    # def LoadDSPPDataset(self, trade_date):
    #     file_name = self.FileNameDSPPDataset(trade_date)
    #     if not os.path.exists(file_name):
    #         origin_dataset = self.LoadDSPPOriginDataset()

    #     load_dataset = np.load(file_name)
    #     print("dspp_dataset_%u: {}".format(trade_date, load_dataset.shape))
    #     return load_dataset

    def SetPPDataDailyUpdate(self, start_date, trade_date):
        date_list = TradeDateListRange(self.end_date, trade_date)
        if int(date_list[-1]) == self.end_date:
            date_list.pop(-1)
        self.merge_daily_data = pd.DataFrame()
        for d in date_list:
            self.DownloadTradeDayData(d)
            df = self.LoadDownloadTradeDayData(d)
            if len(df) == 0:
                print('SetPPDataDailyUpdate.Error, len(df) == 0')
                return
            self.merge_daily_data = self.merge_daily_data.append(df, sort=False)
            # print('%s : %u' % (d, len(self.merge_daily_data)))
        self.pp_data_daily_update = True
        self.end_date_daily_update = trade_date
        self.start_date = start_date
        self.UpdateSettings()

    def CleanDFCols(self, df_data):
        col_captions = df_data.columns.values.tolist()
        for caption in col_captions:
            if len(caption) >= 7:
                if caption[0:7] == 'Unnamed':
                    df_data = df_data.drop([caption], axis=1)
        return df_data

    def LoadStockPPDataDailyUpdate(self, ts_code, cut_from_start_date=False):
        self.DownloadStockData(ts_code)
        df = self.LoadDownloadStockData(ts_code)

        daily_data = self.merge_daily_data[self.merge_daily_data['ts_code'] == ts_code].copy()
        daily_data = daily_data.sort_values(by=['trade_date'], ascending=(False))
        daily_data = daily_data.reset_index(drop=True)

        df = daily_data.append(df, sort=False).reset_index(drop=True)
        df = self.CleanDFCols(df)

        pp_data = preprocess.StockDataPreProcess(df, self.adj_mode)

        if cut_from_start_date:
            pp_data = pp_data[pp_data[:,PPI_trade_date] >= int(self.start_date)]
        return pp_data

    def EndDate(self):
        if self.pp_data_daily_update:
            return self.end_date_daily_update
        else:
            return self.end_date

if __name__ == "__main__":
    data_source = DataSource(20000101, '', '', 1, 20000101, 20200106, False, False, True)
    # data_source.ShowStockCodes()
    # data_source.DownloadData()
    # data_source.UpdatePPData()

    ts_code = '000001.SZ'
    # file_name = data_source.FileNameStockPPData(ts_code)
    # if os.path.exists(file_name):
    #     os.remove(file_name)
    # start_time = time.time()
    # data_source.UpdateStockPPData(ts_code)
    # print(time.time() - start_time)

    data_source.SetPPDataDailyUpdate(20200305)
    start_time = time.time()
    pp_data = data_source.LoadStockPPDataDailyUpdate(ts_code, True)
    print(time.time() - start_time)
    # print(pp_data)
    # np.save('./1.npy', pp_data)

    # data_source2 = DataSource(20000101, '', '', 1, 20000101, 20200305, False, False, True)
    # data_source2.DownloadStockData(ts_code)
    # df = data_source2.LoadDownloadStockData(ts_code)
    # print(df)
    # data_source2.UpdateStockPPData(ts_code)
    # pp_data = data_source2.LoadStockPPData(ts_code, True)
    # print(pp_data)
    # np.save('./2.npy', pp_data)

    # print(data_source.LoadIndexPPData(True).shape)


import discord
import traceback
import sys
from discord.ext import commands
import logging
from discord import Embed
from util.log import *

logger = logging.getLogger('__name__')
logger.setLevel(logging.INFO)
handler = logging.FileHandler(
    filename='discord.log', encoding='utf-8', mode='w')
handler.setFormatter(logging.Formatter(
    '%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
logger.addHandler(handler)

dyl = 332314562575597579

class CommandErrorHandler(commands.Cog):

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

    @commands.Cog.listener()
    async def on_command_error(self, ctx, error):
        """The event triggered when an error is raised while invoking a command.
        Parameters
        ------------
        ctx: commands.Context
            The context used for command invocation.
        error: commands.CommandError
            The Exception raised.
        """
        if hasattr(ctx.command, 'on_error'):
            return

        cog = ctx.cog
        if cog:
            if cog._get_overridden_method(cog.cog_command_error) is not None:
                return

        ignored = (commands.CommandNotFound, )
        # error = getattr(error, 'original', error)

        if isinstance(error, ignored):
            return

        if isinstance(error, commands.DisabledCommand):
            message = await ctx.send(f'{ctx.invoked_with} has been disabled.')
            await ctx.message.delete(delay=15)
            await message.delete(delay=1)

        elif isinstance(error, commands.NoPrivateMessage):
            try:
                await ctx.author.send(f'{ctx.command} can not be used in Private Messages.')
            except discord.HTTPException:
                pass

        elif isinstance(error, commands.PrivateMessageOnly):
            try:
                await ctx.author.send(f'{ctx.command} can only be used in Private Messages.')
            except discord.HTTPException:
                pass

        elif isinstance(error, commands.BadArgument):
            if ctx.command.qualified_name == 'tag list':
                await ctx.send('I could not find that member. Please try again.')
            else:
                await ctx.send("Invalid Argument")

        elif isinstance(error, commands.MissingRequiredArgument):
            await ctx.send("Missing input!")

        elif isinstance(error, discord.ext.commands.CommandOnCooldown):
            if ctx.author.id != dyl:
                errorembed = discord.Embed(title="Slow your roll",
                                           description=f"You're on a cooldown. Please try again in {error.retry_after:.2f} Second(s)!")
                message = await ctx.send(embed=errorembed)
                await message.delete(delay=error.retry_after)
                await ctx.message.delete(delay=5)

            else:
                await ctx.reinvoke()

        elif isinstance(error, commands.MissingPermissions):
            await ctx.send("Permission Denied")

        elif isinstance(error, commands.BotMissingPermissions):
            await ctx.send("Bot does not have enough permissions to perform that action")

        elif isinstance(error, commands.CommandInvokeError):
            error = error.original

            if isinstance(error, discord.Forbidden):

                await ctx.send(f"Discord doesn't let that happen")
                return

            if isinstance(error, discord.HTTPException):
                await ctx.send(f"HTTP Exception:\n{error.text}")
                return
            try:
                logger.error(
                    f"{error.__class__.__name__}: {error} (In {ctx.command.name})\n"
                    f"Traceback:\n{''.join(traceback.format_tb(error.__traceback__))}"
                )
            except:
                pass
            try:
                await errorlog(self.bot, embed=Embed(title=f"Something has fucked up.", description=f"Run by {ctx.author.mention} in channel {ctx.channel.mention}:\n{error}"))
            except discord.HTTPException:
                await errorlog(self.bot, embed=Embed(title="Big Uwu fucky", description="Description too big, but something really fucked up"))
            # await ctx.send("Something has gone pretty hecking bad. Contact Dyl asap")
            raise error

        else:
            embed = discord.Embed(
                title="Error", description=f"Ignoring exception in command {ctx.command}. Run by {ctx.author.mention} in channel {ctx.channel.mention}")
            # embed.add_field("Status", error.status)
            # embed.add_field("Discord Code",error.code)
            # embed.add_field("Error",str(dir(error)))
            await errorlognoping(self.bot, embed)

            # await ctx.send(embed=discord.Embed(title="Error",description='Ignoring exception in command {}:'.format(ctx.command)))
            # await ctx.send(embed=discord.Embed(title="Traceback",description=traceback.format_exc()))
            # traceback.print_exception(
            #     type(error), error, error.__traceback__, file=sys.stderr)

def setup(bot):
    bot.add_cog(CommandErrorHandler(bot))
    print("Error Handler Loaded")


import numpy as np
import sys

args = [arg for arg in sys.argv]

def readData(filename):
    data = []
    for line in reversed(open(filename).readlines()):
        try:
            input = float(line.strip("\n"))
            data.append(input)
        except:
            raise Exception("Error converting input to float")

        if ("Run Stats for experiment at:" in line):
            break
    return(data[::-1])

data = readData(args[1])

highest = max(x for x in data)
lowest = min(x for x in data)
mean = np.mean(data)

print("lowest:\t\t" + str(lowest))
print("mean:\t\t" + str(mean))
print("highest:\t" + str(highest))


from flask import Flask, request, session, render_template
from flask_ask import (
    Ask,
    statement,
    question,
    session as ask_session ,
    request as ask_request,
    convert_errors
)

import json
import requests
import datetime

app = Flask(__name__)
ask = Ask(app, '/alexa_transloc')

# From my experience, these are the stops most useful for undergraduates.
# It might be worth accessing location info to find nearby stops.
important_stops = [
    4146366, # Duke Chapel
    4188200, # Campus Dr at Swift Ave (Westbound) (12007)
    4188202, # Campus Dr at Swift Ave (Eastbound) (12008)
    4195822, # Swift Ave at 300 Swift (13009)
    4117202, # East Campus Quad (12003)
    4195814, # Smith Warehouse Eastbound (12077)
    4158230, # Smith Warehouse Westbound (12071)
    4197174, # Devil's Bistro (13300)
    4188214, # Swift Avenue Eastbound (12059)
    4195826, # Campus Dr at Nasher (Eastbound) (13015)
    4188204, # Nasher  Westbound (12009)
    4192460, # Oregon St at Pace St (13018)
    4098294, # Alexander Ave at Pace St Eastbound (12056)
    4098274, # Anderson St at Mill Village (12051)
    4098278, # Anderson St at Lewis St (12052)
]

AGENCY_ID = 176 # Duke

@app.route('/')
def testing():
    return ""

@ask.launch
def launch():
    hello = render_template('welcome')
    help_text = render_template('help')
    return question(hello).reprompt('list_stops_reprompt')

@ask.intent('ListStopsIntent')
def supported_stops():
    print("list stops invoked")
    supported = render_template('list_stops', stops=important_stops)
    return question(supported).reprompt('list_stops_reprompt')

@ask.intent('SelectStopIntent', mapping={'stop': 'desired_stop'})
def select_stop(stop):
    if 'stop' in convert_errors:
        return question('Can you please repeat which stop you\'d like?')

    response  = request.get_json()
    stop_info = response['request']['intent']['slots']['desired_stop']
    stop_name = stop_info['value']
    stop_id   = stop_info['resolutions']['resolutionsPerAuthority'][0]['values'][0]['value']['id']

    return statement(stop_times_to_english(int(stop_id)))

@ask.intent('AMAZON.HelpIntent')
def help():
    help_text = render_template('help')
    return question(help_text)

@ask.intent('AMAZON.StopIntent')
def stop():
    bye_text = render_template('bye')
    return statement(bye_text)

@ask.intent('AMAZON.CancelIntent')
def cancel():
    bye_text = render_template('bye')
    return statement(bye_text)

@ask.session_ended
def session_ended():
    return "{}", 200

################### Template Filters

@app.template_filter()
def humanize_times(times):
    """ Converts a list of bus times to useful english string. """
    english_string = ""
    times = times[:3]

    for t in times[:-1]:
        if t < 60:
            english_string += "less than a minute, "
        else:
            english_string += str(t // 60) + " minutes, "

    if english_string != "":
        english_string += "and "

    if times[-1] < 60:
        english_string += " less than a minute."
    else:
        english_string += str(times[-1] // 60) + " minutes. "

    return english_string

@app.template_filter()
def humanize_stops(stops):
    """ Converts a list of stops to useful english string. """
    english_string = ""
    for stop in stops:
        english_string += stop_id_to_name(stop) + ", "

    return english_string

################### API Handing

request_headers = {
    "X-Mashape-Key": "MASHAPE API KEY HERE", # request a key at ===> https://market.mashape.com/transloc/openapi-1-2
    "Accept": "application/json"
}

def get_stops():
    """ Returns list of stops for the specified agency. """
    endpoint = "https://transloc-api-1-2.p.mashape.com/stops.json"
    payload = { "agencies": AGENCY_ID, "callback": "call" }
    response = requests.get(endpoint, params=payload, headers=request_headers)
    return response.json()

def get_routes():
    """ Returns list of routes for the specified agency. """
    endpoint = "https://transloc-api-1-2.p.mashape.com/routes.json"
    payload = { "agencies": AGENCY_ID, "callback": "call" }
    response = requests.get(endpoint, params=payload, headers=request_headers)
    return response.json()

def get_arrival_estimates(stop_id):
    """ Returns list of arrival estimates for a certain stop. """
    endpoint = "https://transloc-api-1-2.p.mashape.com/arrival-estimates.json?"
    payload = { "agencies": AGENCY_ID, "stops": str(stop_id), "callback": "call" }
    response = requests.get(endpoint, params=payload, headers=request_headers)
    return response.json()

route_name_map = {}
def route_id_to_name(route_id):
    """ Returns english name for a specific route_id. """
    if len(route_name_map) == 0:
        resp = get_routes()
        for route in resp['data'][str(AGENCY_ID)]:
            name = route["long_name"]
            r_id = route["route_id"]
            route_name_map[str(r_id)] = name

    return route_name_map[str(route_id)]

stop_name_map = {}
def stop_id_to_name(stop_id):
    """ Returns english name for a specific stop_id. """
    if len(stop_name_map) == 0:
        resp = get_stops();
        for stop in resp['data']:
            s_id = stop["stop_id"]
            s_name = stop["name"]
            stop_name_map[str(s_id)] = s_name

    stop_name = stop_name_map[str(stop_id)]
    return stop_name.split(' (')[0] # dumb way to get rid of parenthesis at end

def stop_arrivals(stop_id):
    """ Returns a map of route_ids to arrival times at a specific stop. """
    arrivals = get_arrival_estimates(stop_id)

    route_map = {}
    if len(arrivals['data']) < 1:
        return {}
    for arrival in arrivals['data'][0]['arrivals']:
        if arrival['route_id'] in route_map:
           route_map[arrival['route_id']].append(arrival['arrival_at'])
        else:
            route_map[arrival['route_id']] = [arrival['arrival_at']]

    return route_map

def stop_times(stop_id):
    """ Returns a map of active routes to wait times for that route at a specific stop. """
    arrival_map = stop_arrivals(stop_id)

    stop_times = {}
    for route_id in arrival_map:
        route_name = route_id_to_name(route_id)
        stop_times[route_name] = []
        current_time = datetime.datetime.now()
        for timestamp in arrival_map[route_id]:
            arrival_time = datetime.datetime.strptime(timestamp,"%Y-%m-%dT%H:%M:%S-04:00")
            stop_times[route_name].append((arrival_time - current_time).seconds)

    return stop_times

def stop_times_to_english(stop_id):
    """ Returns an english string detailing wait times for a specific stop. """
    st = stop_times(stop_id)
    if len(st) < 1:
        return render_template('stop_info_empty', stop_name=stop_id_to_name(stop_id))
    output = render_template('stop_info', stop_name=stop_id_to_name(stop_id))
    for route in st:
        output += render_template('route_info', route_name=route, arrival_times=st[route])

    return output

if __name__ == "__main__":
        app.run()


'''
Project: Solvent_preselection

        Relevant functions for solvent pre-selection
                    using the GH-GNN

Author: Edgar Ivan Sanchez Medina
Email: sanchez@mpi-magdeburg.mpg.de
-------------------------------------------------------------------------------
'''
import numpy as np
from tqdm import tqdm
import pandas as pd
from scipy.optimize import minimize_scalar
import warnings
import ast
import matplotlib.pyplot as plt
import matplotlib as mpl
from thermo.unifac import UNIFAC
from GHGNN import GH_GNN
import os

_df_antoine = pd.read_csv('data/Antoine_constants.csv')

def get_compound(name:str, df_comp):
    df = df_comp[df_comp['name'] == name]
    c={}
    for col in df.columns:
        if col == 'UNIFAC_Do groups':
            c[col] = ast.literal_eval(df[col].values[0])
        else:
            c[col] = df[col].values[0]
    return c

def get_Antoine_constants(smiles:str, T:float, db=_df_antoine, verbose=True):
    '''
    Parameters
    ----------
    smiles : str
        SMILES of the pure compound.
    T : float
        Temperature in K.
    db : pd.DataFrame (optional)
        Database containing the Antoine constants

    Raises
    ------
    Exception
        If the SMILES is not in the database.

    '''
    df = db[db['smiles'] == smiles]
    if len(df)== 1:
        # Check temperature range
        T1, T2 = df['T1'].values[0], df['T2'].values[0]
        if T<T1 or T>T2 and verbose:
            warnings.warn(f'Temperature of {smiles} is outside of Antoine constants range')
        return df['A'].values[0], df['B'].values[0], df['C'].values[0]
    elif len(df) > 1:
        df_T = df[(df['T1'] <= T) & (df['T2'] >= T)]
        if len(df_T) == 1:
            return df_T['A'].values[0], df_T['B'].values[0], df_T['C'].values[0]
        elif len(df_T) > 1:
            idxmax = df_T['Delta T'].idxmax() # Largest T range
            df_T_largest = df_T.loc[idxmax]
            return df_T_largest['A'], df_T_largest['B'], df_T_largest['C']
        else:
            if verbose:
                warnings.warn(f'Temperature of {smiles} is outside of Antoine constants range')
            T1_dist = np.abs(df['T1'].to_numpy() - T).reshape(-1,1)
            T2_dist = np.abs(df['T2'].to_numpy() - T).reshape(-1,1)
            T_dist_min = np.min(np.concatenate((T1_dist,T2_dist), axis=1), axis=1)
            idxclosest = np.argmin(T_dist_min)
            df_T_closest = df.iloc[idxclosest]
            return df_T_closest['A'], df_T_closest['B'], df_T_closest['C']
    else:
        raise Exception(f'This SMILES {smiles} is not in database')

def get_vapor_pressure_Antoine(A:float, B:float, C:float, T:float):
    '''
    Computes the vapor pressure of pure compound using the Anotine equation

    Parameters
    ----------
    A : float
        Antoine constant A.
    B : float
        Antoine constant B.
    C : float
        Antoine constant C.
    T : float
        Temperature in K.

    Returns
    -------
    P : float
        Pressure in bar.

    '''
    log_P = A - B/(T + C)
    P = 10**log_P
    return P

def get_relative_volatility(P_i:float, P_j:float, gamma_inf_i:float,
                            gamma_inf_j:float):
    '''
    Computes the relative volatility of a system considering ideal vapor phase
    and non-ideal liquid phase. This assumption is true at low pressures. In
    case a solvent is present, this is called pseudo-binary relative volatility.

    A relative volatility above ~1.1 could be separated using normal distillation.
    In such cases, the key components' boiling point usually differ by more than
    50 K.

    Parameters
    ----------
    P_i : float
        Vapor pressure of pure species i.
    P_j : float
        Vapor pressure of pure species j.
    gamma_inf_i : float
        Activity coefficient at infinite dilution of solute i in the solvent.
    gamma_inf_j : float
        Activity coefficient at infinite dilution of solute j in the solvent.

    Returns
    -------
    a_ij : float
        Relative volatility of compound i over compound j.

    '''
    a_ideal = P_i/P_j                                # Ideal part
    s_ij = get_selectivity(gamma_inf_i,gamma_inf_j)  # Non-ideal part or selectivity
    a_ij = a_ideal * s_ij
    return a_ij

def get_gh_gnn_models(c_1, c_2, c_3):
    '''
    Returns a list of all GH-GNN models corresponding to all binary systems
    of a ternary mixture (two key components and solvent)

    Parameters
    ----------
    c_1 : dict
        Key component i as dict.
    c_2 : dict
        Key component j as dict.
    c_3 : dict
        Solvent as dict.

    Returns
    -------
    gh_gnn_models : list
        All GH-GNN models for every binary system of the ternary mixture.

    '''
    gh_gnn_12 = GH_GNN(c_1['smiles'], c_2['smiles'])
    gh_gnn_21 = GH_GNN(c_2['smiles'], c_1['smiles'])
    gh_gnn_13 = GH_GNN(c_1['smiles'], c_3['smiles'])
    gh_gnn_31 = GH_GNN(c_3['smiles'], c_1['smiles'])
    gh_gnn_23 = GH_GNN(c_2['smiles'], c_3['smiles'])
    gh_gnn_32 = GH_GNN(c_3['smiles'], c_2['smiles'])

    gh_gnn_models = [gh_gnn_12, gh_gnn_21, gh_gnn_13, gh_gnn_31, gh_gnn_23, gh_gnn_32]
    return gh_gnn_models

def get_gammas_GHGNN(K1s, K2s, T):
    ln_gs = []
    for K1, K2 in zip(K1s, K2s):
        ln_g = K1 + K2/T
        ln_gs.append(ln_g)
    return ln_gs

def get_constants_GHGNN(gh_gnn_models):
    K1s, K2s = [], []
    for gh_gnn in gh_gnn_models:
        K1, K2 = gh_gnn.predict(T=0, constants=True) # Rememeber T is a dumb T here
        K1s.append(K1)
        K2s.append(K2)
    return K1s, K2s

class margules_system():
    def __init__(self, ln_g_12:float, ln_g_21:float,
                       ln_g_13:float, ln_g_31:float,
                       ln_g_23:float, ln_g_32:float):

        self.A_12 = ln_g_12
        self.A_21 = ln_g_21

        self.A_13 = ln_g_13
        self.A_31 = ln_g_31

        self.A_23 = ln_g_23
        self.A_32 = ln_g_32

        A_123 = 0

        self.B_123 = 0.5*(self.A_12 + self.A_21 + self.A_13 + self.A_31 +
                          self.A_23 + self.A_32) - A_123

    def get_gammas(self, x1, x2, x3):

        part_12 = x1*x2*(x2*self.A_12 + x1*self.A_21)
        part_13 = x1*x3*(x3*self.A_13 + x1*self.A_31)
        part_23 = x2*x3*(x3*self.A_23 + x2*self.A_32)

        GE = part_12 + part_13 + part_23 + x1*x2*x3*self.B_123

        gamma_1 = np.exp(
            (
                2*(x1*x2*self.A_21 + x1*x3*self.A_31) + x2**2*self.A_12 +
                x3**2*self.A_13 + x2*x3*self.B_123 - 2*GE
            )
            )

        gamma_2 = np.exp(
            (
                2*(x2*x3*self.A_32 + x2*x1*self.A_12) + x3**2*self.A_23 +
                x1**2*self.A_21 + x3*x1*self.B_123 - 2*GE
            )
            )

        gamma_3 = np.exp(
            (
                2*(x3*x1*self.A_13 + x3*x2*self.A_23) + x1**2*self.A_31 +
                x2**2*self.A_32 + x1*x2*self.B_123 - 2*GE
            )
            )

        return gamma_1, gamma_2, gamma_3

def get_xs_for_SF(SF, n_points=30):

    x_k = np.repeat(SF/(1+SF), n_points)
    x_i = np.linspace(0,1-x_k[0],n_points)
    x_j = 1- x_k - x_i

    return x_i, x_j, x_k

class solvent_preselection():

    def __init__(self, mixture, solvents, AD, solvents_names=None):
        self.mixture = mixture
        self.solvents = solvents
        self.AD = AD
        if solvents_names is None:
            self.solvents_names = ['']*len(solvents)
        else:
            self.solvents_names = solvents_names

        # Create results folder
        self.results_folder = 'results/'
        if not os.path.exists(self.results_folder):
            os.makedirs(self.results_folder)

    def get_vapor_pressures(self, c_i, c_j, Ts):
        n_Ts = len(Ts)
        # Get vapor pressures for all temperatures
        P_i_lst = np.zeros(n_Ts)
        P_j_lst = np.zeros(n_Ts)
        for t, T in enumerate(Ts):
            # Compute vapor pressures
            Ai, Bi, Ci = get_Antoine_constants(c_i['smiles'], T)
            P_i_lst[t] = get_vapor_pressure_Antoine(Ai, Bi, Ci, T)

            Aj, Bj, Cj = get_Antoine_constants(c_j['smiles'], T)
            P_j_lst[t] = get_vapor_pressure_Antoine(Aj, Bj, Cj, T)
        return P_i_lst, P_j_lst

    def screen_with_rv(self, n_Ts=10):

        mixture = self.mixture
        solvents= self.solvents
        AD = self.AD
        solvents_names = self.solvents_names

        # Extract mixture information
        c_i = mixture['c_i']
        c_j = mixture['c_j']
        mixture_type = mixture['mixture_type']
        T_range = mixture['T_range']

        mixture = c_i['name'] + '_' + c_j['name'] # Mixture name

        # Temperatures to be evaluated
        Ts = np.linspace(T_range[0], T_range[1], n_Ts)

        # Create folder to store results of screening
        if not os.path.exists(self.results_folder + mixture_type):
            os.makedirs(self.results_folder + mixture_type)

        relative_volatilities_inf = np.zeros((len(solvents), len(Ts)))

        # Get vapor pressures for all temperatures
        P_i_lst, P_j_lst = self.get_vapor_pressures(c_i, c_j, Ts)

        # Check whether AD was computed before, in case not get storage
        if AD is not None:
            print('\n ------ > Computing AD')
            ad_file = self.results_folder + mixture_type + '/' + mixture + '_AD_' + AD + '.csv'
            if os.path.exists(ad_file):
                print(' ------ > AD already exists!\n')
                AD = None
            else:
                n_class_i_storage = np.zeros(len(solvents))
                n_class_j_storage = np.zeros(len(solvents))
                max_10_sim_i_storage = np.zeros(len(solvents))
                max_10_sim_j_storage = np.zeros(len(solvents))

        for s, solvent in enumerate(tqdm(solvents)):
            # Initialize GH-GNN models
            GH_GNN_i = GH_GNN(c_i['smiles'], solvent)
            GH_GNN_j = GH_GNN(c_j['smiles'], solvent)

            # Get AD
            if AD == 'both':
                feasible_sys_i, n_class_i_storage[s], max_10_sim_i_storage[s] = GH_GNN_i.get_AD(AD=AD)
                feasible_sys_j, n_class_j_storage[s], max_10_sim_j_storage[s] = GH_GNN_j.get_AD(AD=AD)
            elif AD is None:
                pass
            else:
                raise Exception('Current implementation only supports AD="both" or AD=None')

            # Get K1 and K2 constants, the temperature pass here could be any float or int
            K1_i, K2_i = GH_GNN_i.predict(T=0, constants=True)
            K1_j, K2_j = GH_GNN_j.predict(T=0, constants=True)

            for t, T in enumerate(Ts):

                P_i = P_i_lst[t]
                P_j = P_j_lst[t]

                ln_gamma_inf_i = K1_i + K2_i/T
                ln_gamma_inf_j = K1_j + K2_j/T

                gamma_inf_i = np.exp(ln_gamma_inf_i)
                gamma_inf_j = np.exp(ln_gamma_inf_j)

                a_inf = get_relative_volatility(P_i, P_j, gamma_inf_i, gamma_inf_j)
                relative_volatilities_inf[s, t] = a_inf

        if AD is not None:
            df_AD = pd.DataFrame({
                'Solvent_SMILES':solvents,
                'Solvent_name':solvents_names,
                'n_class_i': n_class_i_storage,
                'n_class_j': n_class_j_storage,
                'max_10_sim_i': max_10_sim_i_storage,
                'max_10_sim_j': max_10_sim_j_storage,
                })
            df_AD.to_csv(ad_file, index=False)
            print(' ------ > AD computed!\n')

        df_results = pd.DataFrame(relative_volatilities_inf, columns=Ts)
        df_results.insert(0, 'Solvent_SMILES', solvents)
        df_results.insert(1, 'Solvent_name', solvents_names)
        df_results.to_csv(self.results_folder+mixture_type+'/'+mixture+'_rv.csv', index=False)

    def screen_with_minSF(self, n_Ts=10, rv_threshold=3):

        mixture = self.mixture
        solvents= self.solvents
        AD = self.AD
        solvents_names = self.solvents_names

        # Extract mixture information
        c_i = mixture['c_i']
        c_j = mixture['c_j']
        mixture_type = mixture['mixture_type']
        T_range = mixture['T_range']

        mixture = c_i['name'] + '_' + c_j['name'] # Mixture name

        # Temperatures to be evaluated
        Ts = np.linspace(T_range[0], T_range[1], n_Ts)

        # Create folder to store results of screening
        if not os.path.exists(self.results_folder + mixture_type):
            os.makedirs(self.results_folder + mixture_type)

        minSFs = np.zeros((len(solvents), len(Ts)))

        # Get vapor pressures for all temperatures
        P_i_lst, P_j_lst = self.get_vapor_pressures(c_i, c_j, Ts)

        # Check whether AD was computed before, in case not get storage
        if AD is not None:
            print('\n ------ > Computing AD')
            ad_file = self.results_folder + mixture_type + '/' + mixture + '_AD_' + AD + '.csv'
            if os.path.exists(ad_file):
                print(' ------ > AD already exists!\n')
                AD = None
            else:
                n_class_i_storage = np.zeros(len(solvents))
                n_class_j_storage = np.zeros(len(solvents))
                max_10_sim_i_storage = np.zeros(len(solvents))
                max_10_sim_j_storage = np.zeros(len(solvents))

        for s, solvent in enumerate(tqdm(solvents)):
            # Initialize GH-GNN models
            c_k = {'smiles': solvent}
            gh_gnn_models = get_gh_gnn_models(c_i, c_j, c_k)

            GH_GNN_i = gh_gnn_models[2]
            GH_GNN_j = gh_gnn_models[4]

            # Get AD
            if AD == 'both':
                feasible_sys_i, n_class_i_storage[s], max_10_sim_i_storage[s] = GH_GNN_i.get_AD(AD=AD)
                feasible_sys_j, n_class_j_storage[s], max_10_sim_j_storage[s] = GH_GNN_j.get_AD(AD=AD)
            elif AD is None:
                pass
            else:
                raise Exception('Current implementation only supports AD="both" or AD=None')

            # Get K1 and K2 constants
            K1s, K2s = get_constants_GHGNN(gh_gnn_models)

            K1_i = K1s[2]
            K2_i = K2s[2]
            K1_j = K1s[4]
            K2_j = K2s[4]

            for t, T in enumerate(Ts):

                P_i = P_i_lst[t]
                P_j = P_j_lst[t]

                ln_gamma_inf_i = K1_i + K2_i/T
                ln_gamma_inf_j = K1_j + K2_j/T

                gamma_inf_i = np.exp(ln_gamma_inf_i)
                gamma_inf_j = np.exp(ln_gamma_inf_j)

                a_inf = get_relative_volatility(P_i, P_j, gamma_inf_i, gamma_inf_j)

                if a_inf < rv_threshold:
                     minSFs[s,t] = np.nan
                else:
                    ln_gammas_inf = get_gammas_GHGNN(K1s, K2s, T=T)
                    system = margules_system(*ln_gammas_inf)

                    def Rela_Vola(SF):
                        '''
                        Objective function to get minSF for rv=rv_threshold

                        It gets the minimum relative volatility accross all
                        compositions to ensure that the minSF is really the
                        minimum among all possible compositions.

                        '''
                        x_i, x_j, x_k = get_xs_for_SF(SF)
                        gamma_i, gamma_j, gamma_k = system.get_gammas(x_i, x_j, x_k)
                        relative_volatilities = get_relative_volatility(P_i, P_j, gamma_i, gamma_j)
                        return (rv_threshold - min(relative_volatilities))**2

                    # Minimize function to get minSF
                    results = minimize_scalar(Rela_Vola, bounds=(0,10000),
                                              method='bounded', options={'maxiter':2000})
                    minSFs[s,t] = results.x

        if AD is not None:
            df_AD = pd.DataFrame({
                'Solvent_SMILES':solvents,
                'Solvent_name':solvents_names,
                'n_class_i': n_class_i_storage,
                'n_class_j': n_class_j_storage,
                'max_10_sim_i': max_10_sim_i_storage,
                'max_10_sim_j': max_10_sim_j_storage,
                })
            df_AD.to_csv(ad_file, index=False)
            print(' ------ > AD computed!\n')

        df_results = pd.DataFrame(minSFs, columns=Ts)
        df_results.insert(0, 'Solvent_SMILES', solvents)
        df_results.insert(1, 'Solvent_name', solvents_names)
        df_results.to_csv('results/'+mixture_type+'/'+mixture+'_minSF.csv', index=False)

def get_selectivity(gamma_inf_i:float, gamma_inf_j:float):
    '''
    Computes the selectivity from actvity coefficients of key components

    Parameters
    ----------
    gamma_inf_i : float
        Activity coefficient at infinite dilution of solute i in the solvent.
    gamma_inf_j : float
        Activity coefficient at infinite dilution of solute j in the solvent.

    Returns
    -------
    s_ij : float
        Selectivity of compound i over compound j.

    '''
    s_ij = gamma_inf_i/gamma_inf_j
    return s_ij

def normalize_entrainer_free(x_i, y_i, x_j, y_j):
    # Normalization to entrainer-free basis
    x_i = x_i/(x_i + x_j)
    y_i = y_i/(y_i + y_j)
    x_j = 1-x_i
    y_j = 1-y_i
    return (x_i, y_i), (x_j, y_j)

def get_pseudo_binary_VLE_isothermal(c_i:dict, c_j:dict, T:float,
                                     margules_system, SF:float):
    '''
    Computes the vapor and liquid fractions of the key components in the
    pseudo-binary VLE at the given temperature

    Parameters
    ----------
    c_i : dict
        Info of component i
    c_j : dict
        Info of component j
    T : float
        Temperature of the system in K.
    margules_system : class
        Initialized margules system with the corresponding 6 infinite dilution
        activity coefficient values.
    SF : float
        Solvent to feed ratio.

    Returns
    -------
    tuple
        liquid and vapor fraction of components i and j
        (x_i, y_i), (x_j, y_j).

    '''
    x_i, x_j, x_k = get_xs_for_SF(SF)

    gamma_i, gamma_j, gamma_k = margules_system.get_gammas(x_i, x_j, x_k)

    smiles_i,  smiles_j = c_i['smiles'], c_j['smiles']

    Ai, Bi, Ci = get_Antoine_constants(smiles_i, T)
    Aj, Bj, Cj = get_Antoine_constants(smiles_j, T)

    P_i = get_vapor_pressure_Antoine(Ai, Bi, Ci, T)
    P_j = get_vapor_pressure_Antoine(Aj, Bj, Cj, T)

    p_i = x_i*gamma_i*P_i
    p_j = x_j*gamma_j*P_j
    P   = p_i + p_j

    y_i = p_i/P
    y_j = p_j/P

    (x_i, y_i), (x_j, y_j) = normalize_entrainer_free(x_i, y_i, x_j, y_j)

    relative_volatilities = get_relative_volatility(P_i, P_j, gamma_i, gamma_j)

    return [(x_i, y_i), (x_j, y_j)], relative_volatilities

def get_pseudo_binary_VLE_isothermal_thermo(c_i:dict, c_j:dict, c_k:dict, T:float,
                                            SF:float, model:str):

    x_i, x_j, x_k = get_xs_for_SF(SF)

    Ai, Bi, Ci = get_Antoine_constants(c_i['smiles'], T)
    Aj, Bj, Cj = get_Antoine_constants(c_j['smiles'], T)
    P_i = get_vapor_pressure_Antoine(Ai, Bi, Ci, T)
    P_j = get_vapor_pressure_Antoine(Aj, Bj, Cj, T)

    y_i, y_j = np.zeros(x_i.shape[0]), np.zeros(x_i.shape[0])
    gamma_i = np.zeros(x_i.shape[0])
    gamma_j = np.zeros(x_i.shape[0])
    for i in range(x_i.shape[0]):
        xs = [x_i[i], x_j[i], x_k[i]]

        if model == 'UNIFAC_Do':
            GE = UNIFAC.from_subgroups(chemgroups=[c_i['UNIFAC_Do groups'], c_j['UNIFAC_Do groups'], c_k['UNIFAC_Do groups']],
                                        T=T,
                                        xs=xs,
                                        version=1)
        else:
            pass

        gamma_1, gamma_2, gamma_3 = GE.gammas()
        p_i = x_i[i]*gamma_1*P_i
        p_j = x_j[i]*gamma_2*P_j
        P_calc   = p_i + p_j

        y_i[i] = p_i/P_calc
        y_j[i] = p_j/P_calc

        gamma_i[i] = gamma_1
        gamma_j[i] = gamma_2

    (x_i, y_i), (x_j, y_j) = normalize_entrainer_free(x_i, y_i, x_j, y_j)

    relative_volatilities = get_relative_volatility(P_i, P_j, gamma_i, gamma_j)

    return [(x_i, y_i), (x_j, y_j)], relative_volatilities

def get_pseudo_binary_VLE_isobaric_thermo(c_i:dict, c_j:dict, c_k:dict, P:float,
                                            SF:float, bounds, model:str):

    x_i, x_j, x_k = get_xs_for_SF(SF)

    # Optimization
    p_is = np.zeros(x_i.shape[0])
    p_js = np.zeros(x_i.shape[0])
    for i in tqdm(range(x_i.shape[0])):
        xs = [x_i[i], x_j[i], x_k[i]]

        def error_in_P(T, P_true=P):

            Ai, Bi, Ci = get_Antoine_constants(c_i['smiles'], T)
            Aj, Bj, Cj = get_Antoine_constants(c_j['smiles'], T)

            P_i = get_vapor_pressure_Antoine(Ai, Bi, Ci, T)
            P_j = get_vapor_pressure_Antoine(Aj, Bj, Cj, T)

            if model == 'UNIFAC_Do':
                GE = UNIFAC.from_subgroups(chemgroups=[c_i['UNIFAC_Do groups'], c_j['UNIFAC_Do groups'], c_k['UNIFAC_Do groups']],
                                            T=T,
                                            xs=xs,
                                            version=1)
            else:
                pass

            gamma_i, gamma_j, gamma_k = GE.gammas()

            p_i = x_i[i]*gamma_i*P_i
            p_j = x_j[i]*gamma_j*P_j
            P_calc   = p_i + p_j

            return np.abs(P_calc - P_true)

        results = minimize_scalar(error_in_P, bounds=bounds, method='bounded')

        T = results.x

        Ai, Bi, Ci = get_Antoine_constants(c_i['smiles'], T)
        Aj, Bj, Cj = get_Antoine_constants(c_j['smiles'], T)

        P_i = get_vapor_pressure_Antoine(Ai, Bi, Ci, T)
        P_j = get_vapor_pressure_Antoine(Aj, Bj, Cj, T)

        if model == 'UNIFAC_Do':
            GE = UNIFAC.from_subgroups(chemgroups=[c_i['UNIFAC_Do groups'], c_j['UNIFAC_Do groups'], c_k['UNIFAC_Do groups']],
                                        T=T,
                                        xs=xs,
                                        version=1)
        else:
            pass

        gamma_i, gamma_j, gamma_k = GE.gammas()

        p_is[i] = x_i[i]*gamma_i*P_i
        p_js[i] = x_j[i]*gamma_j*P_j

    y_i = p_is/P
    y_j = p_js/P

    (x_i, y_i), (x_j, y_j) = normalize_entrainer_free(x_i, y_i, x_j, y_j)

    return (x_i, y_i), (x_j, y_j)

def get_pseudo_binary_VLE_isobaric(c_i:dict, c_j:dict, P:float,
                                     K1s:list, K2s:list, SF:float, bounds):
    '''
    Computes the vapor and liquid fractions of the key components in the
    pseudo-binary VLE at the given pressure in bar

    Parameters
    ----------
    c_i : dict
        Info of component i
    c_j : dict
        Info of component j
    P : float
        Pressure of the system in bar.
    K1s : list
        Collection of K1 parameters for GH-GNN models.
    K2s : list
         Collection of K2 parameters for GH-GNN models.
    SF : float
        Solvent to feed ratio.
    bounds : tuple
        Temperature bounds

    Returns
    -------
    tuple
        liquid and vapor fraction of components i and j
        (x_i, y_i), (x_j, y_j).

    '''
    x_i, x_j, x_k = get_xs_for_SF(SF)
    smiles_i,  smiles_j = c_i['smiles'], c_j['smiles']

    # Optimization
    p_is = np.zeros(x_i.shape[0])
    p_js = np.zeros(x_i.shape[0])
    for i in tqdm(range(x_i.shape[0])):

        def error_in_P(T, P_true=P):

            Ai, Bi, Ci = get_Antoine_constants(smiles_i, T)
            Aj, Bj, Cj = get_Antoine_constants(smiles_j, T)

            P_i = get_vapor_pressure_Antoine(Ai, Bi, Ci, T)
            P_j = get_vapor_pressure_Antoine(Aj, Bj, Cj, T)

            ln_gammas = get_gammas_GHGNN(K1s, K2s, T=T)
            system = margules_system(*ln_gammas)

            gamma_i, gamma_j, gamma_k = system.get_gammas(x_i[i], x_j[i], x_k[i])

            p_i = x_i[i]*gamma_i*P_i
            p_j = x_j[i]*gamma_j*P_j
            P_calc   = p_i + p_j

            return np.abs(P_calc - P_true)

        results = minimize_scalar(error_in_P, bounds=bounds, method='bounded', options={'maxiter':2000})

        T = results.x

        Ai, Bi, Ci = get_Antoine_constants(smiles_i, T)
        Aj, Bj, Cj = get_Antoine_constants(smiles_j, T)

        P_i = get_vapor_pressure_Antoine(Ai, Bi, Ci, T)
        P_j = get_vapor_pressure_Antoine(Aj, Bj, Cj, T)

        ln_gammas = get_gammas_GHGNN(K1s, K2s, T=T)
        system = margules_system(*ln_gammas)

        gamma_i, gamma_j, gamma_k = system.get_gammas(x_i[i], x_j[i], x_k[i])

        p_is[i] = x_i[i]*gamma_i*P_i
        p_js[i] = x_j[i]*gamma_j*P_j

    y_i = p_is/P
    y_j = p_js/P

    (x_i, y_i), (x_j, y_j) = normalize_entrainer_free(x_i, y_i, x_j, y_j)

    return (x_i, y_i), (x_j, y_j)

def plot_pseudoVLE(SFs, VLEs_lst, exp_lst, c_1, c_2, c_3, T_P,
                              folder_figures, mode, model):
    # Set the default color cycle
    mpl.rcParams['axes.prop_cycle'] = mpl.cycler(color=['#b3cde0', '#6497b1', '#005b96', '#03396c', '#011f4b'])
    mks = ['*', '^', 's', 'd', 'x']

    fig = plt.figure(figsize=(6,5))
    plt.plot([0,1], [0,1], 'k--', label='x=y')
    for i, SF in enumerate(SFs):
        (x_i, y_i), (x_j, y_j) = VLEs_lst[i]
        plt.plot(x_i, y_i, '-', label='SF ' + str(SF))

    for i, d in enumerate(exp_lst):
        sf_exp = d['SF']
        df_exp = d['df']
        plt.plot(df_exp['x'], df_exp['y'], 'k'+mks[i], label='Exp. SF='+str(sf_exp))
    ax = plt.gca()
    ax.yaxis.get_ticklocs(minor=True)
    ax.minorticks_on()
    plt.xlim(0,1)
    plt.ylim(0,1)
    plt.xlabel('Molar liquid fraction ' + c_1['name'], fontsize=14)
    plt.ylabel('Molar vapor fraction ' + c_1['name'], fontsize=14)
    plt.legend(ncol=2)
    if mode == 'T':
        plt.title(f'Isothermal pseudo-binary VLE at {T_P} K ', fontsize=14)
        fig_name = '/'+model+'_isothermal_'+str(T_P)
    elif mode == 'P':
        plt.title(f'Isobaric pseudo-binary VLE at {T_P} bar ', fontsize=14)
        fig_name = '/'+model+'_isobaric_'+str(T_P)
    plt.close(fig)
    name = c_1['name'] + ',' + c_2['name'] + ',' + c_3['name']
    fig.savefig(folder_figures+fig_name+'_'+name+'.png', dpi=300, format='png')
    # fig.savefig(folder_figures+fig_name+'_'+name+'.svg', dpi=300, format='svg')

def plot_relative_vola_SFs(SFs, alphas_lst, VLEs_lst, c_1, c_2, c_3, T,
                              folder_figures, model):
    # Set the default color cycle
    mpl.rcParams['axes.prop_cycle'] = mpl.cycler(color=['#b3cde0', '#6497b1', '#005b96', '#03396c', '#011f4b'])
    fig = plt.figure(figsize=(6,5))
    for i, SF in enumerate(SFs):
        (x_i, y_i), (x_j, y_j) = VLEs_lst[i]
        alphas = alphas_lst[i]
        plt.plot(x_i, alphas, '-', label='SF ' + str(SF))
    ax = plt.gca()
    ax.yaxis.get_ticklocs(minor=True)
    ax.minorticks_on()
    plt.xlim(0,1)
    plt.xlabel('Molar liquid fraction ' + c_1['name'], fontsize=14)
    plt.ylabel('Pseudo-relative volatility ', fontsize=14)
    plt.legend(ncol=2)
    plt.title(f'Isothermal pseudo-relative volatility at {T} K ', fontsize=14)
    fig_name = '/'+model+'_isothermal_RV_'+str(T)

    plt.close(fig)
    name = c_1['name'] + ',' + c_2['name'] + ',' + c_3['name']
    fig.savefig(folder_figures+fig_name+'_'+name+'.png', dpi=300, format='png')
    # fig.savefig(folder_figures+fig_name+'_'+name+'.svg', dpi=300, format='svg')


import cv2
import imutils
import numpy as np
import PIL
import pytesseract

#가우시안 필터 적용 및 숫자 검출률을 높인 버전

img=cv2.imread("testimg3.jpg",cv2.IMREAD_COLOR)
copy_img=img.copy()  #just image Read
cv2.imshow('src', copy_img )

img2=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #그레이 적용
cv2.imshow('gray_img2', img2 )

blur = cv2.GaussianBlur(img2,(3,3),0) #가우시안 필터 블루
cv2.imwrite('blur.jpg',blur)
cv2.imshow('blur', blur )

canny = cv2.Canny(blur,100,200) #엣지 검출
cv2.imwrite('canny.jpg',canny)
cv2.imshow('canny', canny )

'''
cv2.Canny에 적용되는 Threshold1,2 의 값은 특정 사진에 맞춰 설정해준 값입니다. 이외에도 가우시안 값에 적용시킨 3x3 매트릭스 값도 마찬 가지입니다.
Edge에 대한 설명을 조금 추가하자면, 흑백영상에서 명암의 밝기 차이에 대한 변화율이라고 생각하시면 됩니다.
 흑백이미지에 대한 미분 값
'''

#같은 에너지를 가지는 점들을 연결한 선 (번호판) 검은 바탕에 찾는 물체는 흰색 그래서 그레이로 변환

contours, hierarchy = cv2.findContours(canny, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cv2.drawContours(canny, contours, -1, 3)

#전체 이미지에서 Contour의 가로 세로 비율 값과 면적을 통해,
#번호판 영역에 벗어난 걸로 추정되는 값들은 제외 시켜주어야함
box1=[]
f_count=0
select=0
plate_width=0

for i in range(len(contours)):
    cnt=contours[i]
    area = cv2.contourArea(cnt)
    x,y,w,h = cv2.boundingRect(cnt)
    rect_area=w*h #area size
    aspect_ratio = float(w)/h #ratio = width/height

    if(aspect_ratio>0.2)and(aspect_ratio<=1.0)and(rect_area>=100)and(rect_area<=700):
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),1)
        box1.append(cv2.boundingRect(cnt))

cv2.imshow('contour', img ) #콘투어 다 찾기

###절취선 이미지라이트 때문에 그런듯 콘투어는 다 찾아짐
for i in range(len(box1)): ##버블정렬을 이용
    for j in range(len(box1)-(i+1)):
        if box1[j][0]>box1[j+1][0]:
            temp=box1[j]
            box1[j]=box1[j+1]
            box1[j+1]=temp

for m in range(len(box1)):
    count=0
    for n in range(m+1, (len(box1)-1)):
        delta_x=abs(box1[n+1][0]-box1[m][0])
        if delta_x >150:
            break
        delta_y=abs(box1[n+1][1]-box1[m][1])
        if delta_x ==0:
            delta_x=1
        if delta_y ==0:
            delta_y=1
        gradient=float(delta_y)/float(delta_x)
        if gradient<0.25:
            count=count+1
        #measure number plate size
    if count > f_count:
        select = m
        f_Count = count
        plate_width=delta_x
    cv2.imwrite('snake.jpg',img)

number_plate=copy_img[box1[select][1]-10:box1[select][3]+box1[select][1]+20,box1[select][0]-10:140+box1[select][0]]

resize_plate=cv2.resize(number_plate,None,fx=1.8,fy=1.8,interpolation=cv2.INTER_CUBIC+cv2.INTER_LINEAR)
plate_gray=cv2.cvtColor(resize_plate,cv2.COLOR_BGR2GRAY)
ret,th_plate = cv2.threshold(plate_gray,150,255,cv2.THRESH_BINARY)

cv2.imwrite('plate_th.jpg',th_plate)
kernel = np.ones((3,3),np.uint8)
er_plate = cv2.erode(th_plate,kernel,iterations=1)
er_invplate = er_plate
cv2.imwrite('er_plate.jpg',er_invplate)
result = pytesseract.image_to_string(Image.open('er_plate.jpg'), lang='kor')
result.replace(" ","")

recogtest=Recognition()
result=recogtest.ExtractNumber()
print(result)
###절취선 이미지라이트 때문에 그런듯
cv2.waitKey(0)
cv2.destroyAllWindows()


"""main"""
def main():
    """main docx"""
    import json
    txt = json.loads(input())
    lim = float(input())
    human = [i for i in txt]
    human = sorted(list(filter(lambda num: int(txt[num]) >= lim, human)))
    if human == []:
        print("Nope")
        return
    _ = [print(human[i], "%.2f"%txt[human[i]], sep="\t") for i in range(len(human))]

main()


import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
import scipy.io

# 1. Загрузите данные ex3data1.mat из файла.

data = scipy.io.loadmat('ex3data1.mat')

X_train, y_train = data['X'], data['y']
X_valid, y_valid = data['Xval'], data['yval']
X_test, y_test = data['Xtest'], data['ytest']

# добавляем  bias

X_train = np.insert(X_train, 0, 1, axis=1)
X_valid = np.insert(X_valid, 0, 1, axis=1)
X_test = np.insert(X_test, 0, 1, axis=1)

# 2. Постройте график, где по осям откладываются X и y из обучающей выборки.

plt.scatter(X_train[:,1], y_train)
plt.ylabel('Объем воды, вытекающий из дамбы')
plt.xlabel('Изменения уровня воды')
plt.grid()
plt.show()

# 3. Реализуйте функцию стоимости потерь для линейной регрессии с L2-регуляризацией.

def h(theta, X):
    return np.dot(X, theta)

def J(theta, X, y, lmb=0.):
    m = len(X)
    error = 0
    reg = 0

    h_res = h(theta, X).reshape(-1, 1)
    error = np.sum(np.power((h_res - y), 2)) / (2 * m)

    if lmb != 0:
        reg = np.sum(np.power(theta[1:], 2) * lmb) / (2 * m)

    return error + reg

theta = np.zeros(X_train.shape[1])
print(J([1, 1], X_train, y_train, 0.5))

# 4. Реализуйте функцию градиентного спуска для линейной регрессии с L2-регуляризацией.

def gd_step(theta, X, y, lmb=0.):
    m = len(X)
    gradient = 0

    h_res = h(theta, X).reshape(-1, 1)

    gradient = np.dot(X.T, (h_res - y)) / m

    if lmb != 0:
        reg = ((lmb / m) * np.array(theta)).reshape(-1, 1)
        gradient += reg

    return gradient

def gd_step_flatten(theta, X, y, lmb=0.):
    return gd_step(theta, X, y, lmb).flatten()

print(gd_step_flatten([1, 1], X_train, y_train, 0.5))

# 5. Постройте модель линейной регрессии с коэффициентом регуляризации 0 и постройте график полученной функции совместно
# с графиком из пункта 2. Почему регуляризация в данном случае не сработает?

def lin_reg(X, y, lmb=0.):
    theta = np.zeros(X.shape[1])
    calc_theta = optimize.fmin_cg(
        J,
        x0=theta,
        fprime=gd_step_flatten,
        args=(X, y, lmb),
        disp=False
    )

    return calc_theta

theta_1 = lin_reg(X_train, y_train, 0)

y_pred = h(theta_1, X_train)
plt.scatter(X_train[:,1], y_train)
plt.plot(X_train[:,1], y_pred, 'r-')
plt.ylabel('Объем воды, вытекающий из дамбы')
plt.xlabel('Изменения уровня воды')
plt.grid()
plt.show()

# L2-регуляризация помогает решить пробелму переобучения (overfitting, high variance),
# "сгладить кривую". Если посмотреть на гарфик, то мы увидим, что наша модель страдает от недообучения
# (underfitting, high bias), следовательно L2-регуляризация тут не поможет.

# 6. Постройте график процесса обучения (learning curves) для обучающей и валидационной выборки. По оси абсцисс откладывается число элементов из обучающей выборки, а по оси ординат - ошибка (значение функции потерь) для обучающей выборки (первая кривая) и валидационной выборки (вторая кривая). Какой вывод можно сделать по построенному графику?

def draw_learning_curves(X_train, y_train, X_valid, y_valid, lmb=0.):
    m = len(X_train)

    error_train = []
    error_validation = []
    data_count = []

    for i in range(1, m):
        X_train_subset = X_train[:i, :]
        y_subset = y_train[:i]
        theta = lin_reg(X_train_subset, y_subset, lmb)

        error_train.append(J(theta, X_train_subset, y_subset, lmb))
        error_validation.append(J(theta, X_valid, y_valid, lmb))
        data_count.append(i + 1)

    plt.figure(figsize=(8, 5))
    plt.plot(data_count, error_train, label='Train')
    plt.plot(data_count, error_validation, label='Validation')
    plt.legend()
    plt.xlabel('Кол-во данных')
    plt.ylabel('Ошибка')
    plt.grid(True)
    plt.show()

    print(f'train_error: {error_train[-1]}')
    print(f'validation_error: {error_validation[-1]}')

    return error_train, error_validation

draw_learning_curves(X_train, y_train, X_valid, y_valid)

# По графику можно сделать вывод, что модель страдает от "недообучения"(underfitting, high bias). Ошибка модели высока и
# добавление большего количества данных не принесет значимого прироста точности.

# 7. Реализуйте функцию добавления p - 1 новых признаков в обучающую выборку (X2, X3, X4, …, Xp).

def gen_polynom(X, p):
    X_new = np.ones([len(X), p + 1])
    for i in range(1, p + 1):
        X_new[:, i] = X[:, 1] ** i;

    return X_new

print(gen_polynom(np.array([[1, 2], [1, 3], [1, 4]]), 3))

# 8. Поскольку в данной задаче будет использован полином высокой степени, то необходимо перед обучением произвести
# нормализацию признаков.

def create_normalizer(data):
    wo_bias = data[:, 1:]
    mean = np.mean(wo_bias, axis=0)
    range = np.max(wo_bias, axis=0) - np.min(wo_bias, axis=0)
    std = np.std(wo_bias, axis=0)

    def norm_func(val):
        cp = val.copy()
        cp[:, 1:] -= mean
        # cp[:,1:] /= range
        cp[:, 1:] /= std

        return cp

    def denorm_func(val):
        cp = val.copy()
        # cp[:,1:] *= range
        cp[:, 1:] *= std
        cp[:, 1:] += mean

        return cp

    return norm_func, denorm_func

tmp = np.array([
    [1, 3, 0.2],
    [1, 314, 1342,],
    [1, 0.2, 75],
])

norm_func, denorm_func = create_normalizer(tmp)
print('orig')
print(tmp)
print('norm_func')
print(norm_func(tmp))
print('denorm_func')
print(denorm_func(norm_func(tmp)))

# 9. Обучите модель с коэффициентом регуляризации 0 и p = 8.
p = 8
X_train_poly = gen_polynom(X_train, p)
X_valid_poly = gen_polynom(X_valid, p)

norm_func, denorm_func = create_normalizer(X_train_poly)

X_train_poly_norm = norm_func(X_train_poly)
X_valid_poly_norm = norm_func(X_valid_poly)

theta_poly_2 = lin_reg(X_train_poly_norm, y_train, 0)

# 10. Постройте график модели, совмещенный с обучающей выборкой, а также график процесса обучения. Какой вывод можно
# сделать в данном случае?

def draw_theta(theta, X, Y):
    x = np.linspace(np.min(X) - 10, np.max(X) + 10, 100).reshape(-1, 1)
    x_pol = gen_polynom(np.insert(x, 0, 1, axis=1), p)

    x_pol = norm_func(x_pol)

    y_poly_pred = h(theta, x_pol)

    plt.figure(figsize=(8, 5))
    plt.scatter(X[:, 1], y_train, color='red')
    plt.plot(x, y_poly_pred)
    plt.ylabel('Объем воды, вытекающий из дамбы')
    plt.xlabel('Изменения уровня воды')
    plt.grid()
    plt.show()

draw_theta(theta_poly_2, X_train, y_train)
_, _ = draw_learning_curves(X_train_poly_norm, y_train, X_valid_poly_norm, y_valid)

# Если посмотреть на график функции, то видно, что она очень хорошо обучилась на "train" данных и имеет большой процент
# точности, в то же время, если посмотреть на график "learning curves", то мы увидим, что ошибка "train"
# очень мала - 0.03, а ошибка "validation" большая - 45.5. Эти показатели свидетельствуют о том, что модель
# "переобучилась" (overfitting, high variance). Для того, чтобы модель не переобучилась, можно использовать L2-регуляризацию.

# 11. Постройте графики из пункта 10 для моделей с коэффициентами регуляризации 1 и 100. Какие выводы можно сделать?

lambda_1 = 1

theta_poly_reg_1 = lin_reg(X_train_poly_norm, y_train, lambda_1)
draw_theta(theta_poly_reg_1, X_train, y_train)

_, _ = draw_learning_curves(X_train_poly_norm, y_train, X_valid_poly_norm, y_valid, lambda_1)

# Если посмотреть на гарфик фкнции при (lambda = 1), то можно заметить, что хоть модель и не имеет такой процент
# точности как при (lambda = 0) однако она довольно точно обобщает входящие данных. Если посмотреть на график
# "learning curves" то мы увидим, что хоть ошибка "train" возросла с 0.03 до 6.49, зато ошибка "validation" уменьшилась
# с 45.5 до 12.3. Даныне метрики говорят о том, что у модели отсутсовует "overfitting, high variance" и "underfitting, high bias".

lambda_100 = 100

theta_poly_reg_100 = lin_reg(X_train_poly_norm, y_train, lambda_100)
draw_theta(theta_poly_reg_100, X_train, y_train)
_, _ = draw_learning_curves(X_train_poly_norm, y_train, X_valid_poly_norm, y_valid, lambda_100)

# Если посмотреть на гарфик фкнции при (lambda = 100), то можно заметить, что хоть модель очень плохо обобщает входящие
# данные, и по графику она поможа на модель без полиномиальных признаков. Если посмотреть на график "learning curves"
# то мы увидим, что ошибки "train" и "validation" очень вклики, что говорит нам о том, что модель "недообучена"
# (underfitting, high bias). Решить проблему можно уменьшив параметр lambda.

# 12. С помощью валидационной выборки подберите коэффиент регуляризации, который позволяет достичь наименьшей ошибки.
# Процесс подбора отразите с помощью графика (графиков).

lambdas = np.linspace(0, 2.5, 16)
train_error = []
validation_error = []

for i in range(len(lambdas)):
    lmb = lambdas[i]

    theta_lmbd = lin_reg(X_train_poly_norm, y_train, lmb)

    train_error.append(J(theta_lmbd, X_train_poly_norm, y_train, lmb))
    validation_error.append(J(theta_lmbd, X_valid_poly_norm, y_valid, lmb))

plt.figure(figsize=(12,5))
plt.plot(lambdas, train_error, label='Train')
plt.plot(lambdas, validation_error, label='Cross Validation')
plt.legend()
plt.xlabel('lambda')
plt.ylabel('Ошибка')
plt.xticks(lambdas)
plt.grid(True)
plt.show()

# 13. Вычислите ошибку (потерю) на контрольной выборке.

best_lambda = 1.17
best_theta = lin_reg(X_train_poly_norm, y_train, best_lambda)
train_j = J(best_theta, X_train_poly_norm, y_train, best_lambda)
valid_j = J(best_theta, X_valid_poly_norm, y_valid, best_lambda)

X_test_poly = gen_polynom(X_test, p)
X_test_poly_norm = norm_func(X_test_poly)

test_j = J(best_theta, X_test_poly_norm, y_test, best_lambda)

print(f'train_j = {train_j}')
print(f'valid_j = {valid_j}')
print(f'test_j = {test_j}')


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

"""
``rand`` is a module that provides ``random`` utilities,
such as:

* globally setting a seed value
* random integers between a range
* random floats between a range
* return ``True`` or ``False`` based on a probability (the ``maybe`` function)
* return random data
"""

import random as r
import six

RANDOM = r.Random()
_randint = RANDOM.randint
random = _random = RANDOM.random
choice = _choice = RANDOM.choice

def seed(val):
    """Set the seed for any subsequent random values/choices

    :param val: The random seed value
    """
    RANDOM.seed(val)

def weighted_choice(items, probabilities):
    """Returns a randomly-chosen item from ``items`` using the ``probabilities``
    tuple/list to determine probabilities.

    Assumes all weights add up to 1.0

    E.g.

    .. code-block:: python

        # 10% chance of 1
        # 30% chance of 2
        # 60% chance of 3
        weighted_choice([1, 2, 3], [0.1, 0.3, 0.6])
    """
    rand_val = RANDOM.random()

    total_probability = 0.0
    for item, prob in zip(items, probabilities):
        if total_probability <= rand_val < total_probability+prob:
            return item
        total_probability += prob
    raise Exception("Probabilities did not add up to 1.0")

def randint(a, b=None):
    """Return a random integer

    :param int a: Either the minimum value (inclusive) if ``b`` is set, or
    the maximum value if ``b`` is not set (non-inclusive, in which case the minimum
    is implicitly 0)
    :param int b: The maximum value to generate (non-inclusive)
    :returns: int
    """
    # need to minus 1 b/c randint has an inclusive maximum
    if b is None:
        return _randint(0, a-1)
    else:
        return _randint(a, b-1)

def randfloat(a, b=None):
    """Return a random float

    :param float a: Either the minimum value (inclusive) if ``b`` is set, or
    the maximum value if ``b`` is not set (non-inclusive, in which case the minimum
    is implicitly 0.0)
    :param float b: The maximum value to generate (non-inclusive)
    :returns: float
    """
    if b is None:
        max_ = a
        min_ = 0.0
    else:
        min_ = a
        max_ = b

    diff = max_ - min_
    res = _random()
    res *= diff
    res += min_
    return res

def maybe(prob=0.5):
    """Return ``True`` with ``prob`` probability.

    :param float prob: The probability ``True`` will be returned
    :returns: bool
    """
    return _random() < prob

def _binchoice(iterable):
    """Like random.choice but it works on bytes
    """
    idx = randint(len(iterable))
    return iterable[idx:idx+1]

def data(length, charset):
    """Generate ``length`` random characters from charset ``charset``

    :param int length: The number of characters to randomly generate
    :param str charset: The charset of characters to choose from
    :returns: str
    """
    return b"".join(_binchoice(charset) for x in six.moves.range(length))


"""Prepares the files and directories for a new dataset."""

import os
from subprocess import Popen, PIPE

import common
from fake_refgene import main as refgene
from utils.tools import TOOLS
from utils.datasets import DATASETS

# Configures the indices which will be generated.
BWA_EXTS = [".amb", ".ann", ".bwt", ".pac", ".sa"]
BOWTIE_VERSIONS = [1, 2]
INDEXES = ["bwa"] + ["bowtie%d" % version for version in BOWTIE_VERSIONS]

def get_args():
    parser = common.default_parser(
        "Prepares the files and directories for a new dataset.", True)
    return parser.parse_args()

def make_dir(path):
    """Creates a directory if it does not already exist."""
    if not os.path.exists(path):
        os.makedirs(path)

def make_dirs(dataset):
    """Creates all the required directories for the dataset."""
    dirs =  [os.path.join(dataset.get_out_path(), tool) for tool in TOOLS] +\
            [os.path.join(dataset.get_indexes_path(), idx) for idx in INDEXES]
    [make_dir(d) for d in dirs]

def make_refgenes(dataset, chrs):
    """Creates the refGene files for the artificial genomes of the dataset.

    Args:
        dataset: A Dataset instance of the new dataset.
        chrs: A list of chromosome numbers included in the dataset.

    Writes:
        All the refGene files.
    """
    for chr_num in chrs:
        refgene_args = [
            "-c", str(chr_num),
            "-p", dataset.work_dir,
            "-d", dataset.name,
        ]
        refgene(refgene_args)

def make_bowtie_indices(dataset, chrom):
    """Creates the Bowtie indices for the artificial genome of the chromosome.

    Args:
        dataset: A Dataset instance of the new dataset.
        chrom: A Chromosome instance for which to create the indices.

    Writes:
        All the Bowtie indices for the chromosome.
    """
    index_name = "%s_chr%d" % (dataset.name, chrom.num)

    for bowtie_version in BOWTIE_VERSIONS:
        index_type = "bowtie%d" % bowtie_version
        bowtie_cmd = [
            "bowtie%d-build" % bowtie_version,
            "-f", chrom.get_path(),
            os.path.join(dataset.get_indexes_path(), index_type, index_name)
        ]
        _ = Popen(bowtie_cmd).wait()

def make_bwa_index(dataset, chrom):
    """Creates the BWA index for the artificial genome of the chromosome.

        Args:
            dataset: A Dataset instance of the new dataset.
            chrom: A Chromosome instance for which to create the index.

        Writes:
            The BWA index for the chromosome.
        """
    bwa_cmd = ["bwa", "index", chrom.get_path()]
    _ = Popen(bwa_cmd).wait()
    for ext in BWA_EXTS:
        file_path = chrom.get_path() + ext
        file_nme = os.path.basename(file_path)
        os.rename(file_path,
                  os.path.join(dataset.get_indexes_path(), "bwa", file_nme))

def make_indices(dataset, chrs):
    """Creates the indices for the artificial genomes of the dataset.

    Args:
        dataset: A Dataset instance of the new dataset.
        chrs: A list of chromosome numbers included in the dataset.

    Writes:
        All the indices.
    """
    for chr_num in chrs:
        chrom = dataset.get_chr(chr_num)
        # Creates bowtie indices.
        make_bowtie_indices(dataset, chrom)
        # Creates BWA index.
        make_bwa_index(dataset, chrom)

def make_2bit_and_sizes(dataset, chrs):
    """Creates 2bit and sizes files for the artificial genomes of the dataset.

    Args:
        dataset: A Dataset instance of the new dataset.
        chrs: A list of chromosome numbers included in the dataset.

    Writes:
        All the 2bit and sizes files.
    """
    for chr_num in chrs:
        chrom = dataset.get_chr(chr_num)
        fa_file = chrom.get_path()
        twobit_file = chrom.get_path("2bit")
        sizes_file = chrom.get_path("sizes")

        _ = Popen(["faToTwoBit", fa_file, twobit_file]).wait()
        info = Popen(["twoBitInfo", twobit_file, "stdout"], stdout=PIPE)
        info.wait()
        sizes = Popen(["sort", "-k2rn"], stdin=info.stdout, stdout=PIPE)
        sizes.wait()
        with open(sizes_file, 'w') as fd:
            fd.write(sizes.stdout.read())

def main():
    """Prepares the files and directories for a new dataset.

    For command line help, run with the '-h' flag.
    """
    args = get_args()
    dataset = DATASETS[args.dataset]
    dataset.set_work_dir(args.path)

    make_dirs(dataset)
    make_refgenes(dataset, args.chrs)
    make_indices(dataset, args.chrs)
    make_2bit_and_sizes(dataset, args.chrs)

if __name__ == '__main__':
    main()


def dict_sort_func(par, the_sensors):
    sensor = the_sensors.get(par)
    return sensor.get('index')

def get_sensor_names(sensor_dict):
    # Nach index-Feld Sortierte Sensornamen
    f_names = []
    sorted_dict = sorted(sensor_dict, key=lambda key: dict_sort_func(key, sensor_dict))
    for key in sorted_dict:
        sensor = sensor_dict.get(key)
        if sensor.get('index') > 0:
            f_names.append(key)
    return tuple(f_names)

def get_field_names(sensor_dict, sensor_names):
    # Nach index-Feld Sortierte Feldnamen
    field_names = []
    for sensor_name in sensor_names:
        field_names.append(sensor_dict.get(sensor_name).get('field_name'))
    return tuple(field_names)


from typing import Text

def encrypt(text,s):
    text=text.upper()

    result=""
    for i in text:
        if i.isspace():
            result+=""
            continue
        result+= text[(text.index(i)+shift)%len(text)]
    return result

text=input("Introduce un texto")
shift=int(input("Introduce el shift"))
print(encrypt(text,shift))


#! /usr/bin/env python
from __future__ import print_function, division

import argparse

from astropy.coordinates import SkyCoord
from astropy.io.fits import getheader
import astropy.units as u

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('meta')
    parser.add_argument('--degrees', action='store_true')
    args = parser.parse_args()

    header = getheader(args.meta)
    pointing = SkyCoord(header['RA'], header['DEC'], unit=(u.degree, u.degree))
    if args.degrees:
        print(pointing.ra.deg, pointing.dec.deg)
    else:
        print(pointing.to_string('hmsdms'))

if __name__ == '__main__':
    main()


def get_max_flow(graph: list, source: int, sink: int, n: int):# another way to get the max flow: dinics algo.
    #change graph into useful representations
    if sink==source:
        return -1
    adj = [[] for _ in range(n)]
    # flow, capacity  (maybe this representation is more readable than the last one one)
    level = [-1 for _ in range(n)]# the level of the nodes in the current graph, higher level->closer to goal/sink
    for edge in graph:# we don't like the repr. we ger-> change to adj. matrix
        adj[edge[0]].append([edge[1], 0, edge[2], len(adj[edge[1]])])# dest, flow, capacity, idx of back edge
        adj[edge[1]].append([edge[0], 0, 0, len(adj[edge[0]])])# same for back edge

    def bfs(s, t):# the algo itself does not specify how you get the paths but often stg. like dfs is used
        # we try to find paths from source to the sink
        for i in range(n):
            level[i] = -1
        q = []
        q.append(s)
        level[s] = 0

        while len(q):
            node = q.pop(0)

            for edge in adj[node]:
                if level[edge[0]] == -1 and edge[2] > edge[1]:# not visited and free capacity
                    q.append(edge[0])
                    level[edge[0]] = level[node] + 1

        return level[t] > -1 #sink reached

    def sendFlow(current, flow, t):#, start
        # Sink reached
        if current == t:
            return flow

        idx = 0

        #while start[current] < len(adj[current]):# go over all edges
        while idx < len(adj[current]):# go over all edges

            #e = adj[current][start[current]]#connected edge
            e = adj[current][idx]#connected edge
            if level[e[0]] == level[current] + 1 and e[1] < e[2]:# free flow available and on next level

                # find minimum flow
                curr_flow = min(flow, e[2]-e[1])# update bottleneck with minimum free capacity
                temp_flow = sendFlow(e[0], curr_flow, t)# , start

                if temp_flow and temp_flow > 0:
                    e[1] += temp_flow

                    adj[e[0]][e[3]][1] -= temp_flow # subtract frlow of back-edge
                    return temp_flow
            #start[current] += 1
            idx += 1

    max_flow = 0

    while bfs(s, t):
        # start = [0 for _ in range(n + 1)]
        # the version I saw used this start array but I don't see the purpose of it
        # the fragments of it are still there in case someone is interested / if there is a reason for it, the code can be recovered
        # but I think the version with idx is more efficient

        while True:
            flow = sendFlow(s, float('inf'), t) # , start

            # see if there is still capacity in the current graph
            if not flow:
                break

            # Add path flow to overall flow
            max_flow += flow
    return max_flow

if __name__ == "__main__":
    # directed weighted graph

    n = 11
    s = n - 2# source
    t = n - 1# sink(somehow often named t)
    Graph = []# different falvour to create graph
    Graph.append((s, 1, 2))
    Graph.append((s, 2, 1))
    Graph.append((s, 0, 7))
    Graph.append((0, 3, 2))
    Graph.append((0, 4, 4))
    Graph.append((1, 4, 5))
    Graph.append((1, 5, 6))
    Graph.append((2, 3, 4))
    Graph.append((2, 7, 8))
    Graph.append((3, 6, 7))
    Graph.append((3, 7, 1))
    Graph.append((4, 5, 8))
    Graph.append((4, 8, 3))
    Graph.append((5, 8, 3))
    Graph.append((6, t, 1))
    Graph.append((7, t, 3))
    Graph.append((8, t, 4))

    '''
    n = 6
    s = n - 1
    t = n - 2
    Graph = []
    Graph.append((s, 0, 10))
    Graph.append((s, 1, 10))
    Graph.append((2, t, 10))
    Graph.append((3, t, 10))
    Graph.append((0, 1, 2))
    Graph.append((0, 2, 4))
    Graph.append((0, 3, 8))
    Graph.append((1, 3, 9))
    Graph.append((3, 2, 6))# sol = 19
    '''

    x = get_max_flow(Graph, s, t, n)
    print("max_flow: ", x)


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

#Import dataframe
df = pd.read_csv('../../data/fer2013.csv')

def clean_image_df():
    '''
    clean_image_df: takes in raw fer2013 dataframe. Creates an emotion
    labeled column that corresponds to numerical representations. Then splits
    up the df into three dataframes: Testing, Training, and Validation.

    Parameters
    ----------
    df: pandas dataframe

    Returns
    -------
    Training_df, Validation_df, Test_df, df: training data, validation data, testing data, and original data
    '''
    df = pd.read_csv('../../data/fer2013.csv')
    Emotions = ["Angry", "Disgust", "Fear", "Happy", "Sad", "Surprise", "Neutral"]

    lst = []
    for i in df.emotion:
        lst.append(Emotions[i])

    df['emotion_label'] = lst
    training_df = df[df['Usage'] == 'Training']
    validation_df = df[df['Usage'] == 'PublicTest']
    test_df = df[df.Usage == 'PrivateTest']

    return training_df, validation_df, test_df, df


import glob
import os
import shutil
import random
import sys

random.seed(14)
base = sys.argv[1]
files = glob.glob(sys.argv[1] + "*.svs") + glob.glob(sys.argv[1] + "*.tif")
random.shuffle(files)

split_ratio = .8
split = int(len(files) * split_ratio)

train_files = files[:split]
valid_files = files[split:]

train_folder = os.path.join(base,'train/')
valid_folder = os.path.join(base,'valid/')
test_folder = os.path.join(base,'test/')

if not os.path.exists(train_folder) : os.mkdir(train_folder)
if not os.path.exists(valid_folder) : os.mkdir(valid_folder)
if not os.path.exists(test_folder) : os.mkdir(test_folder)

train_folder = os.path.join(train_folder,'540x540_80x80/')
valid_folder = os.path.join(valid_folder,'540x540_80x80/')
#test_folder = os.path.join(test_folder,'Images/')

if not os.path.exists(train_folder) : os.mkdir(train_folder)
if not os.path.exists(valid_folder) : os.mkdir(valid_folder)
if not os.path.exists(os.path.join(test_folder,'Images/')) : os.mkdir(os.path.join(test_folder,'Images/'))
if not os.path.exists(os.path.join(test_folder,'Labels/')) : os.mkdir(os.path.join(test_folder,'Labels/'))
if not os.path.exists(os.path.join(test_folder,'Combined/')) : os.mkdir(os.path.join(test_folder,'Combined/'))

def move_numpy(filelist, destFolder):
    for t in filelist:
        i = 0
        combined = glob.glob(os.path.join(t,'Combined/*.npy'))
        for src in combined:
            dest = destFolder + "{}_{}.npy".format(t[-19:-13],i)
            new_path = shutil.copy(src, dest)
            print(new_path)
            i+=1

def move_image(filelist, destFolder):
    for t in filelist:
        i = 0
        combined = glob.glob(os.path.join(t,'Images/*.png'))
        for src in combined:
            dest = destFolder+ 'Images/' + "{}.png".format(t[-19:-13])
            new_path = shutil.copy(src, dest)
            print(new_path)
            i+=1
        combined = glob.glob(os.path.join(t, 'Labels/*.mat'))
        for src in combined:
            dest = destFolder + 'Labels/' + "{}.mat".format(t[-19:-13])
            new_path = shutil.copy(src, dest)
            print(new_path)
            i+=1
        combined = glob.glob(os.path.join(t, 'Combined/*.png'))
        for src in combined:
            dest = destFolder + 'Combined/' + "{}.png".format(t[-19:-13])
            new_path = shutil.copy(src, dest)
            print(new_path)
            i+=1

move_numpy(train_files, train_folder)
move_numpy(valid_files, valid_folder)
move_image(valid_files, test_folder)


# class BinaryTree:
# 	def __init__(self, rootVal):
# 		self.root = rootVal
# 		self.leftChild = None
# 		self.rightChild = None

def parsingTree(exp):

	res = BinaryTree(None)
	curr = res
	tree_stack = []

	for char in exp:

		if char == '(':
			curr.leftChild = BinaryTree(None)
			tree_stack.append(curr)
			curr = curr.leftChild

		if char in '0123456789':
			curr.root = char
			curr = tree_stack.pop()

		if char in '+-*/':
			curr.root = char
			tree_stack.append(curr)
			curr.rightChild = BinaryTree(None)
			curr = curr.rightChild

		if char == ')':
			curr = tree_stack.pop()

def evaluate(parse_tree):
    opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}
    leftC = parse_tree.leftChild
    rightC = parse_tree.rightChild

    if leftC and rightC:
    	fn = opers[parse_tree.rootVal]
    	return fn(evaluate(leftC), evaluate(rightC))
    else:
    	return parse_tree.rootVal