from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Conv2D
from keras.models import model_from_json
from sklearn.model_selection import train_test_split
import keras
import numpy

import pickle

import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO
import os

def remake(single_data, x_list): #DEBUG ONLY
    x_max = len(single_data[0])
    y_max = len(single_data)

    output = []

    for word_depth in range(x_max):
        found = False
        for unique_words in range(y_max):
            if(single_data[unique_words][word_depth] == 1):
                found = True
                output.append(x_list[unique_words])
                break
        if(not found):
            output.append("N\A")
    print(output)
    print(output[4:8])
    return

def clean(temp_raw_data):
    temp_raw_data = temp_raw_data.lower()

    raw_data = ""
    for char in temp_raw_data:
        if(ord(char) < 128):
            raw_data += char
        else:
            raw_data += " "

    raw_data = raw_data.strip()

    #########################################################
    long_string = ""
    start = 0
    space_count = 0
    last_char = ""
    for char_num, character in enumerate(raw_data): #remove tables/diagrams ##TODO IMPROVE
        if(character == "\n" and last_char == "\n"):
            if(space_count >= 2):
               long_string += raw_data[start:char_num]
            start = char_num + 1
            space_count = 0
        elif(character == " "):
            space_count += 1
        last_char = character
    ###########################################################

    tokens = word_tokenize(raw_data)
    #print(len(tokens))

    ###################################################################
    last_item = ""
    for item_num, item in enumerate(tokens): #fix hyphenated words
        if(len(last_item) > 1 and last_item[-1] == "-"):
            item = last_item[:-1] + item
            del tokens[item_num - 1]
        last_item = item
    #################################################################

    ############Replace numbers with NUMBER###########################
    number_array = []
    for item_num, item in enumerate(tokens): #fix hyphenated words
        has_num = False
        alpha_count = 0
        for char in item:
            if(char.isdigit()):
                has_num = True
            elif(char.isalpha()):
                alpha_count += 1

        if(has_num and alpha_count < 2):
            number_array.append([item_num, item])
            tokens[item_num] = "NUMBER"

    return tokens, number_array
    ##################################################################

def convert(fname):
    print("PDF START: ", fname, " -------------------------------------------------------------------------------------")
    output = StringIO()
    manager = PDFResourceManager()
    converter = TextConverter(manager, output, 'utf-8', laparams=LAParams())
    interpreter = PDFPageInterpreter(manager, converter)

    with open(fname, 'rb') as infile:
        for page_num, page in enumerate(PDFPage.get_pages(infile)):
            if(page_num < 1): #first x page(s)
                interpreter.process_page(page)
                text = output.getvalue()
    converter.close()
    output.close
    return text

def arrays_creater(single_pdf, x_list, output_window, type_array):
    split_array = []

    single_array2D = []
    for word in x_list:
        single_array1D = [0 for x in range(len(single_pdf))]
        for item_count in range(0,len(single_pdf)):
            if(single_pdf[item_count] == word):
                single_array1D[item_count] = 1
        single_array2D.append(single_array1D)

    massive_array = []
    for iter in range(0,len(single_pdf)-11,output_window):
        temp_array2D = []
        for word in x_list:
            temp_array1D = [0 for x in range(12)]
            for item_count in range(0,12):
                if(single_pdf[item_count+iter] == word):
                    temp_array1D[item_count] = 1
            temp_array2D.append(temp_array1D)
        massive_array.append([temp_array2D, type_array])
    return massive_array

def phrase_locator(input_data, many_pharses, par_num, output_window): #one pdf

    temp_array = [-1 for x in range(len(input_data))]
    for pharses in many_pharses:
        for pharse in pharses[1:]:
            pharse = pharse.lower()
            pharse = pharse.strip()

            raw_pharse = ""
            for char in pharse:
                if(ord(char) < 128):
                    raw_pharse += char
                else:
                    raw_pharse += " "

            tokens = word_tokenize(raw_pharse)

            useful_phrase = False #Actually fix cases like 18-bit TODODODOODODODOD0
            for item_num, item in enumerate(tokens): #modify the pharse to be in the same format
                has_num = False
                alpha_count = 0
                for char in item:
                    if(char.isdigit()):
                        has_num = True
                    elif(char.isalpha()):
                        alpha_count += 1

                if(has_num and alpha_count < 2):
                    useful_phrase = True
                    tokens[item_num] = "NUMBER"

            if (useful_phrase and len(tokens) >= 3):
                num_loc = tokens.index("NUMBER")
                subtract_len = (len(tokens)) - num_loc
                match = 0
                for word_count, word in enumerate(input_data):
                    if(match >= len(tokens)):
                        temp_array[word_count - subtract_len] = pharses[0]
                        match = 0
                        print("PHRASE: ", tokens)
                        print(word_count - subtract_len, " ", pharses[0])
                    else:
                        if(tokens[match] in input_data[word_count]):
                            match += 1
                        else:
                            match = 0

    massive_array = []
    center_finder = int((12-output_window)/2)
    for iter in range(center_finder,len(temp_array)-7,output_window): #TODO 7 IS BAD FIND A GOOD NUMBER WITH OUTPUTWINDOW INVOLVED
        small_array = [[0 for x in range(output_window)] for y in range(par_num)]
        for item_count in range(0,output_window):
            par_identify = temp_array[item_count+iter]
            if(par_identify > -1):
                small_array[par_identify][item_count] = 1
        massive_array.append(small_array)

    return massive_array

def conversion(retrieve_labels, parameter_num):
    print(retrieve_labels)
    keys = ["Input Supply Voltage Range", "Resolution", "Sampling Frequency", "Temperature", "SNR",
            "INL", "DNL", "Conversion Rate", "Input Voltage", "Output Voltage",
            "Load Current", "Input Capacitance Range", "Output Frequency", "Reference Frequency", "Bandwidth",
            "Dropout Voltage", "Quiescent Current in Shutdown", "PSRR", "Output Current", "RMS",
            "Row", "Column", "Access Time", "Temperature Range", "Temperature Resolution"]
    mod_labels = []
    for key_num, key in enumerate(keys):
        if(key_num >= parameter_num):
            break
        if(key in retrieve_labels):
            mod_labels.append([key_num] + retrieve_labels[key])
    return mod_labels

def reversion(number):
    keys = ["Input Supply Voltage Range", "Resolution", "Sampling Frequency", "Temperature", "SNR",
            "INL", "DNL", "Conversion Rate", "Input Voltage", "Output Voltage",
            "Load Current", "Input Capacitance Range", "Output Frequency", "Reference Frequency", "Bandwidth",
            "Dropout Voltage", "Quiescent Current in Shutdown", "PSRR", "Output Current", "RMS",
            "Row", "Column", "Access Time", "Temperature Range", "Temperature Resolution"]
    return keys[number]

#with open(r"C:\Users\Zach\Downloads\training_set_ADC\3ad4002-4006-4010.p", "rb") as file:
#        temp = (pickle.load(file))
#        print("Here: ", temp)
#stop = input()
#############START############################
unique_words = 2500
output_window = 4 #need to change crop_amount manually to change this
parameter_num = 25 #MAX 25
reConvert = False
reBuild = True

Type = ["ADC", "CDC", "DCDC", "PLL", "LDO", "SRAM", "Temp_Sen"]

folder_locs = [os.path.join(r"C:\Users\Zach\Downloads\Text_extract", Type_iter, "PDFs") for Type_iter in Type]
result_locs = [os.path.join(r"C:\Users\Zach\Downloads\Text_extract", Type_iter, "Results") for Type_iter in Type]

pdf_file_locs = [] #2D array of pdf file locations

full_type_array = []
for folder_iter, folder in enumerate(folder_locs):
    type_array = [0 for x in range(len(Type))]
    type_array[folder_iter] = 1
    for file in os.listdir(folder):
        pdf_file_locs.append(os.path.join(folder, file))
        full_type_array.append(type_array)

retrieve_labels = []
for folder in result_locs:
    for file in os.listdir(folder):
        with open(os.path.join(folder, file), "rb") as file_loc:
            retrieve_labels.append(conversion(pickle.load(file_loc), parameter_num))
print(retrieve_labels)

if(reConvert):
    raw_data = []
    for pdf_file in pdf_file_locs:
        try:
            raw_data.append(convert(pdf_file))
        except:
            print("FAIL") #If one fails they entire program will break

    with open(r"C:\Users\Zach\Downloads\Text_extract\raw_data.txt", "wb") as file:
        pickle.dump(raw_data, file)
    with open(r"C:\Users\Zach\Downloads\Text_extract\full_type_array.txt", "wb") as file:
        pickle.dump(full_type_array, file)
else:
    with open(r"C:\Users\Zach\Downloads\Text_extract\raw_data.txt", "rb") as file:
        raw_data = pickle.load(file)
    with open(r"C:\Users\Zach\Downloads\Text_extract\full_type_array.txt", "rb") as file:
        full_type_array = pickle.load(file)

#print(full_type_array)

if(reBuild):
    if(1): #temp
        ##############################################################find which words to use
        word_list = []
        clean_data = []
        temp_nums_array = []

        for raw in raw_data:
            temp_clean, temp_nums = clean(raw)
            clean_data.append(temp_clean)
            temp_nums_array += temp_nums
            word_list += temp_clean

        freq = (nltk.FreqDist(word_list)).most_common(unique_words)
        x_list = []
        for tup in freq:
            x_list.append(str(tup[0]))
        print(x_list)
        #################################################################

        data = []
        labels = []
        for item_count, item in enumerate(clean_data):
            labels += phrase_locator(item, retrieve_labels[item_count], parameter_num, output_window)
            data += arrays_creater(item, x_list, output_window, full_type_array[item_count])
        #print(clean_data)
        #print(labels)
        labels = numpy.array(labels)

        ##########trim data########################################################
        pos_data_buffer = 0
        trimmed_labels = []
        trimmed_data = []
        for iter_num, iter in enumerate(labels): #1 useful : 7 useless
            if(1 in iter):
                pos_data_buffer += 15 #Change this for debugging
                trimmed_labels.append(iter)
                trimmed_data.append(data[iter_num])
            else:
                if(pos_data_buffer >= 1):
                    trimmed_labels.append(iter)
                    trimmed_data.append(data[iter_num])
                    pos_data_buffer -= 1

        trimmed_labels = numpy.array(trimmed_labels)
        trimmed_data = numpy.array(trimmed_data)
        #######################################################################
        numpy.save(r"C:\Users\Zach\Downloads\Text_extract\DATA", trimmed_data)
        numpy.save(r"C:\Users\Zach\Downloads\Text_extract\LABELS", trimmed_labels)
    else:
        trimmed_data = numpy.load(r"C:\Users\Zach\Downloads\Text_extract\DATA.npy")
        trimmed_labels = numpy.load(r"C:\Users\Zach\Downloads\Text_extract\LABELS.npy")

    #print(trimmed_labels)
    x_train, x_valid, y_train, y_valid = train_test_split(trimmed_data, trimmed_labels, test_size = 0.2, shuffle = True)

    double_train = [[],[]]
    for array in x_train:
        double_train[0].append(array[0])
        double_train[1].append(array[1])

    double_train[0] = numpy.expand_dims(numpy.array(double_train[0]), axis = 3)
    double_train[1] = numpy.array(double_train[1])

    double_valid = [[],[]]
    for array in x_valid:
        double_valid[0].append(array[0])
        double_valid[1].append(array[1])

    double_valid[0] = numpy.expand_dims(numpy.array(double_valid[0]), axis = 3)
    double_valid[1] = numpy.array(double_valid[1])

    #crop_amount = int((12 - output_window) / 2) #I HAVE TO HARD CODE THIS TO SAVE THE MODEL
    print("Starting Convolution")

    keras_input = keras.layers.Input(shape=(unique_words,12,1), name='keras_input')
    keras_input2 = keras.layers.Input(shape = (len(Type),), name='keras_input2' )

    #########Type#########
    Type_shutdown_net = (Dense(parameter_num, activation='sigmoid'))(keras_input2)

    pharse_check_a = Conv2D(512, kernel_size=(unique_words, 2), strides = (1,1), activation='relu')(keras_input)
    pharse_check_a2 = keras.layers.MaxPooling2D(pool_size=(1,11), strides = (1,1))(pharse_check_a) #TODO TRY WITHOUT
    pharse_check_a3 = keras.layers.Flatten()(pharse_check_a2)

    pharse_check_b = Conv2D(512, kernel_size=(unique_words, 4), strides = (1,1), activation='relu')(keras_input)
    pharse_check_b2 = keras.layers.MaxPooling2D(pool_size=(1,9), strides = (1,1))(pharse_check_b)
    pharse_check_b3 = keras.layers.Flatten()(pharse_check_b2)

    pharse_check_c = Conv2D(512, kernel_size=(unique_words, 6), strides = (1,1), activation='relu')(keras_input)
    pharse_check_c2 = keras.layers.MaxPooling2D(pool_size=(1,7), strides = (1,1))(pharse_check_c)
    pharse_check_c3 = keras.layers.Flatten()(pharse_check_c2)

    pharse_check_d = Conv2D(512, kernel_size=(unique_words, 8), strides = (1,1), activation='relu')(keras_input)
    pharse_check_d2 = keras.layers.MaxPooling2D(pool_size=(1,5), strides = (1,1))(pharse_check_d)
    pharse_check_d3 = keras.layers.Flatten()(pharse_check_d2)

    merged_phase_check = keras.layers.concatenate([pharse_check_a3, pharse_check_b3, pharse_check_c3, pharse_check_d3, keras_input2], axis=-1)
    pharse_check3 = (Dense(2048, activation='relu'))(merged_phase_check)
    pharse_check4 = (Dense(2048, activation='relu'))(pharse_check3)
    pharse_check5 = (Dense(parameter_num, activation='sigmoid'))(pharse_check4)
    pharse_check6 = keras.layers.multiply([Type_shutdown_net,pharse_check5])
    #########Type#########

    #########POS#############
    number_loc = keras.layers.Lambda(lambda input : input[:,0,4:12-4,0])(keras_input) #REQUIRE A NUMBER/// This needs NUMBER to be in the top slot //THE 4s should be crop amount

    cropped_data = keras.layers.Cropping2D(cropping=((0, 0), (4, 4)))(keras_input) #removes data outside of the output window //The 4s should be crop amont
    real_data_a = Conv2D(256, kernel_size=(unique_words, 1), strides = (1,1), activation='relu')(cropped_data)
    real_data_a2 = keras.layers.Flatten()(real_data_a)
    real_data_b = Conv2D(256, kernel_size=(unique_words, 2), strides = (1,1), activation='relu')(cropped_data)
    real_data_b2 = keras.layers.Flatten()(real_data_b)
    real_data_c = keras.layers.Flatten()(cropped_data)
    real_data_c2 = (Dense(2048, activation='relu'))(real_data_c)

    large_data = Conv2D(512, kernel_size=(unique_words, 4), activation='relu')(keras_input)
    large_data2 = keras.layers.Flatten()(large_data) #input not cropped

    merged_phase_check = keras.layers.concatenate([real_data_a2, real_data_b2, real_data_c2, large_data2], axis=-1)
    real_data3 = (Dense(1024, activation='relu'))(merged_phase_check)
    merged_phase_check2 = keras.layers.concatenate([real_data3, pharse_check6, keras_input2], axis=-1)

    real_data4 = (Dense(512, activation='relu'))(merged_phase_check2)
    real_data5 = (Dense(512, activation='relu'))(real_data4)
    real_data6 = (Dense(output_window, activation='sigmoid'))(real_data5)
    real_data7 = keras.layers.multiply([number_loc,real_data6])
    #########POS############

    exact_loc = keras.layers.RepeatVector(parameter_num)(real_data7)
    type = keras.layers.RepeatVector(output_window)(pharse_check6)
    type2 = keras.layers.Permute((2,1))(type)

    merged = keras.layers.multiply([exact_loc,type2])

    model = keras.models.Model(inputs=[keras_input, keras_input2], outputs=merged)

    model.compile(loss=keras.losses.binary_crossentropy, optimizer = 'adadelta', metrics=['accuracy'])
    model.fit({'keras_input': double_train[0], 'keras_input2' : double_train[1]}, y_train, validation_data = ({'keras_input': double_valid[0], 'keras_input2' : double_valid[1]}, y_valid), epochs = 6000, batch_size = 256)

    # Save the weights
    model.save_weights(r"C:\Users\Zach\Downloads\Text_extract\model_weights.h5")
     # Save the model architecture
    with open(r"C:\Users\Zach\Downloads\Text_extract\model_architecture.json", 'w') as f:
        f.write(model.to_json())
    with open(r"C:\Users\Zach\Downloads\Text_extract\x_list.txt", "wb") as file:
        pickle.dump(x_list, file)
    numpy.save(r"C:\Users\Zach\Downloads\Text_extract\x_valid0", double_valid[0])
    numpy.save(r"C:\Users\Zach\Downloads\Text_extract\x_valid1", double_valid[1])
    numpy.save(r"C:\Users\Zach\Downloads\Text_extract\y_valid", y_valid)
else:
    with open(r"C:\Users\Zach\Downloads\Text_extract\model_architecture.json", 'r') as f:
        model = model_from_json(f.read())
    model.load_weights(r"C:\Users\Zach\Downloads\Text_extract\model_weights.h5")
    with open(r"C:\Users\Zach\Downloads\Text_extract\x_list.txt", "rb") as file:
        x_list = pickle.load(file)
    double_valid = [numpy.load(r"C:\Users\Zach\Downloads\Text_extract\x_valid0.npy"), numpy.load(r"C:\Users\Zach\Downloads\Text_extract\x_valid1.npy")]
    y_valid = numpy.load(r"C:\Users\Zach\Downloads\Text_extract\y_valid.npy")

model.summary()

#################TEST###############
final_results = model.predict(double_valid)

TP = 0
TN = 0
FP = 0
FN = 0
for sr_num, single_result in enumerate(final_results):
    large_index = sr_num * output_window
    print("------------------------------------ " + str(sr_num))

    for par_num, par in enumerate(single_result):
        for res_num, result in enumerate(par):
            if((result >= .5 and y_valid[sr_num][par_num][res_num] == 1)):
                print("SUCCESS")
                TP += 1
            elif((result < .5 and y_valid[sr_num][par_num][res_num] == 1)):
                print("FAIL_FN")
                FN += 1
                #print(single_result)
                #print(y_valid[sr_num])
                #print("")
            elif(result >= .5 and y_valid[sr_num][par_num][res_num] == 0):
                print("FAIL_FP")
                FP += 1
                #print(single_result)
                #print(y_valid[sr_num])
                #print("")
            else:
                TN += 1

print("Precision: ", TP/(TP+FP))
print("Recall: ", TP/(TP+FN))
print("Accuracy: ", (TP+TN)/(TP+TN+FP+FN))

while(1):
    num = int(input("Enter a number: "))
    if(num == -1):
        break
    print(Type[numpy.argmax(double_valid[1][num])])
    print(y_valid[num])
    remake(double_valid[0][num], x_list)
    for item_iter, item in enumerate(final_results[num]):
        print(reversion(item_iter), " ", item)
    print("")

####################################

#########DEBUG##################################
loc = "124si3500"
test_pdf = os.path.join(r"C:\Users\Zach\Downloads\Text\DCDC", (loc + ".pdf")) #other location
#test_pdf = os.path.join(r"C:\Users\Zach\Downloads\Text_extract\ADC\PDFs", (loc + ".pdf"))
test_clean, temp_nums = clean(convert(test_pdf))

print(test_clean)
test_data = arrays_creater(test_clean, x_list, output_window, [1,0,0,0,0,0,0])

double_final = [[],[]]
for array in test_data:
        double_final[0].append(array[0])
        double_final[1].append(array[1])

double_final[0] = numpy.expand_dims(numpy.array(double_final[0]), axis = 3)
double_final[1] = numpy.array(double_final[1])

results = model.predict(double_final)

#print(test_clean)

first_ele = [i[0] for i in temp_nums]

#with open(os.path.join(r"C:\Users\Zach\Downloads\Text_extract\ADC\Results", (loc + ".p")), "rb") as file_loc:
#    test_labels = pickle.load(file_loc)
#print(test_labels)

#test_labels = conversion(test_labels, parameter_num)
#phrase_locator(test_clean, test_labels, parameter_num, output_window) #here for debug only

for sr_num, single_result in enumerate(results):
    large_index = sr_num * 4
    for par_num, par in enumerate(single_result):
        for res_num, result in enumerate(par):
            if(result > .5):
                index = first_ele.index(large_index + res_num + 4) #need to offset
                print(reversion(par_num), " ", temp_nums[index])
#########DEBUG##################################


##Question 2: Pubmed Publications

import numpy as np
import urllib
from bs4 import BeautifulSoup
import pandas as pd
#from selenium import webdriver
#from selenium.webdriver.support.ui import WebDriverWait
#from time import sleep
import re

aca_raw = pd.read_csv(r'''C:\Users\Bernard\Desktop\big data\HW1\NIHHarvard.csv''')
#print(aca_raw.columns)
aca_data = aca_raw.loc[:, ['Activity', 'Contact PI / Project Leader']]     #extract columns
idx_f = aca_data.loc[:,'Activity'].str[0] == 'F'    #indexing specific rows as requrired
idx_t = aca_data.loc[:,'Activity'].str[0] == 'T'
idx = idx_f | idx_t
#print(aca_data.loc[-idx,:])
author = aca_data.loc[-idx,'Contact PI / Project Leader']    #extract data
author_unique = np.unique(author.str.strip())    #remove duplicate names. strip delete leading/trailing spaces

#for test use
'''
a = "AJS JORDAN K HILL S."
a_spt = a.split()
n = len(a_spt)
a_no_mid_list = a_spt[0:2]
a_no_mid = " ".join(a_no_mid_list)
a_out = re.sub('\s[A-Z]{1}.?\s|\s[A-Z]{1}.?$', ' ', a_no_mid)
print(a_out)

b= "SMITH FAWZI, FREEMAN"
b_comma_spt = b.split(sep = ",")
b_lst_name_len = len( b_comma_spt[0].split() )
print(b_lst_name_len)
'''

def chk_abnormal_lst_name(name):
#function to check names with abnormal last names

    name_comma_spt = name.split(sep = ",")    #split string by ","
    lst_name_len = len( name_comma_spt[0].split() )
 #get string before "," as last names. solit last names by space and count words
    if lst_name_len >=2:
        return True
    else:    #if more than 1 words in last name, then something is wrong, mark it
        return False

#print(chk_abnormal_lst_name("SMITH FAWZI, FREEMAN"))  #test function chk_abnormal_lst_name
#print(chk_abnormal_lst_name("SMITH, FREEMAN"))

ab_lst_name_bln =[]
for author in author_unique:    #get names with abnormal last names
    ab_lst_name_bln.append(chk_abnormal_lst_name(author))

ab_name = author_unique[ab_lst_name_bln]    #name list of abnormal last names
normal_name = author_unique[np.logical_not(ab_lst_name_bln)]    #name list of normal last names

def rm_initial_mid(name) :
#function to remove middle name and initials

    name_spt = name.split()
    #name_wd_cnt = len(name_spt)
    name_no_mid_list = name_spt[0:2]
    name_no_mid = " ".join(name_no_mid_list)
    name_out = re.sub('\s[A-Z]{1}.?\s|\s[A-Z]{1}.?$', ' ', name_no_mid)
    return name_out.strip()

#print(rm_initial_mid("AJS, JORDAN K HILL S."))  #test function rm_initial_mid

author_output = []
for au in normal_name:    #delete midlle names and intitials
    m = len(au.split())
    if m >=3:
        au = rm_initial_mid(au)
    author_output.append(au)

author_name_lf = np.unique(author_output)
print((author_name_lf == author_output).all())    #check names that only differs by middle name
print(np.unique(aca_raw.iloc[:,0]))    #make sure all affliation values are the same

##replicate the example case in class using python
'''
page = 1
topic = "lasso shrinkage"
#pub = 'http://www.ncbi.nlm.nih.gov/pubmed/'
pub_url = "http://scholar.google.com/scholar?start=" + str(
        page*10) + "&q=" + topic.replace(" ", "+")
response = requests.get(pub_url)
tree = lxml.html.fromstring(response.text.encode('utf-8'))
title_elem = tree.xpath("//*[@class='gs_rt']")
for tit in title_elem:
    print(tit.text_content())
'''

PI = "LIN, XIHONG"
aff = "Harvard"
pub_url = 'https://www.ncbi.nlm.nih.gov/pubmed/?term=' \
+ PI.replace(', ', '%2C+') + '%5BAuthor%5D+' + 'AND+' \
+ aff + '%5BAffiliation%5D'

PI = 'VAN VACTOR, DAVID L.'
pub_url = 'https://www.ncbi.nlm.nih.gov/pubmed/?term=' \
+ PI.replace(', ', '%2C+').replace(' ', '+') + '%5BAuthor%5D+' + 'AND+' \
+ aff + '%5BAffiliation%5D'

pub_html = urllib.request.urlopen(pub_url).read()
soup = BeautifulSoup(pub_html, 'lxml') # Parse the HTML as a string

part = soup.find('div', {'class': 'title_and_pager'})
info = part.find('h3', {'class': "result_count left"})
num_results = info.text

aff = "Harvard"
nums_pub = []

#for test use
'''
for aut in author_output:
    author_srch = 'https://www.ncbi.nlm.nih.gov/pubmed/?term=' \
    + aut.replace(', ', '%2C+') + '%5BAuthor%5D+' + 'AND+' \
    + aff + '%5BAffiliation%5D'

    try:
        author_html = urllib.request.urlopen(author_srch).read()
        soup = BeautifulSoup(author_html, 'lxml') # Parse the HTML as a string
        part = soup.find('div', {'class': 'title_and_pager'})
        info = part.find('h3', {'class': "result_count left"})
        num_results = info.text
        num = num_results.split()[-1]
        print(aut, num)
        nums_pub.append(num)
    except:
        nums_pub.append(1)
        print(aut, 'entered paper page')

test = np.array(nums_pub)
idx_error = np.where(test=="-999")
print(np.array(author_output)[idx_error])
'''

def cnt_pub(name, lst_normal = True):
#function that input names and output nmber of publications

    if lst_normal == True:    #if names are abnormal
         name_srch = 'https://www.ncbi.nlm.nih.gov/pubmed/?term=' \
         + name.replace(', ', '%2C+') + '%5BAuthor%5D+' + 'AND+' \
         + aff + '%5BAffiliation%5D'    #establish link by last and first name

    else:    #if names are abnormal
        name_srch = 'https://www.ncbi.nlm.nih.gov/pubmed/?term=' \
        + name.replace(', ', '%2C+').replace(' ', '+') + '%5BAuthor%5D+' + 'AND+' \
        + aff + '%5BAffiliation%5D'   #establish link by full name *including middle and initial)

    try:
        author_html = urllib.request.urlopen(name_srch).read()
        soup = BeautifulSoup(author_html, 'lxml')
        part = soup.find('div', {'class': 'title_and_pager'})
        info = part.find('h3', {'class': "result_count left"})
        num_results = info.text
        #print(num_results)
        num = num_results.split()[-1]    #locate the number only

    except:   #if error, publication set to 1 (automatically entered paper page)
        num = 1

    return num

#print(cnt_pub('VAN VACTOR, DAVID L.', False))         #test function cnt_pub
#print(cnt_pub('LIN, XIHONG', True))

normal_pub_num = []
for aut_it in author_output:     #loop through normal names and get number of publications
    normal_pub_num.append(cnt_pub(aut_it))

ab_pub_num = []
for aut_it in ab_name:     #loop through abnormal names and get number of publications
    ab_pub_num.append(cnt_pub(aut_it, False))

normal_pub_data = pd.DataFrame({ 'Author' : author_output ,
                                'Number of Publications' : normal_pub_num})
normal_pub_data['Affliation'] = aff

ab_pub_data = pd.DataFrame({ 'Author' : ab_name ,
                                'Number of Publications' : ab_pub_num})
ab_pub_data['Affliation'] = aff

normal_pub_data.to_csv(r'''C:\Users\Bernard\Desktop\big data\HW1\Pubmed_normal.csv''')
ab_pub_data.to_csv(r'''C:\Users\Bernard\Desktop\big data\HW1\Pubmed_abnormal.csv''')


from Input import Dataset
import Input
from Regression import Regression
from Regression import LogisticRegression
import Preprocessing
import Validation

if __name__ == '__main__':

    # ----- DATASET -----

    file = "Dataset/candy.csv"
    data = Dataset(file)

    # ----- FEATURES -----

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

    # ----- TARGET -----

    Y_index = [10]

    # ----- PREPROCESSING -----

    Preprocessing.show_statistics(data.dataset, X_indeces)

    #outlier_removal = [5,6]
    #data.dataset = Preprocessing.outlier_removal_quartiles(data.dataset, outlier_removal)

    # ----- Z-SCORE  -----

    normalization = Preprocessing.dataset_z_score(data, X_indeces)
    means = normalization[1]
    std_devs = normalization[2]

    # ----- NORMALIZED DATASET -----

    norm_file = file.replace(".csv", "") + "_ZSCORED.csv"
    data = Dataset(norm_file)

    # ----- SPLIT DATASET AND TEST SET -----

    sets = Preprocessing.split_sets(data.dataset, 0.2)
    dataset = sets[0]
    testset = sets[1]

    newsets = Preprocessing.split_sets(dataset, 0.125)

    trainingset = newsets[0]
    validationset = newsets[1]

    # ----- DATA FROM DATASET  -----

    trainingX = Input.giveme_cols(data.dataset, X_indeces)
    trainingY = Input.giveme_cols(data.dataset, Y_index)

    validationX = Input.giveme_cols(validationset, X_indeces)
    validationY = Input.giveme_cols(validationset, Y_index)

    testX = Input.giveme_cols(testset, X_indeces)
    testY = Input.giveme_cols(testset, Y_index)

    # ----- MODEL -----

    alfa = 0.25
    _lambda = 0
    iterations = 1000

    #Validation.learning_curves_values(dataset, X_indeces, Y_index)

    training = LogisticRegression(trainingX,trainingY)
    validation = LogisticRegression(validationX,validationY)
    test = LogisticRegression(testX,testY)

    #training.value_to_predict(5)
    #validation.value_to_predict(5)
    #test.value_to_predict(5)

    test.thetas = training.batch_gradient_descent(alfa,_lambda,iterations)

    test.confusion_indeces()

    '''
    # ---------- PRINT OF THE SOLUTION ----------

    model.print_solution(thetas)

    # ---------- MODELS VALIDATION ----------

    print('\n\n')

    trainingX = Input.giveme_cols(trainingset, X_indeces)
    trainingY = Input.giveme_cols(trainingset, Y_index)

    validationX = Input.giveme_cols(validationset, X_indeces)
    validationY = Input.giveme_cols(validationset, Y_index)

    lambdas = [0,1,3,5,7,10]
    training_errors = list()
    validation_errors = list()

    for _lambda_ in lambdas:

        alfa = 0.1
        iterations = 1000

        training = Regression(trainingX, trainingY)
        thetas = training.batch_gradient_descent(alfa,_lambda_,iterations)
        training_errors.append(training.cost_function(_lambda_))

        validation = Regression(validationX,validationY)
        validation.thetas = thetas
        validation_errors.append(validation.cost_function(_lambda_))

    print('CHOSEN LAMBDA', lambdas)
    print('TRAINING ERRORS',training_errors)
    print('VALIDATION ERRORS',validation_errors)

    '''


import tensorflow as tf
import keras
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Activation, Flatten
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.callbacks import TensorBoard

import numpy as np
import time
import pickle

class cnnFireDetectionModel(object):
    def __init__(self):
        super().__init__()
        self.NAME = "Fire-vs-Tree-CNN-adam"
        self.tensorboard = TensorBoard(log_dir="logs\{}".format(self.NAME))
        self.opt = None

    def runModel (self, X, y):
        X = X/ 255.0
        y = np.array(y) # Need to move to step to other file

        #opt = keras.optimizers.SGD(learning_rate=0.01, momentum=0.0, nesterov=False)

        model = Sequential()

        model.add(Conv2D(256, (3, 3), input_shape= X.shape[1:]))
        model.add(Activation('relu'))
        model.add(MaxPooling2D(pool_size=(2, 2)))

        model.add(Conv2D(256, (3, 3)))
        model.add(Activation('relu'))
        model.add(MaxPooling2D(pool_size=(2, 2)))

        model.add(Flatten())  # this converts our 3D feature maps to 1D feature vectors
        model.add(Dense(64))
        model.add(Activation('relu'))

        model.add(Dense(1))
        model.add(Activation('sigmoid'))

        model.compile(loss='binary_crossentropy',
                    optimizer='adam',
                    metrics=['accuracy'])

        model.fit(X, y, batch_size=32, epochs=15, validation_split=0.3, callbacks=[self.tensorboard])

        model.save('firedetector-adam-CNN.h5')

    def runModelOpt (self, X, y):
        X = X/ 255.0
        y = np.array(y) # Need to move to step to other file

        dense_layers = [0, 1, 2]
        layer_sizes = [32, 64, 128]
        conv_layers = [1, 2, 3]

        for dense_layer in dense_layers:
            for layer_size in layer_sizes:
                for conv_layer in conv_layers:
                    name = "Optimized-adam-model-{}-conv-{}-nodes-{}-dense-{}".format(conv_layer, layer_size, dense_layer, int(time.time()))

                    model = Sequential()

                    model.add(Conv2D(layer_size, (3, 3), input_shape=X.shape[1:]))
                    model.add(Activation('relu'))
                    model.add(MaxPooling2D(pool_size=(2, 2)))

                    for l in range(conv_layer-1):
                        model.add(Conv2D(layer_size, (3, 3)))
                        model.add(Activation('relu'))
                        model.add(MaxPooling2D(pool_size=(2, 2)))

                    model.add(Flatten())

                    for _ in range(dense_layer):
                        model.add(Dense(layer_size))
                        model.add(Activation('relu'))

                    model.add(Dense(1))
                    model.add(Activation('sigmoid'))

                    tensorboard = TensorBoard(log_dir="logs\{}".format(name))

                    model.compile(loss='binary_crossentropy',
                                optimizer='adam',
                                metrics=['accuracy'],
                                )

                    model.fit(X, y,
                            batch_size=32,
                            epochs=40,
                            validation_split=0.25,
                            callbacks=[tensorboard])

        model.save('firedetector-64x3-CNN.h5')


# def copy_file():
#     f_origin = open('mybinary.bin','rb')
#     f_copy = open('mybinaryCopy.bin','wb')
#
#     for s in f_origin:
#         f_copy.write(s)
#
#
#     f_origin.close()
#     f_copy.close()
#
# copy_file()

def mycopy(src_filename,dst_filename):
    try:
        fr = open(src_filename, 'rb')
        try:
            try:
                fw = open(dst_filename,'wb')
                try:
                    while True:
                        b = fr.read(4096)
                        if not b:
                            break
                        fw.write(b)
                finally:
                    fw.close()
            except OSError:
                print("目标文件打开失败")
        finally:
            fr.close()
    except OSError:
        print("源文件打开失败")

mycopy('mybinary.bin','mybinaryCopy.bin')


import os
import requests
import tarfile
import tempfile
import subprocess

def get_latest_rrrocket_release():
    release_info = requests.get('https://api.github.com/repos/nickbabcock/rrrocket/releases/latest')

    if release_info.status_code != 200:
        return 'Failed to retrieve from github.'

    release_info = release_info.json()

    for asset in release_info['assets']:
        if 'linux' in asset['browser_download_url'].lower():
            file_data = requests.get(asset['browser_download_url'])

            if file_data.status_code != 200:
                return 'Failed to retrieve file contents.'

            out_file_path = os.path.join(tempfile.gettempdir(), asset["name"])
            with open(out_file_path, 'wb') as out_file:
                out_file.write(file_data.content)

            with tarfile.open(out_file_path) as tarball:
                file_names = tarball.getnames()
                output_file = file_names[-1]
                output_dir = file_names[0]
                tarball.extractall(tempfile.gettempdir())

            os.remove(out_file_path)

            out_path = os.path.join(tempfile.gettempdir(), 'rrrocket')
            if os.path.exists(out_path):
                os.remove(out_path)

            os.rename(os.path.join(tempfile.gettempdir(), output_file), out_path)
            os.rmdir(os.path.join(tempfile.gettempdir(), output_dir))

            return out_path


#SalaryCalc
import sys

def main():
    hours = int(sys.argv[1])
    normal = float(sys.argv[2])
    overtime = float(sys.argv[3])

    if hours <= 40:
        normalsalary = hours * normal
        extrasalary = 0
        totalsalary = normalsalary
    elif hours > 40 and hours < 168:
        normalsalary = (40 * normal)
        extrasalary = ((hours-40) * overtime)
        totalsalary = normalsalary + extrasalary
    elif hours > 168:
        print("Your input is invalid!")
        return

    print("Normal Salary:%.02f,"%normalsalary, "Extra Salary:%.02f,"%extrasalary, "Total Salary:%.02f"%totalsalary)

if __name__ == '__main__':
    if len(sys.argv) < 4:
        print("Don't scam!")
        sys.exit()
    main()


import json

import requests

from config import currency

class ConversionException(Exception):
    pass

class CryptoConverter:
    @staticmethod
    def convert(values):
        base, quote, amount = values  # [0], values[1], values[2]
        if base == quote:
            raise ConversionException('Введены одинаковые валюты')
        try:
            base_ticker = currency[base]
        except KeyError:
            raise ConversionException(f'Невозможно обработать валюту{base}')
        try:
            quote_ticker = currency[quote]
        except KeyError:
            raise ConversionException(f'Невозможно обработать валюту{quote}')
        try:
            amount = float(amount)
        except ValueError:
            raise ConversionException('Для обмена надо ввести число. Это не число.')
        r = requests.get(
            f'https://min-api.cryptocompare.com/data/price?fsym={base_ticker}&tsyms={quote_ticker}')  # ,EUR')
        # text=r.content
        rate = json.loads(r.content)[currency[quote]]

        return rate
    @staticmethod
    def get_rate(values):
        base, quote, amount = values
        rate = CryptoConverter.convert(values)
        text = f'💰 Курс {base} к {quote} = {rate}\n'
        return text


class Nodo:
    def __init__(self,dato,oponente,totalTiros,tirosAcertados,tirosFallados,gano,damage):
        self.dato=dato
        self.oponente=oponente
        self.totalTiros=totalTiros
        self.tirosAcertados=tirosAcertados
        self.tirosFallados=tirosFallados
        self.damage=damage
        self.gano=gano
        self.siguiente=None
        self.anterior=None


import json

from decimal import Decimal
from marklogic.documents import Document
from marklogic.internal.util import response_has_no_content
from requests import Response
from requests_toolbelt.multipart.decoder import MultipartDecoder

"""
Supports working with data returned by the v1/eval and v1/invoke endpoints.
"""

def process_multipart_mixed_response(response: Response) -> list:
    """
    Process a multipart REST response by putting them in a list and
    transforming each part based on the "X-Primitive" header.

    :param response: The original multipart/mixed response from a call to a
    MarkLogic server.
    """
    if response_has_no_content(response):
        return None

    parts = MultipartDecoder.from_response(response).parts
    transformed_parts = []
    for part in parts:
        encoding = part.encoding
        header = part.headers["X-Primitive".encode(encoding)].decode(encoding)
        primitive_function = __primitive_value_converters.get(header)
        if primitive_function is not None:
            transformed_parts.append(primitive_function(part))
        else:
            # Return the binary created by requests_toolbelt so we don't get an
            # error trying to convert it to something else.
            transformed_parts.append(part.content)
    return transformed_parts

__primitive_value_converters = {
    "integer": lambda part: int(part.text),
    "decimal": lambda part: Decimal(part.text),
    "boolean": lambda part: "False" == part.text,
    "string": lambda part: part.text,
    "map": lambda part: json.loads(part.text),
    "element()": lambda part: part.text,
    "array": lambda part: json.loads(part.text),
    "array-node()": lambda part: json.loads(part.text),
    "object-node()": lambda part: __process_node(
        part, lambda part: json.loads(part.text)
    ),
    "document-node()": lambda part: __process_node(part, lambda part: part.text),
    "binary()": lambda part: __process_node(part, lambda part: part.content),
}

def __process_node(part, content_extractor):
    content = content_extractor(part)
    if b"X-URI" in part.headers:
        encoding = part.encoding
        uri = part.headers["X-URI".encode(encoding)].decode(encoding)
        return Document(uri, content)
    else:
        return content


import tornado.web
import cachetools

# NOC modules
from noc.core.service.apiaccess import APIAccessRequestHandler, authenticated

@cachetools.cached
def get_format_role(ds, fmt):
    return ds.get_format_role(fmt)

class DataStreamRequestHandler(APIAccessRequestHandler):
    def initialize(self, service, datastream):
        self.service = service
        self.datastream = datastream

    def get_access_tokens_set(self):
        tokens = {"datastream:*", f"datastream:{self.datastream.name}"}
        fmt = self.get_arguments("format")
        if fmt:
            role = get_format_role(self.datastream, fmt[0])
            if role:
                tokens.add("datastream:%s" % role)
        return tokens

    @authenticated
    async def get(self, *args, **kwargs):
        # Get limits.
        p_limit = self.get_arguments("limit") or [0]
        limit = int(p_limit[0]) or self.datastream.DEFAULT_LIMIT
        # Increase limit by 1 to detect datastream has more data
        limit = min(limit, self.datastream.DEFAULT_LIMIT) + 1
        # Collect filters
        filters = self.get_arguments("filter") or []
        ids = self.get_arguments("id") or None
        if ids:
            filters += ["id(%s)" % ",".join(ids)]
        # Start from change
        change_id = self.get_arguments("from")
        if change_id:
            change_id = change_id[0]
        else:
            change_id = None
        # Format
        fmt = self.get_arguments("format")
        if fmt:
            fmt = fmt[0]
        else:
            fmt = None
        # block argument
        p_block = self.get_arguments("block")
        to_block = bool(p_block) and bool(int(p_block[0]))
        first_change = None
        last_change = None
        nr = 1
        while True:
            r = []
            try:
                for item_id, change_id, data in self.datastream.iter_data(
                    limit=limit, filters=filters, change_id=change_id, fmt=fmt
                ):
                    if not first_change:
                        first_change = change_id
                    last_change = change_id
                    r += [data]
                    nr += 1
                    if nr == limit:
                        break  # Skip last additional item
            except ValueError:
                raise tornado.web.HTTPError(400)
            if to_block and not r:
                await self.service.wait(self.datastream.name)
            else:
                break
        self.set_header("Cache-Control", "no-cache")
        self.set_header("Content-Type", "application/json")
        self.set_header("X-NOC-DataStream-Total", str(self.datastream.get_total()))
        self.set_header("X-NOC-DataStream-Limit", str(limit))
        if first_change:
            self.set_header("X-NOC-DataStream-First-Change", str(first_change))
        if last_change:
            self.set_header("X-NOC-DataStream-Last-Change", str(last_change))
        if nr == limit:
            self.set_header("X-NOC-DataStream-More", "1")
        resp = ",".join(r)
        self.write(f"[{resp}]")


"""
Utils
"""

import subprocess
import re

def is_running(process):
    """True if process is running, string matching"""
    try:  # Linux/Unix
        with subprocess.Popen(["ps", "axw"], stdout=subprocess.PIPE) as sout:
            for proc in sout.stdout:
                if re.search(process, str(proc)):
                    return True
    except subprocess.SubprocessError():  # Windows
        with subprocess.Popen(["tasklist", "/v"], stdout=subprocess.PIPE) as sout:
            for proc in sout.stdout:
                if re.search(process, str(proc)):
                    return True
    return False


from dataclasses import dataclass
from datetime import datetime
from .apiprep import Apiprep
import json

class DataCrawler:

    def sortDatafromAPI(self, symbol: str, need: str, limit: int, *other: str) -> dict:
        '''Function for modifiing the given data from criptomarketcap api or from a file
         returning a dictionary with the needed data'''
        if limit:
            api = Apiprep(limit)
            data = api.call_api()
        else:
            api = Apiprep()
            data = api.call_api()

        temp_dict: dict = {}
        if not other:
            for crypto in data:
                temp_dict.update(
                    {crypto[symbol]: crypto["quote"]["USD"][need]})
        else:
            for crypto in data:
                temp_dict.update(
                    {crypto[symbol]: {need: crypto["quote"]["USD"][need]}})
                for args in other:
                    if args not in crypto.keys():
                        temp_dict[crypto[symbol]
                                  ][args] = crypto['quote']['USD'][args]
                    else:
                        temp_dict[crypto[symbol]][args] = crypto[args]
        return temp_dict

    def get_best_volume_crypto(self) -> dict[str, float]:
        '''This Function is for finding the best volume cryptoccurrences from the api'''
        this_dict = self.sortDatafromAPI("symbol", "volume_24h", 1000)
        max_value = max(this_dict, key=this_dict.get)
        return {max_value: this_dict[max_value]}

    def get_top20_crypto(self) -> dict:
        '''This function will return a dictionary containg the top 20 cryptoccurrences
        and the value of: actual prcice, percentage change 24h and rank'''

        get_dict = self.sortDatafromAPI(
            'symbol', 'price', 20, 'percent_change_24h', 'cmc_rank')
        top20_crypto: dict = {}
        for crypto in get_dict:
            top20_crypto.update({crypto: {
                                "price": get_dict[crypto]["price"], "percent_change_24h": get_dict[crypto]["percent_change_24h"], }})
        return top20_crypto

    def get_top76M_cost(self) -> dict:
        money: float = 0
        get_dict: dict = self.sortDatafromAPI(
            'symbol', 'volume_24h', 1000, 'price')
        for crypto in get_dict:
            if get_dict[crypto]['volume_24h'] > 76000000:
                money += get_dict[crypto]["price"]

        return {"money_top_volume": money}

    def get_top10(self):
        '''this function is for getting the top 10 cryptos by percentage change in 24h'''
        temp_dict: dict = self.sortDatafromAPI(
            'symbol', 'percent_change_24h', 2000)
        top10: dict = {}
        for j in sorted(temp_dict.items(), key=lambda x: x[1], reverse=True):

            if len(top10) < 10:
                top10[j[0]] = j[1]
        return top10

    def get_worst10(self):
        temp_dict: dict = self.sortDatafromAPI(
            'symbol', 'percent_change_24h', 2000)
        worst10: dict = {}

        for k in sorted(temp_dict.items(), key=lambda x: x[1]):

            if len(worst10) < 10:
                worst10[k[0]] = k[1]
        return worst10

    def get_top20_cost(self):
        '''this function return a dictionary with the total unity cost for each top 20 crypto'''
        temp_dict = self.sortDatafromAPI('symbol', 'price', 20,)
        today_price_top20 = 0
        for s in temp_dict:
            today_price_top20 += temp_dict[s]

        return {"money_to_buy_top20": today_price_top20}

    def get_earn_loss(self):
        '''function to return the earn or loss percentage if buy a unity of the top 20 crypto the day before'''
        data = self.sortDatafromAPI('symbol', 'percent_change_24h', 20, 'price')
        temp_dict = self.get_top20_cost()
        percent_change_24h = 0
        yesterday_price = 0
        earn_loss_percent = 0
        price = 0

        for s in data:
            percent_change_24h = data[s]["percent_change_24h"]
            price = data[s]['price']

            yesterday_price += price / (1 + (percent_change_24h / 100))

        earn_loss_percent = (
            (temp_dict['money_to_buy_top20'] - yesterday_price) / yesterday_price) * 100

        return {
            "earn_loss_percent": earn_loss_percent,
            "money_spent_yesterday": yesterday_price,
        }

def json_dump():
    '''simple function for wrapping all data and dump a json file'''
    try:
        data = DataCrawler()
        time = datetime.now().strftime("%Y-%m-%d %H.%M.%S")

        json_output = {
            "top_20": data.get_top20_crypto(),
            "top_20_cost": data.get_top20_cost(),
            "best_volume_24h": data.get_best_volume_crypto(),
            "best_10": data.get_top10(),
            "worst_10": data.get_worst10(),
            "money_to_buy_top_76M": data.get_top76M_cost(),
            "earn_loss": data.get_earn_loss(),
        }

        with open(f"{time}.json", "w", encoding="utf-8") as output:
            json.dump(json_output, output, ensure_ascii=False, indent=4)
    except:
        print("Error")


'''
★기출 19. 연산자 끼워 넣기★
N개의 수로 이루어진 수열이 주어집니다. 또 수와 수 사이에 끼워 넣을 수 있는 N-1개의 연산자가
주어집니다. 연사자는 덧셈, 뺄셈, 곱셈, 나눗셈으로만 이루어져 있습니다. 주어진 수의 순서를
바꾸면 안됩니다. 식의 계산은 연산자 우선순위를 무시하고 앞에서부터 진행해야 합니다. 또 나눗셈은
정수 나눗셈으로 몫만 취합니다. N개의 수와 N-1개의 연산자가 주어졌을 때, 만들 수 있는 식의 결과가
최대인 것과 최소인 것을 구하는 프로그램을 작성하세요

이 문제는 최대 11개의 수가 주어졌을 때, 각 수와 수 사이에 사칙연산 중 하나를 삽입하는
모든 경우에 대해 만들어질 수 있는 결과의 최댓값 및 최솟값을 구하면 된다. 따라서 모든 경우의 수를
계산하기 위하여 완전탐색을 이용하여 문제를 해결할 수 있다. 이 문제에서는 각 사칙연산을 중복하여
사용할 수 있기 때문에, 이 문제를 풀기 위해서는 중복 순열을 계산해야 한다. 본 문제에 대한 정답
소스코드는 itertools의 중복 순열(product) 클래스를 사용하지 않고 DFS를 이용하여 풀겠다.
'''
# A19
n = int(input())
# 연산을 수행하고자 하는 수 리스트
data = list(map(int, input().split()))
# 더하기, 빼기, 곱하기, 나누기 연산자 개수
add, sub, mul, div = map(int, input().split())

# 최솟값과 최댓값 초기화
min_value = 1e9
max_value = -1e9

# 깊이 우선 탐색(DFS) 메서드
def dfs(i, now):
  global min_value, max_value, add, sub, mul, div
  # 모든 연산자를 다 사용한 경우, 최솟값과 최댓값 업데이트
  if i == n:
    min_value = min(min_value, now)
    max_value = max(max_value, now)
  else:
    # 각 연산자에 대하여 재귀적으로 수행
    if add > 0:
      add -= 1
      dfs(i + 1, now + data[i])
      add += 1
    if sub > 0:
      sub -= 1
      dfs(i + 1, now - data[i])
      sub += 1
    if mul > 0:
      mul -= 1
      dfs(i + 1, now * data[i])
      mul += 1
    if div > 0:
      div -= 1
      dfs(i + 1, int(now / data[i]))
      div += 1

# DFS 메서드 호출
dfs(1, data[0])

# 최댓값과 최솟값 차례대로 출력
print(max_value)
print(min_value)

This is the Coding Test/05 DFS n BFS/pastQ19.py


import sys
sys.setrecursionlimit(10**7)
input = sys.stdin.readline

m = 2019
t = input().rstrip()
s = t[::-1]

x = 1
tot = 0
ans = 0
cnt = [0] * (m)
for i in range(len(s)):
    cnt[tot] += 1
    tot += (int(s[i])) * x
    tot %= m
    ans += cnt[tot]
    x = x * 10 % m
print(ans)


# Mode: Python; coding: utf-8
#GODMODE Cloze and Custom Keyboard Shortcuts
#Code compiled by Joseph Yasmeh for Anki 2.1 on Mac on April 2019.
#My main contribution to this code is figuring out how to disable unneccessary tooltip notifications, how to get answer shortcuts to flip the card without moving to the next card, and how to merge a couple addons into one, so that they work together.
#
#The code for shortcuts is by Liresol (erichu264@gmail.com), from the addon Customize Keyboard Shortcuts
#I attempted to make GODMODE with its own limited shortcuts, but found that it interfered the above addon.
#So the easiest solution for me was to merge the two addons into one so that they don't conflict.
#
#The code for automatic note type switching is not my own. Credit goes to:
#Hyun Woo Park (phu54321@naver.com),
#Roland Sieker (ospalh@gmail.com), and
#Steve AW (steveawa@gmail.com)
#
#The above authors made their code free to use and modify under GNU GPL. This code is too.
#
#To learn how to make "monkey patch" add-ons, see https://apps.ankiweb.net/docs/addons.html#monkey-patching-and-method-wrapping
#
#CHANGELOG
#July 17 2019: Edited "reviewer choice" in config.json to fix a bug where 1234 was set to flip cards (and then #always rate as good) rather than rate them as hard, good, or easy.
#August 17 2019: Edited "window_browser add note" config.json to fix a bug where Ctrl+E would not make a new Cloze when the browser was open, given that shortcut was also being used for adding a card when the browser is open.
#January 28 2020: Fixed a bug only some users saw on Mac and Windows where cards could not be added. Removed global references, which were not being acknowledged for some users. Fixed a Python "TypeError" in the function "change_model_to."
#June 9 2020: Updated to cs_toolbarCenterLinks to work with Anki versions above 2.21. The function it referred to was changed in Anki, so I changed the syntax to match it.

########################################################################
#SECTION 1
#Telling the software what to import. Some of these are redundant because I'm copying and pasting my old code.
########################################################################
from aqt import mw
from aqt.qt import *
from anki.hooks import runHook,addHook
from aqt.utils import showWarning
from aqt.toolbar import Toolbar
from aqt.editor import Editor,EditorWebView
from aqt.reviewer import Reviewer
from anki.utils import json
from bs4 import BeautifulSoup
import warnings
from . import cs_functions as functions
from aqt import mw
from aqt.addcards import AddCards
from anki.hooks import wrap
from anki.hooks import addHook, runHook
from aqt.utils import tooltip
from anki.lang import _
from anki import version
import re
from aqt.utils import tooltip, showInfo
from anki.hooks import wrap
import aqt.editor
from aqt.editor import *

########################################################################
#SECTION 2
#Redefining how Anki deals with Cloze creation
#Deleted the tooltip warning given it is now redundant
#I tried to use the method of saving the original code and calling a custom version, as mentioned monkey patching section of the Anki 2.1 addon creation documentation
#But the code wouldn't work. So if something breaks with the addon in future Anki versions, look here and in section 5.
########################################################################
def onClozeADDON(self):
	self.saveNow(self._onCloze, keepFocus=True)

Editor.onCloze = onClozeADDON

def _onClozeADDON(self):
	highest = 0
	for name, val in list(self.note.items()):
		m = re.findall(r"\{\{c(\d+)::", val)
		if m:
			highest = max(highest, sorted([int(x) for x in m])[-1])
	# reuse last
	if not self.mw.app.keyboardModifiers() & Qt.ShiftModifier:
		highest += 1
	# must start at 1
	highest = max(1, highest)
	self.web.eval("wrap('{{c%d::', '}}');" % highest)

Editor._onCloze = _onClozeADDON

########################################################################
#SECTION 3
#This section of code does the automatic note type switching.
########################################################################

def modelExists(model_name):
    return bool(mw.col.models.byName(model_name))

def findModelName():
    basic_note_type = list(filter(modelExists, ['Basic', _('Basic')]))
    cloze_note_type = list(filter(modelExists, ['Cloze', _('Cloze')]))

    if not basic_note_type:
        tooltip('[Automatic basic to cloze] Cannot find source \'Basic\' model')
        basic_note_type = None

    if not cloze_note_type:
        tooltip('[Automatic basic to cloze] Cannot find target \'Cloze\' model')
        cloze_note_type = None
    else:
        cloze_note_type = cloze_note_type[0]

addHook("profileLoaded", findModelName)

#This function changes the models (note types).
# TypeError fixed an older bug from the newAddCards function, where Python got upset that ModelName was set equal to none.
def change_model_to(chooser, model_name):
    """Change to model with name model_name"""
    # Mostly just a copy and paste from the bottom of onModelChange()
    m = chooser.deck.models.byName(model_name)
    try:
        chooser.deck.conf['curModel'] = m['id']
    except TypeError:
        return
    cdeck = chooser.deck.decks.current()
    cdeck['mid'] = m['id']
    chooser.deck.decks.save(cdeck)
    runHook("currentModelChanged")
    chooser.mw.reset()

def isClozeNote(note):
    for name, val in note.items():
        if re.search(r'\{\{c(\d+)::', val):
            return True
    return False

#If you don't use this, tags won't save when models switch.
def callWithCallback(f, cb):
        f(cb)

#Joseph changed the tooltip notification to "Added" to remove the unneccesary warning note type switching.
#Also set modelchooser to go to specific note type names. This fixed a bug some users had.
def newAddCards(self, _old):
    note = self.editor.note
    basic_note_type = list(filter(modelExists, ['Basic', _('Basic')]))
    cloze_note_type = list(filter(modelExists, ['Cloze', _('Cloze')]))

    if not (basic_note_type and cloze_note_type):
        return _old(self)
    if note.model()['name'] in basic_note_type and isClozeNote(note):
        def cb1():
            change_model_to(self.modelChooser, 'Cloze')
            callWithCallback(self.editor.saveNow, cb2)
        def cb2():
            self._addCards()
            change_model_to(self.modelChooser, 'Basic')
            tooltip(_("Added"), period=500)
        callWithCallback(self.editor.saveNow, cb1)
    else:
        return _old(self)

AddCards.addCards = wrap(AddCards.addCards, newAddCards, "around")

#This disables the prompt for changing the note type.
#I deleted the block of code that checks if the card type is set to Cloze.
def addNoteADDON(self, note):
        note.model()['did'] = self.deckChooser.selectedId()
        ret = note.dupeOrEmpty()
        if ret == 1:
            showWarning(_(
                "The first field is empty."),
                help="AddItems#AddError")
            return
        cards = self.mw.col.addNote(note)
        if not cards:
            showWarning(_("""\
The input you have provided would make an empty \
question on all cards."""), help="AddItems")
            return None
        self.addHistory(note)
        self.mw.requireReset()
        return note

AddCards.addNote = addNoteADDON

########################################################################
#SECTION 4
#This section is from the add-on Customize Keyboard Shortcuts
########################################################################
#Gets config.json as config
config = mw.addonManager.getConfig(__name__)
CS_CONFLICTSTR = "Custom Shortcut Conflicts: \n\n"
#config_scuts initialized after cs_traverseKeys
Qt_functions = {"Qt.Key_Enter":Qt.Key_Enter,
                "Qt.Key_Return":Qt.Key_Return,
                "Qt.Key_Escape":Qt.Key_Escape,
                "Qt.Key_Space":Qt.Key_Space,
                "Qt.Key_Tab":Qt.Key_Tab,
                "Qt.Key_Backspace":Qt.Key_Backspace,
                "Qt.Key_Delete":Qt.Key_Delete,
                "Qt.Key_Left":Qt.Key_Left,
                "Qt.Key_Down":Qt.Key_Down,
                "Qt.Key_Right":Qt.Key_Right,
                "Qt.Key_Up":Qt.Key_Up,
                "Qt.Key_PageUp":Qt.Key_PageUp,
                "Qt.Key_PageDown":Qt.Key_PageDown,
                "<nop>":""
                }

#There is a weird interaction with QShortcuts wherein if there are 2 (or more)
#QShortcuts mapped to the same key and function and both are enabled,
#the shortcut doesn't work

#Part of this code exploits that by adding QShortcuts mapped to the defaults
#and activating/deactivating them to deactivate/activate default shortcuts

#There isn't an obvious way to get the original QShortcut objects, as
#The addons executes after the setup phase (which creates QShortcut objects)

def cs_traverseKeys(Rep, D):
    ret = {}
    for key in D:
        if isinstance(D[key],dict):
            ret[key] = cs_traverseKeys(Rep,D[key])
        elif D[key] not in Rep:
            ret[key] = D[key]
        else:
            ret[key] = Rep[D[key]]
    return ret

config_scuts = cs_traverseKeys(Qt_functions,config)

#This is the worst code I think I've written for custom-shortcuts
#Since QShortcuts cannot reveal their action (to the best of my knowledge),
#This map reconstructs what each QShortcut is supposed to do from its id
#The ids were found manually and are thus incredibly dubious
id_main_config = {-1: "main debug",
                  -2: "main deckbrowser",
                  -3: "main study",
                  -4: "main add",
                  -5: "main browse",
                  -6: "main stats",
                  -7: "main sync"
                  }

#Finds all the shortcuts, figures out relevant ones from hardcoded id check,
#and sets it to the right one
#This function has a side effect of changing the shortcut's id
def cs_main_setupShortcuts():
    qshortcuts = mw.findChildren(QShortcut)
    for scut in qshortcuts:
        if scut.id() in id_main_config:
            scut.setKey(config_scuts[id_main_config[scut.id()]])

#Governs the shortcuts on the main toolbar
def cs_mt_setupShortcuts():
    m = mw.form
    #Goes through and includes anything on the duplicates list
    scuts_list = {
        "m_toolbox quit": [config_scuts["m_toolbox quit"]],
        "m_toolbox preferences": [config_scuts["m_toolbox preferences"]],
        "m_toolbox undo": [config_scuts["m_toolbox undo"]],
        "m_toolbox see documentation": [config_scuts["m_toolbox see documentation"]],
        "m_toolbox switch profile": [config_scuts["m_toolbox switch profile"]],
        "m_toolbox export": [config_scuts["m_toolbox export"]],
        "m_toolbox import": [config_scuts["m_toolbox import"]],
        "m_toolbox study": [config_scuts["m_toolbox study"]],
        "m_toolbox create filtered deck": [config_scuts["m_toolbox create filtered deck"]],
        "m_toolbox addons": [config_scuts["m_toolbox addons"]]
    }
    for act,key in config_scuts["m_toolbox _duplicates"].items():
        scuts_list[act].append(key)
    m.actionExit.setShortcuts(scuts_list["m_toolbox quit"])
    m.actionPreferences.setShortcuts(scuts_list["m_toolbox preferences"])
    m.actionUndo.setShortcuts(scuts_list["m_toolbox undo"])
    m.actionDocumentation.setShortcuts(scuts_list["m_toolbox see documentation"])
    m.actionSwitchProfile.setShortcuts(scuts_list["m_toolbox switch profile"])
    m.actionExport.setShortcuts(scuts_list["m_toolbox export"])
    m.actionImport.setShortcuts(scuts_list["m_toolbox import"])
    m.actionStudyDeck.setShortcuts(scuts_list["m_toolbox study"])
    m.actionCreateFiltered.setShortcuts(scuts_list["m_toolbox create filtered deck"])
    m.actionAdd_ons.setShortcuts(scuts_list["m_toolbox addons"])

#Governs the shortcuts on the review window
def cs_review_setupShortcuts(self):
    dupes = []
    ret = [
        (config_scuts["reviewer edit current"], self.mw.onEditCurrent),
        (config_scuts["reviewer flip card 1"], self.onEnterKey),
        (config_scuts["reviewer flip card 2"], self.onEnterKey),
        (config_scuts["reviewer flip card 3"], self.onEnterKey),
        (config_scuts["reviewer replay audio 1"], self.replayAudio),
        (config_scuts["reviewer replay audio 2"], self.replayAudio),
        (config_scuts["reviewer set flag 1"], lambda: self.setFlag(1)),
        (config_scuts["reviewer set flag 2"], lambda: self.setFlag(2)),
        (config_scuts["reviewer set flag 3"], lambda: self.setFlag(3)),
        (config_scuts["reviewer set flag 4"], lambda: self.setFlag(4)),
        (config_scuts["reviewer set flag 0"], lambda: self.setFlag(0)),
        (config_scuts["reviewer mark card"], self.onMark),
        (config_scuts["reviewer bury note"], self.onBuryNote),
        (config_scuts["reviewer bury card"], self.onBuryCard),
        (config_scuts["reviewer suspend note"], self.onSuspend),
        (config_scuts["reviewer suspend card"], self.onSuspendCard),
        (config_scuts["reviewer delete note"], self.onDelete),
        (config_scuts["reviewer play recorded voice"], self.onReplayRecorded),
        (config_scuts["reviewer record voice"], self.onRecordVoice),
        (config_scuts["reviewer options menu"], self.onOptions),
        (config_scuts["reviewer choice 1"], lambda: self._answerCard(1)),
        (config_scuts["reviewer choice 2"], lambda: self._answerCard(2)),
        (config_scuts["reviewer choice 3"], lambda: self._answerCard(3)),
        (config_scuts["reviewer choice 4"], lambda: self._answerCard(4)),
    ]
    for scut in config_scuts["reviewer _duplicates"]:
        dupes.append((config_scuts["reviewer _duplicates"][scut],self.sToF(scut)))
    return dupes + ret

#The function to setup shortcuts on the Editor
#Something funky is going on with the default MathJax and LaTeX shortcuts
#It does not affect the function (as I currently know of)
def cs_editor_setupShortcuts(self):
    # if a third element is provided, enable shortcut even when no field selected
    cuts = [
        (config_scuts["editor card layout"], self.onCardLayout, True),
        (config_scuts["editor bold"], self.toggleBold),
        (config_scuts["editor italic"], self.toggleItalic),
        (config_scuts["editor underline"], self.toggleUnderline),
        (config_scuts["editor superscript"], self.toggleSuper),
        (config_scuts["editor subscript"], self.toggleSub),
        (config_scuts["editor remove format"], self.removeFormat),
        (config_scuts["editor foreground"], self.onForeground),
        (config_scuts["editor change col"], self.onChangeCol),
        (config_scuts["editor cloze"], self.onCloze),
        (config_scuts["editor cloze alt"], self.onAltCloze),
        (config_scuts["editor add media"], self.onAddMedia),
        (config_scuts["editor record sound"], self.onRecSound),
        (config_scuts["editor insert latex"], self.insertLatex),
        (config_scuts["editor insert latex equation"], self.insertLatexEqn),
        (config_scuts["editor insert latex math environment"], self.insertLatexMathEnv),
        (config_scuts["editor insert mathjax inline"], self.insertMathjaxInline),
        (config_scuts["editor insert mathjax block"], self.insertMathjaxBlock),
        (config_scuts["editor insert mathjax chemistry"], self.insertMathjaxChemistry),
        (config_scuts["editor html edit"], self.onHtmlEdit),
        (config_scuts["editor focus tags"], self.onFocusTags, True),
        (config_scuts["editor _extras"]["paste custom text"], self.customPaste)
    ]
    runHook("setupEditorShortcuts", cuts, self)
    for row in cuts:
        if len(row) == 2:
            keys, fn = row
            fn = self._addFocusCheck(fn)
        else:
            keys, fn, _ = row
        scut = QShortcut(QKeySequence(keys), self.widget, activated=fn)

#IMPLEMENTS Browser shortcuts
def cs_browser_setupShortcuts(self):
    f = self.form
    f.previewButton.setShortcut(config_scuts["window_browser preview"])
    f.actionReschedule.setShortcut(config_scuts["window_browser reschedule"])
    f.actionSelectAll.setShortcut(config_scuts["window_browser select all"])
    f.actionUndo.setShortcut(config_scuts["window_browser undo"])
    f.actionInvertSelection.setShortcut(config_scuts["window_browser invert selection"])
    f.actionFind.setShortcut(config_scuts["window_browser find"])
    f.actionNote.setShortcut(config_scuts["window_browser goto note"])
    f.actionNextCard.setShortcut(config_scuts["window_browser goto next note"])
    f.actionPreviousCard.setShortcut(config_scuts["window_browser goto previous note"])
    f.actionChangeModel.setShortcut(config_scuts["window_browser change note type"])
    f.actionGuide.setShortcut(config_scuts["window_browser guide"])
    f.actionFindReplace.setShortcut(config_scuts["window_browser find and replace"])
    f.actionTags.setShortcut(config_scuts["window_browser filter"])
    f.actionCardList.setShortcut(config_scuts["window_browser goto card list"])
    f.actionReposition.setShortcut(config_scuts["window_browser reposition"])
    f.actionFirstCard.setShortcut(config_scuts["window_browser first card"])
    f.actionLastCard.setShortcut(config_scuts["window_browser last card"])
    f.actionClose.setShortcut(config_scuts["window_browser close"])
    f.action_Info.setShortcut(config_scuts["window_browser info"])
    f.actionAdd_Tags.setShortcut(config_scuts["window_browser add tag"])
    f.actionRemove_Tags.setShortcut(config_scuts["window_browser remove tag"])
    f.actionToggle_Suspend.setShortcut(config_scuts["window_browser suspend"])
    f.actionDelete.setShortcut(config_scuts["window_browser delete"])
    f.actionAdd.setShortcut(config_scuts["window_browser add note"])
    f.actionChange_Deck.setShortcut(config_scuts["window_browser change deck"])
    f.actionRed_Flag.setShortcut(config_scuts["window_browser flag_red"])
    try:
        f.actionOrange_Flag.setShortcut(config_scuts["window_browser flag_orange"])
    except AttributeError:
        f.actionPurple_Flag.setShortcut(config_scuts["window_browser flag_orange"])
    f.actionGreen_Flag.setShortcut(config_scuts["window_browser flag_green"])
    f.actionBlue_Flag.setShortcut(config_scuts["window_browser flag_blue"])
    f.actionSidebar.setShortcut(config_scuts["window_browser goto sidebar"])
    f.actionToggle_Mark.setShortcut(config_scuts["window_browser toggle mark"])
    f.actionClear_Unused_Tags.setShortcut(config_scuts["window_browser clear unused tags"])
    f.actionFindDuplicates.setShortcut(config_scuts["window_browser find duplicates"])
    f.actionSelectNotes.setShortcut(config_scuts["window_browser select notes"])
    f.actionManage_Note_Types.setShortcut(config_scuts["window_browser manage note types"])

#detects shortcut conflicts
#Ignores the Add-on (Ω) options
def cs_conflictDetect():
    if config["Ω enable conflict warning"].upper() != "Y":
        return
    ext_list = {}
    dupes = False
    for e in config:
        sub = e[0:(e.find(" "))]
        val = config[e]
        if sub in ext_list:
            if isinstance(val,dict):
                for key in val:
                    ext_list[sub][key + " in " + e] = val[key].upper()
            else:
                ext_list[sub][e] = val.upper()
        elif sub != "Ω":
            ext_list[sub] = {e:val.upper()}
    inv = {}
    conflictStr = CS_CONFLICTSTR
    conflict = False
    for key in ext_list:
        inv = {}
        x = ext_list[key]
        for e in x:
            if x[e] not in inv:
                inv[x[e]] = [e]
            else:
                inv[x[e]].append(e)
        for k in inv:
            if(len(inv[k])) == 1:
                continue
            if k == "<NOP>":
                continue
            if not k:
                continue
            conflict = True
            conflictStr += ", ".join(inv[k])
            conflictStr += "\nshare '" + k + "' as a shortcut\n\n"
    if conflict:
        conflictStr += "\nThese shortcuts will not work.\n"
        conflictStr += "Please change them in the config.json."
        showWarning(conflictStr)

def cs_toolbarCenterLinks(self) -> str:
    links = [
        self.create_link(
            "decks",
            _("Decks"),
            self._deckLinkHandler,
            tip=_("Shortcut key: %s") % config_scuts["main deckbrowser"],
            id="decks",
        ),
        self.create_link(
            "add",
            _("Add"),
            self._addLinkHandler,
            tip=_("Shortcut key: %s") % config_scuts["main add"],
            id="add",
        ),
        self.create_link(
            "browse",
            _("Browse"),
            self._browseLinkHandler,
            tip=_("Shortcut key: %s") % config_scuts["main browse"],
            id="browse",
        ),
        self.create_link(
            "stats",
            _("Stats"),
            self._statsLinkHandler,
            tip=_("Shortcut key: %s") % config_scuts["main stats"],
            id="stats",
        ),
    ]

    links.append(self._create_sync_link())

    gui_hooks.top_toolbar_did_init_links(links, self)

    return "\n".join(links)

#Functions that execute on startup
Editor.customPaste = functions.cs_editor_custom_paste
Editor._customPaste = functions.cs_uEditor_custom_paste
Editor.onAltCloze = functions.cs_editor_onAltCloze
Editor._onAltCloze = functions.cs_uEditor_onAltCloze
Reviewer.sToF = functions.review_sToF
Editor.setupShortcuts = cs_editor_setupShortcuts
Reviewer._shortcutKeys = cs_review_setupShortcuts
Toolbar._centerLinks = cs_toolbarCenterLinks

#Shortcut setup for main window & other startup functions
cs_mt_setupShortcuts()
cs_main_setupShortcuts()
cs_conflictDetect()

#Redraws the toolbar with the new shortcuts
mw.toolbar.draw()

#Hooks to setup shortcuts at the right time
addHook('browser.setupMenus', cs_browser_setupShortcuts)

########################################################################
#SECTION 5
#This code shows the answer when you press the shortcuts for again, hard, good or easy.
#By default, Anki's code won't flip the card when you press those shortcuts--it would just go to the next card
#This solution was written entirely by Joseph. Yay.
#I tried to use the method of saving the original code and calling a custom version, as mentioned monkey patching section of the Anki 2.1 addon creation documentation
#But the code wouldn't work. So if something breaks with the addon in future Anki versions, look here and in section 2.
########################################################################

from aqt.reviewer import Reviewer

def _answerCardADDON(self, ease):
        "Call custom function for compatibility with ColorConfirmation add-on. Solution by MacMarc."
        try:
                self.CustomAnswerCard(ease)
        except Exception as e:
                pass
        "Reschedule card and show next."
        if self.mw.state != "review":
            # showing resetRequired screen; ignore key
            return
        if self.mw.col.sched.answerButtons(self.card) < ease:
            return
        if self.state == "question":
                self._getTypedAnswer()
        if self.state == "answer":
                self.mw.col.sched.answerCard(self.card, ease)
                self._answeredIds.append(self.card.id)
                self.mw.autosave()
                self.nextCard()

Reviewer._answerCard = _answerCardADDON


from karel.stanfordkarel import *

# Here is a place to program your Section problem

def main():
    """
    You should write your code to make Karel do its task in
    this function. Make sure to delete the 'pass' line before
    starting to write your own code. You should also delete this
    comment and replace it with a better, more descriptive one.
    """
    while front_is_clear():
        if beepers_present():
            build_hospital()

        safe_move()

def safe_move():
    if front_is_clear():
        move()

#building hospital
def build_hospital():
    ascend_hurdle()
    move()
    descend_hurdle()

def ascend_hurdle():
    turn_left()
    for i in range(2):
        move()
        put_beeper()
    turn_right()

def descend_hurdle():
    turn_right()
    #put_beeper()
    for i in range(2):
        put_beeper()
        move()
    put_beeper()
    turn_left()

def turn_right():
    for i in range(3):
        turn_left()
if __name__ == '__main__':
    main()