from keras.models import Sequential
from keras import layers
from keras.preprocessing.text import Tokenizer
import pandas as pd
from sklearn import preprocessing
from keras.layers import Flatten
from keras.layers.embeddings import Embedding
from keras.preprocessing.sequence import pad_sequences
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

df = pd.read_csv('imdb_master.csv',encoding='latin-1')
sentences = df['review'].values
y = df['label'].values
max_review_len= max([len(s.split()) for s in sentences])

#tokenizing data
tokenizer = Tokenizer(num_words=max_review_len)
tokenizer.fit_on_texts(sentences)

vocab_size= len(tokenizer.word_index)+1
sentences = tokenizer.texts_to_sequences(sentences)
padded_docs= pad_sequences(sentences,maxlen=max_review_len)

#getting the vocabulary of data
#sentences = tokenizer.texts_to_matrix(sentences)

le = preprocessing.LabelEncoder()
y = le.fit_transform(y)
X_train, X_test, y_train, y_test = train_test_split(padded_docs, y, test_size=0.25, random_state=1000)

# Number of features
# print(input_dim)
model = Sequential()
model.add(Embedding(vocab_size, 50, input_length=max_review_len))
model.add(layers.Flatten())
model.add(layers.Dense(300,activation='relu'))
model.add(layers.Dense(3, activation='softmax'))
model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])
history=model.fit(X_train,y_train, epochs=5, verbose=True, validation_data=(X_test,y_test), batch_size=256)
#evaluating model on training data and calculating loss and accuracy of training data
[train_loss, train_acc] = model.evaluate(X_test,y_test)
print("Evaluation result on Train Data : Loss = {}, accuracy = {}".format(train_loss, train_acc))
# summarize history for accuracy&loss
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['accuray', 'val_accuracy','loss','val_loss'], loc='upper right')
plt.show()

# Predicting the Value for test sample
pred = model.predict_classes(X_test[[2],:])
print("Actual Prediction",y_test[1],"Predicted Prediction", pred)


import pandas as pd
import numpy as np
# from app02 import predict

abnb = pd.read_csv('AbnbSF_clean.csv')
print(abnb.shape)

abnb.info()

print("\nNULL :\n", abnb.isnull().sum())

def wrangle(X):
    """Wrangle train, validate, and test sets in the same way"""

    # Prevent SettingWithCopyWarning
    X = X.copy()

    # Fillna for other columns with most frquent values. Definitely with 1 (one)
    cols3 = ['Bedrooms', 'Beds', 'Bathrooms']
    for col in cols3:
        X[col] = X[col].fillna(X[col].mode()[0])

    # return the wrangled dataframe
    return X

abnb = wrangle(abnb)

df = abnb.copy()
df.head(3)
print("\ndescribe price: \n", df['Price'].describe())

# Limit the price: remove 0.
df =   df[(df['Price'].between(20,800))]#

# Get a dataframe with all train columns except unneeded columns
df_features = df.drop(columns=['Price'], axis=1) ## Without Lat/Long was done on 07/28

# Get a list of the numeric features
numeric_features = df_features.select_dtypes(include='number').columns.tolist()

# Cardinality of the nonnumeric features
cardinality = df_features.select_dtypes(exclude='number').nunique()

# List of all categorical features with  (2< cardinality <70)
cat_features = cardinality[(cardinality <= 70) & (cardinality > 2) ].index.tolist()

features = numeric_features + cat_features
print(features)

train = df[features]

print(train.head(2))

# Define X & y
X_train = df[features]
y_train = df['Price']

# Split into train & test
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X_train, y_train, test_size=0.2, random_state=42)

print(X_train.shape, y_train.shape,  X_test.shape, y_test.shape)

from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from scipy.stats import randint, uniform

import xgboost as xgb
from xgboost import XGBRegressor

# Shapley Value & XGBoost

feature_sh = ['Accommodates',  'Bathrooms', 'Bedrooms', 'Beds', 'Minimum_Nights', 'Maximum_Nights', 'Guests_Included']

# Define X & y
X_train = df[feature_sh]
y_train = df['Price']

# Split into train & test
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X_train, y_train, test_size=0.2, random_state=42)
X_train.shape, y_train.shape,  X_test.shape, y_test.shape

xgbreg2 = XGBRegressor(n_estimators=100,
                       random_state=42,
                       n_jobs=2,
                       max_depth=4,
                       learning_rate=0.1)

xgbreg2.fit(X_train, y_train)

import shap

# Using the predict function
def predict1(Accommodates,	Bedrooms,	Bathrooms,	Beds,	Minimum_Nights,	Maximum_Nights,	Guests_Included):

    # Make dataframe from the inputs
    dshap = pd.DataFrame(
        data=[[Accommodates,	Bathrooms,	Bedrooms,	Beds,	Minimum_Nights,	Maximum_Nights,	Guests_Included]],
        columns=['Accommodates',	'Bathrooms',	'Bedrooms',	'Beds',	'Minimum_Nights',	'Maximum_Nights',	'Guests_Included']
    )

    # Get the model's prediction
    pred = xgbreg2.predict(dshap)[0]

    result = f'is ${pred:,.0f} \n'
    print(result)

    # print(type(pred))
    return pred

Accommodates = 2
Bathrooms= 2
Bedrooms = 2
Beds= 3
Minimum_Nights = 1
Maximum_Nights = 3
Guests_Included = 5

print("\nThe airbnb rent prediction for below features is:")
print("Accommodates =", Accommodates)
print("Bathrooms =", Bathrooms)
print("Bedrooms =", Bedrooms)
print("Beds =", Beds)
print("Minimum_Nights =", Minimum_Nights)
print("Maximum_Nights =", Maximum_Nights)
print("Guests_Included =", Guests_Included)

pred = predict1(Accommodates, Bathrooms, Bedrooms, Beds, Minimum_Nights , Maximum_Nights, Guests_Included)


import json
import re
import math
import time
import datetime
import threading
import logging

import pymysql
from tweepy import Stream, OAuthHandler
from tweepy.streaming import StreamListener

current_version = 10  # must be int
auth = OAuthHandler('rVBGZlv0fXG535e6XtNFTHKFB',
                    'a8uK4zSu5uku5mpOXggQxa9k29QDF4aEjbAWDOuBDZX1h59WhT')
auth.set_access_token('3260207947-ieMSsYpzSwuCkM5ceM4c8AudzQv0QHpElLULAFa',
                      'PaWOAasOFoPFDdYMARVr31mK1iy35sOqhZzpr1fDmNkZX')

logging.basicConfig(filename='tn_' + str(current_version) + '.log',
                    level=logging.INFO,
                    format="%(asctime)s:%(levelname)s:%(message)s")
# tweets tweets_w_nums total_nums non_nums no_data_tweets
stream_speed_divider = 2
queue = []
tweet_data = [0, 0, 0, 0, 0]
database_latency_min = -1
database_latency_max = 0
database_latency_avg_sum = 0
database_latency_num = 1
processing_latency_min = -1
processing_latency_max = 0
processing_latency_avg_sum = 0
processing_latency_num = 1

logging.info("Starting Twitter Numbers Version: " + str(current_version))
print("Twitter Numbers Version: " + str(current_version))

class myThread (threading.Thread):

    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter

    def run(self):
        if self.threadID == 1:
            print("Main Loop Thread Starting")
            main_loop()
            print("Main Loop Thread Ending")
        elif self.threadID == 2:
            print("Streaming Thread Starting")
            while True:
                try:
                    print("Start Streaming")
                    logging.info("Start Streaming")
                    twitterStream = Stream(auth, listener())
                    twitterStream.sample()

                except Exception as e:
                    print("Error. Restarting Stream.... Error: ")
                    print(e.__doc__)
                    print("Error Message")
                    logging.error("Streaming Error: "
                                  + str(e)
                                  + ": " + str(e.__doc__))
                    time.sleep(5)
            print("Streaming Thread Ending")

class listener(StreamListener):

    def __init__(self, path=None):
        self.path = path
        self.rec_tries_420 = 0
        self.rec_tries_other = 0
        self.data_count = 0

    def on_data(self, data):

        if self.data_count == 0:
            d1 = datetime.datetime.now()
            update_tweet_data(0)

            all_data = json.loads(data)

            pro_l = process_tweet(all_data)

            if len(pro_l) != 0:
                update_tweet_data(1)
            for s in pro_l:
                update_tweet_data(2)
                add_to_queue(s)

            self.data_count = get_stream_speed_divider()
            d2 = datetime.datetime.now()
            processing_latency((d2 - d1).microseconds)
            # print((d2 - d1))
        else:
            self.data_count -= 1
        return(True)

    def on_error(self, status):
        print("error code: " + str(status))
        logging.error("Stream Connection Error: " + str(status))
        if status == 420:
            sleep_time = 60 * math.pow(2, self.rec_tries_420)
            print(time.strftime("%Y%m%d_%H%M%S"))
            print("Recnnecting in: ")
            print(str(sleep_time / 60) + " minutes")
            logging.info("Reconnecting in: "
                         + str(sleep_time / 60)
                         + "minutes")
            print("'''")
            time.sleep(sleep_time)
            self.rec_tries_420 += 1
        else:
            sleepy = 5
            print(time.strftime("%Y%m%d_%H%M%S"))
            print("A reconnection attempt will occur in "
                  + str(sleepy)
                  + " seconds.")
            logging.info("Reconnecting in: " + str(sleepy))
            time.sleep(sleepy)

        return True

def get_stream_speed_divider():
    global stream_speed_divider
    return stream_speed_divider

def add_to_queue(num):
    global queue
    queue.append(num)

def update_tweet_data(index):
    # tweets tweets_w_nums total_nums non_nums no_data_tweets
    global tweet_data
    tweet_data[index] += 1

def database_latency(time):
    global database_latency_min
    global database_latency_max
    global database_latency_avg_sum
    global database_latency_num
    database_latency_num += 1
    database_latency_avg_sum += time
    if database_latency_min == -1:
        database_latency_min = time
    elif time < database_latency_min:
        database_latency_min = time
    if time > database_latency_max:
        database_latency_max = time

def processing_latency(time):
    global processing_latency_min
    global processing_latency_max
    global processing_latency_avg_sum
    global processing_latency_num
    processing_latency_num += 1
    processing_latency_avg_sum += time
    if processing_latency_min == -1:
        processing_latency_min = time
    elif time < processing_latency_min:
        processing_latency_min = time
    if time > processing_latency_max:
        processing_latency_max = time

def process_tweet(tweet):
    try:
        # pro_tweet_l = re.findall(r'\b\d+\b', tweet["text"])
        pro_tweet_l = re.findall('[-+]?[.]?[\d]+(?:,\d\d\d)*'
                                 '[\.]?\d*(?:[eE][-+]?\d+)?',
                                 tweet["text"])
        for n in range(0, len(pro_tweet_l)):
            pro_tweet_l[n] = str(pro_tweet_l[n])
            pro_tweet_l[n] = pro_tweet_l[n].replace(",", "")
            if pro_tweet_l[n][len(pro_tweet_l[n]) - 1] == '.':
                pro_tweet_l[n] = pro_tweet_l[n][:-1]
            if '.' in pro_tweet_l[n]:
                pro_tweet_l[n] = float(pro_tweet_l[n])
                pro_tweet_l[n] = str(pro_tweet_l[n])
    except:
        update_tweet_data(4)
        pro_tweet_l = []
    return pro_tweet_l

def add_to_db(num, current_table_name):
    try:
        if c.execute("SELECT * FROM "
                     + current_table_name
                     + " WHERE number LIKE %s", (num)) == 0:
            c.execute("INSERT INTO "
                      + current_table_name
                      + " (number,count) VALUES (%s,1)", (num))
        else:
            c.execute("SELECT count From "
                      + current_table_name
                      + " WHERE number LIKE %s", (num))
            c.execute("SELECT count From "
                      + current_table_name
                      + " WHERE number LIKE %s", (num))
            new_count = int(c.fetchone()[0])
            new_count += 1
            c.execute("UPDATE "
                      + current_table_name
                      + " SET count='%s' WHERE number LIKE %s",
                      (new_count, num))
        db.commit()
    except UnicodeEncodeError as e:
        print("Error Skipping: " + str(e))
        update_tweet_data(3)
        pass

def create_table(time, is_complete, prv_table_name="", old_table_name=""):
    table_name = "tn_" + str(current_version) + "_" + str(time.year) + "_" + \
        str(time.month) + "_" + str(time.day) + "_" + str(time.hour)
    # tweets tweets_w_nums total_nums non_nums no_data_tweets
    global tweet_data
    global processing_latency_min
    global processing_latency_max
    global processing_latency_avg_sum
    global processing_latency_num
    global database_latency_min
    global database_latency_max
    global database_latency_avg_sum
    global database_latency_num
    print("Creating Table: " + table_name)
    c.execute("CREATE TABLE " + table_name + "(number CHAR(255), count INT)")
    logging.info("Creating Table: " + table_name)
    if prv_table_name != "" and old_table_name != "":
        print("Writing Tweet Data: "
              + str(tweet_data)
              + " for table :"
              + old_table_name)
        write_metadata(current_version,
                       old_table_name, is_complete,
                       tweets=tweet_data[0],
                       tweets_w_nums=tweet_data[1],
                       total_nums=tweet_data[2],
                       non_nums=tweet_data[3],
                       no_data_tweets=tweet_data[4])
        tweet_data = [0, 0, 0, 0, 0]
        database_latency_min = -1
        database_latency_max = 0
        database_latency_avg_sum = 0
        database_latency_num = 1
        processing_latency_min = -1
        processing_latency_max = 0
        processing_latency_avg_sum = 0
        processing_latency_num = 1
    if time.minute == 0:
        write_metadata(current_version, table_name, 1)
    else:
        write_metadata(current_version, table_name, 0)

    db.commit()
    return table_name

def write_metadata(version,
                   table_name,
                   is_complete,
                   tweets=-1,
                   tweets_w_nums=-1,
                   total_nums=-1,
                   non_nums=-1,
                   no_data_tweets=-1):
    # table name | complete | total tweets | total tweets with numbers
    # | total numbers | number of differnt numbers | total of non numbers
    # | total of no tweet data | processing_latency_min |
    # processing_latency_max
    # | processing_latency_avg | database_latency_min database_latency_max
    # | database_latency_avg
    # tn_3_2017_10_19_24
    # 000000000000000000000
    metadata_table_name = "tn_" + str(version) + "_metadata"
    dif_nums = c.execute("SELECT * FROM " + table_name)
    database_latency_avg = database_latency_avg_sum / database_latency_num
    processing_latency_avg = \
        processing_latency_avg_sum / processing_latency_num
    print("Writing Metadate for Table: " + table_name)
    logging.info("Writing Metadate for Table: " + table_name)
    try:
        c.execute("SELECT * FROM " + metadata_table_name)
    except pymysql.DatabaseError:
        print("Creating Metadata Table: " + metadata_table_name)
        logging.info("Creating Metadata Table: " + metadata_table_name)
        c.execute("CREATE TABLE "
                  + metadata_table_name
                  + "(version INT, table_name CHAR(21), "
                  "is_complete TINYINT(1), tweets INT, "
                  "tweets_w_nums INT, "
                  "total_nums INT, "
                  "dif_nums INT, "
                  "non_nums INT, "
                  "no_data_tweets INT, "
                  "processing_latency_min INT, "
                  "processing_latency_max INT, "
                  "processing_latency_avg INT, "
                  "database_latency_min INT, "
                  "database_latency_max INT, "
                  "database_latency_avg INT, "
                  "stream_speed_divider INT)")
        db.commit()

    if c.execute("select * from "
                 + metadata_table_name
                 + " where table_name like '"
                 + table_name + "'") == 0:
        print("Creating New Row")
        c.execute("INSERT INTO "
                  + metadata_table_name
                  + "(version, table_name, is_complete, tweets, tweets_w_nums,"
                  " total_nums, dif_nums, non_nums, no_data_tweets, "
                  "processing_latency_min, processing_latency_max, "
                  "processing_latency_avg, database_latency_min, "
                  "database_latency_max, database_latency_avg, "
                  "stream_speed_divider)"
                  " VALUES (%s, %s, %s, %s, %s, %s, %s, "
                  "%s, %s, %s, %s, %s, %s, %s, %s, %s)",
                  (version,
                    table_name,
                    is_complete,
                    tweets,
                    tweets_w_nums,
                    total_nums,
                    dif_nums,
                    non_nums,
                    no_data_tweets,
                    processing_latency_min,
                    processing_latency_max,
                    processing_latency_avg,
                    database_latency_min,
                    database_latency_max,
                    database_latency_avg,
                    stream_speed_divider))
    else:
        print("Updating Row")
        c.execute("UPDATE "
                  + metadata_table_name
                  + " SET version=%s, "
                  "table_name=%s, "
                  "is_complete=%s, "
                  "tweets=%s, "
                  "tweets_w_nums=%s, "
                  "total_nums=%s, "
                  "dif_nums=%s, "
                  "non_nums=%s, "
                  "no_data_tweets=%s, "
                  "processing_latency_min=%s, "
                  "processing_latency_max=%s, "
                  "processing_latency_avg=%s, "
                  "database_latency_min=%s, "
                  "database_latency_max=%s, "
                  "database_latency_avg=%s, "
                  "stream_speed_divider=%s "
                  "WHERE table_name=%s",
                  (version,
                    table_name,
                    is_complete,
                    tweets,
                    tweets_w_nums,
                    total_nums,
                    dif_nums,
                    non_nums,
                    no_data_tweets,
                    processing_latency_min,
                    processing_latency_max,
                    processing_latency_avg,
                    database_latency_min,
                    database_latency_max,
                    database_latency_avg,
                    stream_speed_divider,
                    table_name))
    # c.execute("INSERT INTO " + metadata_table_name

def main_loop():
    current_time = datetime.datetime.utcnow()
    table_start_hour = current_time.hour
    try:
        if current_time.minute == 0:
            current_table_name = create_table(current_time, 1)
            is_complete = 1
        else:
            current_table_name = create_table(current_time, 0)
            is_complete = 0
    except pymysql.DatabaseError:
        print("table already created")
        logging.info("Table Already Created")
        current_table_name = "tn_" \
            + str(current_version) \
            + "_" + str(current_time.year) \
            + "_" + str(current_time.month) \
            + "_" + str(current_time.day) \
            + "_" + str(current_time.hour)
        write_metadata(current_version, current_table_name, 0)
        is_complete = 0
        pass
    try:
        queue_len_warning = False
        while True:
            current_time = datetime.datetime.utcnow()
            if current_time.hour != table_start_hour:
                print("creating new table")
                try:
                    old_table_name = current_table_name
                    current_table_name = \
                        create_table(current_time,
                                     is_complete,
                                     prv_table_name=old_table_name,
                                     old_table_name=old_table_name)
                    is_complete = 1
                except pymysql.DatabaseError as error:
                    print("database already created(in loop)")
                    print("error: " + str(error))
                    logging.info("Table Already Created(loop)")
                    current_table_name = "tn_" \
                        + str(current_version) \
                        + "_" + str(current_time.year) \
                        + "_" + str(current_time.month) \
                        + "_" + str(current_time.day) \
                        + "_" \
                        + str(current_time.hour)
                table_start_hour = current_time.hour
            if(len(queue) > 0):
                print("Number: " + queue[0])
                d3 = datetime.datetime.now()
                num = str(queue[0])
                add_to_db(num, current_table_name)
                del queue[0]
                d4 = datetime.datetime.now()
                database_latency((d4 - d3).microseconds)
                if len(queue) > 100:
                    if queue_len_warning is False:
                        print("Queue Length Above 100: " + str(len(queue)))
                        logging.warning("Queue Length Above 100: "
                                        + str(len(queue)))
                        queue_len_warning = True
                if len(queue) < 90 and queue_len_warning is True:
                    print("Queue Length Under 90")
                    logging.warning("Queue Length Under 90")
                    queue_len_warning = False
    except KeyboardInterrupt:
        print("closing database")
        db.close()

db = pymysql.connect("localhost", "root", "Climate123", "test")
c = db.cursor()
# Create new threads
thread1 = myThread(1, "Thread_main_loop", 1)
thread2 = myThread(2, "Thread_streaming", 2)
# Start new Threads
thread1.start()
thread2.start()


print("dict.py")

alien_0 = {'color': 'green', 'points': 5}
print(alien_0['color'])
print(alien_0['points'])

new_points = alien_0['points']
print(f"You just earned {new_points} points\n")

alien_0['x_position'] = 0
alien_0['y_position'] = 25
print(alien_0)

alien_1 = {}
print(alien_1)
alien_1['color'] = 'green'
alien_1['points'] = 5
print(alien_1)

alien_1['color'] = 'yellow'
print(alien_1)

alien_1 = {'x_position': 0, 'y_position': 25, 'speed': 'medium'}
if alien_1['speed'] == 'slow':
    x_increment = 1
elif alien_1['speed'] == 'medium':
    x_increment = 2
else:
    x_increment = 3
alien_1['x_position'] = alien_1['x_position'] + x_increment
print(f"New position: {alien_1['x_position']}")

alien_2 = {'color': 'red', 'points': 0}
print(alien_2)
del alien_2['color']
print(alien_2)

favorite_languages = {
    'jen': 'python',
    'sarah': 'c',
    'edward': 'ruby',
    'phil': 'python',
}
language = favorite_languages['sarah'].title()
print(f"Sarah's favorite language is {language}.\n")
print(f"Jen's favorite language is {favorite_languages['jen'].title()}")

person = {
    'first name': 'mary',
    'last name': 'flynn',
    'age': 54,
    'city': 'drumshanbo',
}

print(person)

favorite_number = {
    'kevin': 7,
    'april': 1,
    'meg': 4,
    'sean': 89,
}

print(favorite_number)
print("\n")

user_0 = {
    'username': 'efermi',
    'first': 'enrico',
    'last': 'fermi',
}
# for key.value in user_0.items():
#     print(f"Key: {key}")
#     print(f"Value: {value}")
#     print("\n")


# -*- coding: utf-8 -*-
"""
Created on Sun Oct 15 23:15:55 2017

@author: HSIN
"""

# import matplotlib
# matplotlib.use('Agg')
# import matplotlib.pyplot as plt

import os
import sys
import csv
import time
import numpy as np
import pickle
from keras.models import Sequential
from keras.layers import Input, Dense, Dropout, Flatten, Activation, BatchNormalization
from keras.layers import LSTM, GRU, SimpleRNN
from keras.layers import Conv1D, Conv2D
from keras.layers import MaxPooling1D, MaxPooling2D
from keras.layers import RepeatVector, TimeDistributed
from keras.layers import Bidirectional
from keras.layers import Embedding
from keras.layers.advanced_activations import PReLU
from keras.models import Model
from keras.models import load_model
from keras.optimizers import SGD, Adadelta, RMSprop
from keras.callbacks import EarlyStopping, ModelCheckpoint

stime = time.time()

max_sent_len = 800

char_list = []
phone_48_list = []
phone_39_list = []

data_path = sys.argv[1]
output_path = sys.argv[2]

print('Get 48-Char Map', time.time()-stime)
f = open(os.path.join(data_path, '48phone_char.map'), 'r')
for line in f:
    data = line.replace('\n', '')
    data = data.split('\t')
    phone_48_list.append(data[0])
    char_list.append(data[2])
    phone_39_list.append(0)
f.close()

print('Get 48-39 Map', time.time()-stime)
f = open(os.path.join(data_path, 'phones/48_39.map'), 'r')
for line in f:
    data = line.replace('\n', '')
    data = data.split('\t')
    phone_39_list[phone_48_list.index(data[0])] = data[1]
f.close()

phone_39_set_list = sorted(list(set(phone_39_list)))

"""
train_ids = []
train_mfccs = []
train_fbanks = []
train_labels = []

print('Get Train MFCC Feature', time.time()-stime)
f = open(os.path.join(data_path, 'mfcc/train.ark'), 'r')
ids = []
count = 0
for line in f:
    count += 1
    if count  % 100000 == 0:
        print('Get Train MFCC', count)

    data = line.replace('\n', '')
    data = data.split(' ')
    ids.append(data[0])
    train_mfccs.append(([float(x) for x in data[1:]]))
f.close()

sorted_zip_list = list(sorted(zip(ids,train_mfccs)))
sorted_zip_list = list(zip(*sorted_zip_list))
ids = np.asarray(sorted_zip_list[0])
train_mfccs = np.asarray(sorted_zip_list[1])

print('Get Train FBANK Feature', time.time()-stime)
f = open(os.path.join(data_path, 'fbank/train.ark'), 'r')
ids = []
count = 0
for line in f:
    count += 1
    if count  % 100000 == 0:
        print('Get Train FBANK', count)

    data = line.replace('\n', '')
    data = data.split(' ')
    ids.append(data[0])
    train_fbanks.append(([float(x) for x in data[1:]]))
f.close()

sorted_zip_list = list(sorted(zip(ids,train_fbanks)))
sorted_zip_list = list(zip(*sorted_zip_list))
ids = np.asarray(sorted_zip_list[0])
train_fbanks = np.asarray(sorted_zip_list[1])

train_features = np.concatenate((train_mfccs,train_fbanks), axis=1)

print('Get Train Label', time.time()-stime)
f = open(os.path.join(data_path, 'label/train.lab'), 'r')
ids = []
count = 0
for line in f:
    count += 1
    if count  % 100000 == 0:
        print('Get Train Label', count)

    data = line.replace('\n', '')
    data = data.split(',')
    ids.append(data[0])
    lab = phone_39_list[phone_48_list.index(data[1])]
    train_labels.append(lab)
f.close()

sorted_zip_list = list(sorted(zip(ids,train_labels)))
sorted_zip_list = list(zip(*sorted_zip_list))
ids = np.asarray(sorted_zip_list[0])
train_labels = np.asarray(sorted_zip_list[1])

train_ids = ids
speaker_ids = list(set(ids))

for i in range(len(speaker_ids)):
    sids = speaker_ids[i]
    sids_split = sids.split('_')
    speaker_ids[i] = '_'.join(sids_split[:2])

speaker_ids = list(set(speaker_ids))

X = []
y = []

for i in range(len(speaker_ids)):
    if i % 1000 == 0:
        print('Train Padding Set', i)
    to_append = []
    for j in range(max_sent_len):
        to_append.append(np.zeros(train_features.shape[1]))
    X.append(to_append)

    to_append = []
    for j in range(max_sent_len):
        to_append.append(np.zeros(39))
    y.append(to_append)

id_max_record = np.zeros(len(speaker_ids))

for i in range(len(train_ids)):
    if i % 10000 == 0:
        print('Train ID Transform Step1', i)
        print(train_labels[i])
    tids = train_ids[i]
    tids_split = tids.split('_')
    speaker_id = '_'.join(tids_split[:2])
    context_id = int(tids_split[2])

    if context_id > id_max_record[speaker_ids.index(speaker_id)]:
        id_max_record[speaker_ids.index(speaker_id)] = context_id

for i in range(len(train_ids)):
    if i % 10000 == 0:
        print('Train ID Transform Step2', i)
        print(train_labels[i])
    tids = train_ids[i]
    tids_split = tids.split('_')
    speaker_id = '_'.join(tids_split[:2])
    context_id = int(tids_split[2])

    pad_time = 0
    while(1):
        if context_id-1 + pad_time*id_max_record[speaker_ids.index(speaker_id)] >= max_sent_len:
            break
        X[speaker_ids.index(speaker_id)][int(context_id-1 + pad_time*id_max_record[speaker_ids.index(speaker_id)])]  = train_features[i]
        pad_time += 1

    pad_time = 0
    while(1):
        if context_id-1 + pad_time*id_max_record[speaker_ids.index(speaker_id)] >= max_sent_len:
            break
        y[speaker_ids.index(speaker_id)][int(context_id-1 + pad_time*id_max_record[speaker_ids.index(speaker_id)])][phone_39_set_list.index(train_labels[i])] = 1
        pad_time += 1

print('Dump X', time.time()-stime)
with open('X', 'wb') as fp:
    pickle.dump(X, fp)

print('Dump y', time.time()-stime)
with open('y', 'wb') as fp:
    pickle.dump(y, fp)
"""

"""
print('load X', time.time()-stime)
with open ('X', 'rb') as fp:
    X = pickle.load(fp)

print('load y', time.time()-stime)
with open ('y', 'rb') as fp:
    y = pickle.load(fp)

X = np.asarray(X)
y = np.asarray(y)

print('X_shape', X.shape, time.time()-stime)
print('y_shape', y.shape, time.time()-stime)
"""

def build_RNN_model(max_sent_len, word_size, output_size):

    drop_out_ratio = 0.2

    model = Sequential()

    model.add(SimpleRNN(1024,
                        input_shape = (max_sent_len, word_size),
                        dropout = drop_out_ratio,
                        return_sequences=True))

    model.add(BatchNormalization())
    model.add(Dropout(drop_out_ratio))

    model.add(TimeDistributed(Dense(output_size, activation='softmax')))

    model.compile(loss='categorical_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'],
                  sample_weight_mode='temporal')

    return model

"""

np.random.seed(seed = 174646)

train_valid_ratio = 0.9
indices = np.random.permutation(X.shape[0])
train_idx, valid_idx = indices[:int(X.shape[0] * train_valid_ratio)], indices[int(X.shape[0] * train_valid_ratio):]
X_train, X_valid = X[train_idx,:], X[valid_idx,:]
y_train, y_valid = y[train_idx,:], y[valid_idx,:]

epochs = 250
patience = 10
batch_size = 128

model = build_RNN_model(X.shape[1] , X.shape[2], y.shape[2])
model.summary()

earlystopping = EarlyStopping(monitor='val_loss', patience = patience, verbose=1, mode='min')
checkpoint = ModelCheckpoint('hw1_rnn_model.hdf5',
                             verbose=1,
                             save_best_only=True,
                             save_weights_only=True,
                             monitor='val_loss',
                             mode='min')

history = model.fit(X_train, y_train,
                 epochs = epochs,
                 batch_size = batch_size,
                 validation_data = (X_valid, y_valid),
                 callbacks=[earlystopping,checkpoint])

del model

loss_list = list(history.history['loss'])
val_loss_list = list(history.history['val_loss'])

acc_list = list(history.history['acc'])
val_acc_list = list(history.history['val_acc'])

history_list = [loss_list, val_loss_list, acc_list, val_acc_list]

with open('rnn_history','wb') as fp:
    pickle.dump(history_list, fp)

with open('rnn_history','rb') as fp:
    history_list = pickle.load(fp)

plt.plot(history_list[0])
plt.plot(history_list[1])
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'valid'], loc='upper left')
plt.savefig('rnn_model_loss.png')
plt.clf()

plt.plot(history_list[2])
plt.plot(history_list[3])
plt.title('model acc')
plt.ylabel('acc')
plt.xlabel('epoch')
plt.legend(['train', 'valid'], loc='upper left')
plt.savefig('rnn_model_acc.png')
plt.clf()
"""

test_ids = []
test_mfccs = []
test_fbanks = []

print('Get Test MFCC Feature', time.time()-stime)
f = open(os.path.join(data_path, 'mfcc/test.ark'), 'r')
ids = []
count = 0
for line in f:
    count += 1
    if count  % 100000 == 0:
        print('Get Test MFCC', count)

    data = line.replace('\n', '')
    data = data.split(' ')
    ids.append(data[0])
    test_mfccs.append(([float(x) for x in data[1:]]))
f.close()

sorted_zip_list = list(sorted(zip(ids,test_mfccs)))
sorted_zip_list = list(zip(*sorted_zip_list))
ids = np.asarray(sorted_zip_list[0])
test_mfccs = np.asarray(sorted_zip_list[1])

print('Get Test FBANK Feature', time.time()-stime)
f = open(os.path.join(data_path, 'fbank/test.ark'), 'r')
ids = []
count = 0
for line in f:
    count += 1
    if count  % 100000 == 0:
        print('Get Test FBANK', count)

    data = line.replace('\n', '')
    data = data.split(' ')
    ids.append(data[0])
    test_fbanks.append(([float(x) for x in data[1:]]))
f.close()

sorted_zip_list = list(sorted(zip(ids,test_fbanks)))
sorted_zip_list = list(zip(*sorted_zip_list))
ids = np.asarray(sorted_zip_list[0])
test_fbanks = np.asarray(sorted_zip_list[1])

test_features = np.concatenate((test_mfccs,test_fbanks), axis=1)

test_ids = ids
test_speaker_ids = list(set(ids))

for i in range(len(test_speaker_ids)):
    sids = test_speaker_ids[i]
    sids_split = sids.split('_')
    test_speaker_ids[i] = '_'.join(sids_split[:2])

test_speaker_ids = list(set(test_speaker_ids))

X_test = []

for i in range(len(test_speaker_ids)):
    if i % 1000 == 0:
        print('Test Padding Set', i)
    to_append = []
    for j in range(max_sent_len):
        to_append.append(np.zeros(test_features.shape[1]))
    X_test.append(to_append)

test_id_max_record = np.zeros(len(test_speaker_ids))

for i in range(len(test_ids)):
    if i % 10000 == 0:
        print('Test ID Transform Step1', i)
    tids = test_ids[i]
    tids_split = tids.split('_')
    test_speaker_id = '_'.join(tids_split[:2])
    context_id = int(tids_split[2])

    if context_id > test_id_max_record[test_speaker_ids.index(test_speaker_id)]:
        test_id_max_record[test_speaker_ids.index(test_speaker_id)] = context_id

for i in range(len(test_ids)):
    if i % 10000 == 0:
        print('Test ID Transform Step2', i)
    tids = test_ids[i]
    tids_split = tids.split('_')
    test_speaker_id = '_'.join(tids_split[:2])
    context_id = int(tids_split[2])

    pad_time = 0
    while(1):
        if context_id-1 + pad_time*test_id_max_record[test_speaker_ids.index(test_speaker_id)] >= max_sent_len:
            break
        X_test[test_speaker_ids.index(test_speaker_id)][int(context_id-1 + pad_time*test_id_max_record[test_speaker_ids.index(test_speaker_id)])]  = test_features[i]
        pad_time += 1

X_test = np.asarray(X_test)

model = build_RNN_model(X_test.shape[1] , X_test.shape[2], 39)
model.load_weights('hw1_rnn_model.hdf5')

batch_size = 128
res = model.predict(X_test, verbose = 1, batch_size = batch_size)
res = np.argmax(res, axis = 2)
res = res.astype('<U5')
for i in range(res.shape[0]):
    for j in range(res.shape[1]):
        res[i][j]=phone_39_set_list[int(res[i][j])]

smooth_range = 5

for i in range(res.shape[0]):
    for j in range(res.shape[1]):
        if j == 0:
            continue

        else:

            for k in range(smooth_range):
                if k == 0:
                    continue
                else:
                    if j < res.shape[1] - k:
                        if res[i][j-1] == res[i][j+k]:
                            for l in range(k):
                                res[i][j+l] = res[i][j-1]

res_signed = np.ones(res.shape)
res_signed = res_signed.astype('<U20')

for i in range(res.shape[0]):
    for j in range(res.shape[1]):
        if res[i][j] == 'sil':
            res_signed[i][j] = 'to_be_deleted'
        else:
            break

    for j in range(res.shape[1]):
        if res[i][res.shape[1]-1-j] == 'sil':
            res_signed[i][res.shape[1]-1-j] = 'to_be_deleted'
        else:
            break

    for j in range(res.shape[1]):
        if j == 0:
            continue
        elif res[i][j] == res[i][j-1]:
            res_signed[i][j] = 'to_be_deleted'

    for j in range(res.shape[1]):
        if j > test_id_max_record[i]-1:
            res_signed[i][j] = 'to_be_deleted'

result = []
for i in range(res.shape[0]):
    seq = []
    for j in range(res.shape[1]):
        if res_signed[i][j] == 'to_be_deleted':
            continue
        else:
            seq.append(res[i][j])

    result.append(seq)

char_result = []
for i in range(len(result)):
    data = []
    seq = ''
    for j in range(len(result[i])):
        seq = seq + char_list[phone_48_list.index(result[i][j])]

    data.append(test_speaker_ids[i])
    data.append(seq)

    char_result.append(data)

char_result = sorted(char_result)

def most_common(lst):
    return max(set(lst), key=lst.count)

ids = []
seqs = []
seq_s = []
seq_e = []
for data in char_result:
    ids.append(data[0])
    seqs.append(data[1])
    seq_s.append(data[1][0])
    seq_e.append(data[1][-1])

most_common_s = most_common(seq_s)
most_common_e = most_common(seq_e)

if seq_s.count(most_common_s)/len(seq_s) > 0.5:
    print('remove Head', most_common_s)
    print(seq_s.count(most_common_s)/len(seq_s))

    for i in range(len(seqs)):
        if seqs[i][0] == most_common_s:
            seqs[i] = seqs[i][1:]

if seq_e.count(most_common_e)/len(seq_e) > 0.5:
    print('remove Tail', most_common_e)
    print(seq_e.count(most_common_e)/len(seq_e))

    for i in range(len(seqs)):
        if seqs[i][-1] == most_common_e:
            seqs[i] = seqs[i][:-1]

revised_result = []
for i in range(len(seqs)):
    data = []
    data.append(ids[i])
    data.append(seqs[i])

    revised_result.append(data)

revised_result = sorted(revised_result)

revised_result = [['id','phone_sequence']] + revised_result

f = open(output_path, 'w')
w = csv.writer(f)
w.writerows(revised_result)
f.close()

print('Time Taken:', time.time()-stime)


# othello.py
# By Mithil Hari
# Contains a class representing the GameState of an
# Othello Game. Also contains 3 Class Exceptions,
# that are relevant to the game rules of Othello.

NONE = 0
BLACK = 1
WHITE = 2

class InvalidMoveError(Exception):
    '''Raised whenever an invalid move is made'''
    pass

class NoAvailableMovesError(Exception):
    '''Raised whenever a player has no available moves to make'''
    pass

class GameOverError(Exception):
    '''
    Raised whenever an attempt is made to make a move after the game is
    already over
    '''
    pass

class GameState:
    def __init__(self, dimensions: [int], first_player: int, topleft_disc: int, win_rule: str):
        self._rows, self._columns = dimensions
        self._turn = first_player
        self._topleft_disc = topleft_disc
        # win_rule is a parameter that determines how the game is won
        # It's values are either '<' or '>'
        self._win_rule = win_rule

        self._board = self._new_game_board()
        self._arrange_center_cells()
        self._black_counter = 2
        self._white_counter = 2
        # Stores the number of times a turn has been passed
        self._turns_passed = 0

    def get_board(self) -> [[int]]:
        '''Returns the game board'''
        return self._board

    def get_num_rows(self) -> int:
        ''' Returns the number of rows of a board'''
        return self._rows

    def get_num_cols(self) -> int:
        '''Returns the number of columns of a board'''
        return self._columns

    def turn(self) -> int:
        '''Returns whose turn it is'''
        return self._turn

    def opposite_turn(self) -> None:
        '''Updates the turn to the next player'''
        if self._turn == WHITE:
            self._turn = BLACK
        else:
            self._turn = WHITE

    def winner(self) -> int:
        '''
        Determines the winning player in the given GameState, if any.
        '''
        winner = -1

        if self._win_rule == '>':
            if self._black_counter > self._white_counter:
                winner = BLACK
            elif self._white_counter > self._black_counter:
                winner = WHITE
            else:
                winner = NONE

        else:
            if self._black_counter < self._white_counter:
                winner = BLACK
            elif self._white_counter < self._black_counter:
                winner = WHITE
            else:
                winner = NONE

        return winner

    def get_num_black_discs(self) -> int:
        '''Returns the number of black discs on a board'''
        return self._black_counter

    def get_num_white_discs(self) -> int:
        '''Returns the number of white discs on a board'''
        return self._white_counter

    def disc_at(self, row_number: int, column_number: int) -> int:
        '''Returns the disc found at the given indices, if any.'''
        if self._is_valid_row_number(row_number) and \
           self._is_valid_column_number(column_number) and \
           self._board[row_number][column_number] != NONE:
            return self._board[row_number][column_number]

        # Return -1 if the disc found is not BLACK or WHITE

        return -1

    def require_game_not_over(self) -> None:
        '''
        Raises a GameOverError if the given game state represents a situation
        where the game is over.
        '''
        if self._board_is_full() or self._no_moves_left():
            raise GameOverError()

    def require_available_moves(self) -> None:
        '''
        Given a GameState, checks if there are any legal moves availabe to make on the board.
        If there are no legal moves available to make, the turn is passed onto the next player.
        '''
        available_moves = []

        for row in range(self._rows):
            for col in range(self._columns):
                board_cell = self._board[row][col]
                if board_cell == NONE:
                    available_moves.extend(
                        self._find_discs(
                            self._get_interesting_directions(row, col),
                            row, col))

        if len(available_moves) == 0:
            self._turns_passed+= 1
            raise NoAvailableMovesError()

    def move(self, row_number: int, column_number: int) -> None:
        '''
        Given a row and column to place a disc in, attempts to
        make the move it is valid and if the game is not over yet.
        Raises an InvalidMoveError if it is not.
        '''
        self.require_game_not_over()
        self._require_valid_row_number(row_number)
        self._require_valid_column_number(column_number)
        self._require_empty_cell(row_number, column_number)

        interesting_directions = self._get_interesting_directions(
            row_number, column_number)
        discs_to_flip = self._find_discs(interesting_directions,
                                         row_number, column_number)

        # If no discs can be flipped this means that
        # the move is invalid.

        if len(discs_to_flip) >  0:

            # Adds a disc for the current player
            # in the location of the desired move.

            self._board[row_number][column_number] = self._turn

            # Flips the color of the discs affected
            # by the move.

            self._flip_discs(discs_to_flip)
            self._reset_number_of_turns_passed()
            self.opposite_turn()
            self._calc_num_discs()

        else:
            raise InvalidMoveError()

    def _calc_num_discs(self) -> None:
        '''Calculates the number of white and black discs on a board'''
        black_counter = 0
        white_counter = 0

        for row in range(self._rows):
            for col in range(self._columns):
                if self._board[row][col] != NONE:
                    if self._board[row][col] == BLACK:
                        black_counter+= 1
                    else:
                        white_counter+= 1

        self._black_counter = black_counter
        self._white_counter = white_counter

    def _reset_number_of_turns_passed(self) -> None:
        '''
        Resets the number of turns passed to zero.
        '''
        self._turns_passed = 0

    def _flip_discs(self, discs_to_flip: [[int]]) -> None:
        '''
        Changes the color of the discs that can be flipped by the players move.
        '''
        for disc in discs_to_flip:
                row, col = disc
                self._board[row][col] = self._turn

    def _get_interesting_directions(self, initial_row: int, initial_column: int) -> [[int]]:
        '''
        An interesting direction, is one that contains an opponents disc in a cell
        adjacent to the current cell. Returns a list of such directions.
        '''
        interesting_directions = []
        all_directions = [[-1,0], [-1,1], [0,1], [1,1], [1,0], [1,-1], [0,-1], [-1,-1]]

        for direction in all_directions:
            rdelta, cdelta = direction
            adjacent_row = initial_row + rdelta
            adjacent_column = initial_column + cdelta

            # Get's the value of the disc at the adajcent
            # row and adjacent column.
            adjacent_cell = self.disc_at(adjacent_row, adjacent_column)

            # If the adjacent cell is equal to the opposite player, consider
            # this direction interesting
            if adjacent_cell != -1 and adjacent_cell != self._turn:
                interesting_directions.append(direction)

        return interesting_directions

    def _find_discs(self, directions: [[int]], row_number: int, column_number: int) -> [[int]]:
        '''
        Given the initial disc position and a list of the directions in which
        the other player was found, finds all discs that can be flipped
        by a legal move.
        '''
        discs_to_flip = []

        for direction in directions:
            rdelta, cdelta = direction
            next_row = row_number + rdelta
            next_column = column_number + cdelta
            next_disc = self.disc_at(next_row, next_column)
            potential_discs_to_flip = []

            end_of_sequence = False

            # Searches for discs of the opposite color
            # until a disc of the same color is found
            # or if the end of the board is reached
            # in that direction.
            # The end of the sequence has been reached if
            # the next disc looked at is actually an empty
            # cell or if it is not the opponents disc.

            while not(end_of_sequence) and next_disc != -1:

                if next_disc != self._turn:
                    potential_discs_to_flip.append([next_row, next_column])
                else:
                    end_of_sequence = True

                last_disc = self.disc_at(next_row, next_column)
                next_row += rdelta
                next_column += cdelta
                next_disc = self.disc_at(next_row, next_column)

            # Only stores the discs found, if the last disc
            # color is equal to the current player.
            # If the move is = B and the sequence found
            # is W->W->(Empty/Off the board), no discs
            # will be flipped. However if the sequence
            # is something like B->W->W->B then the discs found
            # in the sequence will be flipped.

            if last_disc == self._turn:
                discs_to_flip.extend(potential_discs_to_flip)

        return discs_to_flip

    def _new_game_board(self) -> [[int]]:
        '''
        Creates a new game board. Initially, a game board has the size
        self._rows x self._columns and is comprised of integers with the
        value NONE everywhere except the center of the board. The center
        of the board contains four discs, two white and two black, that
        are arranged according to the specified Othello rule.
        '''
        board = []

        for row in range(self._rows):
            board.append([])
            for col in range(self._columns):
                board[-1].append(NONE)

        return board

    def _arrange_center_cells(self) -> None:
        '''
        Given a game board adds a disc, BLACK or WHITE, to each of
        the four center cells according to the specified arrangement.
        '''
        toprow = int(self._rows/2 - 1)
        botrow = int(self._rows/2)
        leftcol = int(self._columns/2 - 1)
        rightcol = int(self._columns/2)

        if self._topleft_disc != NONE:
            if self._topleft_disc == BLACK:
                self._board[toprow][leftcol] = BLACK
                self._board[toprow][rightcol] = WHITE
                self._board[botrow][leftcol] = WHITE
                self._board[botrow][rightcol] = BLACK
            else:
                self._board[toprow][leftcol] = WHITE
                self._board[toprow][rightcol] = BLACK
                self._board[botrow][leftcol] = BLACK
                self._board[botrow][rightcol] = WHITE

    def _board_is_full(self) -> bool:
        '''Returns True if the board has no empty cells.'''
        for row in self._board:
            if NONE in row:
                return False

        return True

    def _no_moves_left(self) -> bool:
        '''
        Returns True if both players passed their turn, meaning that
        there are no moves left.
        '''
        return self._turns_passed == 2

    def _require_valid_row_number(self, row_number: int) -> None:
        '''Raises an InvalidMoveError if its parameter is not a valid row number'''
        if not(self._is_valid_row_number(row_number)):
            raise InvalidMoveError()

    def _require_valid_column_number(self, column_number: int) -> None:
        '''Raises an InvalidMoveError if its parameter is not a valid column number'''
        if not(self._is_valid_column_number(column_number)):
            raise InvalidMoveError()

    def _require_empty_cell(self, row_number: int, column_number: int) -> None:
        '''Raises an InvalidMoveError if its parameter is not an empty cell in the board'''
        if self._board[row_number][column_number] != NONE:
            raise InvalidMoveError()

    def _is_valid_row_number(self, row_number: int) -> bool:
        '''Returns True if the given row number is valid; returns False otherwise'''
        return 0 <= row_number < self._rows

    def _is_valid_column_number(self, column_number: int) -> bool:
        '''Returns True if the given column number is valid; returns False otherwise'''
        return 0 <= column_number < self._columns


# Accepted on leetcode(39)
# Time complexity : Exponential
# checking through each possible path and capturing the path whenever the target is zero
class Solution:
    def combinationSum(self, candidates, target):
        if len(candidates) == 0: return []  # Edge case
        result = []
        self.helper(result, candidates, [], target, 0)  # call helper
        return result

    def helper(self, result, candidates, path, target, index):
        if target == 0:  # if target is zero add path
            result.append(path[:])
            # range starts from index(to make sure same path is not traversed twice)
        for i in range(index, len(candidates)):
            if candidates[i] <= target:  # if the candidate 'i' is less than target then continue.
                path.append(candidates[i])
                self.helper(result, candidates, path, target - candidates[i], i)
                path.pop()

sol = Solution()
print(sol.combinationSum([1,2,3,5,7],8))


#program to illustrate module

import math

def Main():
    num = float(input("Enter the number: "))
    #fabs is used to get absolute value of a decimal number
    num = math.fabs(num)
    print(num)

if __name__ == "__main__":
    Main()


# <markdowncell>

# # Convolutional Neural Network Workshop
#
# The aim of this workshop is to demonstrate the basics of using [TensorFlow](https://www.tensorflow.org/) for
# convolutional neural networks.
#
# TensorFlow is an open source system developed by Google Brain for working with computation graphs. It supports
# automatic differentiation, making it suitable for implementing backpropagation.
#
# Optimized implementation of convolutional network is not trivial, especially on GPU. Therefore, in your projects
# you are much more likely to use frameworks like TensorFlow than to code from scratch.
#
# This notebook will cover the basics, providing references to additional resources where needed. You are welcome
# to use many excellent tutorials on the web, including the [one from Google](https://www.tensorflow.org/get_started/get_started).

# Overview:
# * [Preparing data](#Preparing-data)
# * [Defining and examining a simple network](#Defining-and-examining-a-simple-network)
# * [Adding batch normalization](#Adding-batch-normalization)
# * [Training a more complex network](#Training-a-more-complex-network)
# * [Saving and loading models](#Saving-and-loading-models)
# * [Transfer learning](#Transfer-learning)

# <markdowncell>

# **Recommended TensorFlow version is 1.9.0.** If you are working on a machine with GPU, it is recommended that you
# **install GPU version of TensorFlow** (`tensorflow-gpu` Python package). You will also need `matplotlib`.
#
# We begin by importing all modules that we will need, including TensorFlow. This also verifies TensorFlow installation.

# <codecell>

import os
import tensorflow as tf
import numpy as np
from matplotlib import pyplot as plt
get_ipython().run_line_magic('matplotlib', 'inline')
from utils import load_cifar10, visualize_grid

# <markdowncell>

# ## Preparing data
#
# We will use CIFAR-10 dataset. Please download it from [here](https://www.cs.toronto.edu/~kriz/cifar.html)
# (alternatively from [here](https://1drv.ms/u/s!ApgLrbSPQF9XirQ0fEqljE5_IT0q6g)
# or [here](https://1drv.ms/u/s!ApgLrbSPQF9XirQ16VxWmz_x0bRwMg)), and make sure to specify correct location
# in `cifar10_dir` below. We load the dataset by calling a utility function.

# <codecell>

cifar10_dir = 'cifar-10-batches-py'
x_dev, y_dev, x_test, y_test = load_cifar10(cifar10_dir)

# <markdowncell>

# The dataset is divided into part that can be used for training and validation (which we will call
# *development*), and test part.
#
# There are 50,000 dev images, and 10,000 test images. test images. All images are 32 x 32 pixels, and
# have 3 channels. Each image is assigned to one of 10 classes.

# <codecell>

print('Dev data shape: ', x_dev.shape)
print('Dev labels shape: ', y_dev.shape)
print('Test data shape: ', x_test.shape)
print('Test labels shape: ', y_test.shape)
print('Class labels: ', np.unique(np.concatenate([y_dev, y_test])))

# <markdowncell>

# We define some useful constants to be used throughout

# <codecell>

num_dev = x_dev.shape[0]
num_test = x_test.shape[0]
num_classes = len(np.unique(np.concatenate([y_dev, y_test])))

# <markdowncell>

# ### Training/validation split
#
# From dev data we take 10% for validation, and use the rest for training.
# To make sure that train and dev set have the same distribution, we randomize
# dev set first.

# <codecell>

num_val = num_dev // 10
num_train = num_dev - num_val

perm = np.random.permutation(num_dev)
x_dev = x_dev[perm]
y_dev = y_dev[perm]

x_train = x_dev[:num_train]
y_train = y_dev[:num_train]
x_val = x_dev[num_train:]
y_val = y_dev[num_train:]

# <markdowncell>

# ### Data normalization
#
# We normalize all data by subtracting mean image from each sample.
# Note that the mean image is computed from training set only.

# <codecell>

mean_image = np.mean(x_train, axis=0)
x_train -= mean_image
x_val -= mean_image
x_test -= mean_image

# <markdowncell>

# ### Visualizing training examples

# Let's look at `sample_size` random examples from each class.

# <codecell>

sample_size = 10
sample = x_train[[i for c in range(num_classes)
                  for i in np.random.permutation(np.flatnonzero(y_train == c))[:sample_size]]]
sample += mean_image
plt.figure(figsize = (12, 12))
plt.imshow(visualize_grid(sample))
plt.axis("off")
plt.show()

# <markdowncell>

# Now that we know what the classes represent, we can create an array of human-readable names.

# <codecell>

class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

# <markdowncell>

# ## Defining and examining a simple network
#
# We define a simple network consisting of basic layers:
# * convolutional layer,
# * max-pooling layer,
# * fully connected layer, and
# * ReLU activation function.
#
# TensorFlow supports many other layer types and activations. See https://www.tensorflow.org/api_guides/python/nn for official API documentation.

# <markdowncell>

# The following line clears any network that might already exist in memory.

# <codecell>

tf.reset_default_graph()

# <markdowncell>

# ### Create TensorBoard log file
#
# We will use TensorBoard to visualize various data about our network. TensorBoard parses log files (also called event files) generated by TensorFlow. We will be placing those files in a separate dir.

# <codecell>

log_dir = './logs/'

# <markdowncell>

# A new event file is created by instantiating a `tf.FileWriter` class.

# <codecell>

writer = tf.summary.FileWriter(os.path.join(log_dir, 'simple_net'))

# <markdowncell>

# ### Placeholders for data
# First we define placeholders for input data (input image and its label) using `tf.placeholder`.
# We will eventually bind these to actual numerical data values.
#
# We choose to represent input data as 4D tensors whose shape is N x H x W x C, where:
# * N is the number of examples in a batch (batch size)
# * H is the height of each image in pixels
# * W is the height of each image in pixels
# * C is the number of channels (usually 3: R, G, B)
#
# This is the right way to represent the data for spatial operations like convolution. For fully connected layers, however, all dimensions except batch size will be collapsed into one.
#
# In `tf.placeholder`, if a dimension has value `None`, it will be set automatically once actual data is provided.

# <codecell>

def setup_input():
    x = tf.placeholder(tf.float32, [None, 32, 32, 3], name='x')
    y = tf.placeholder(tf.int64, [None], name='y')
    is_training = tf.placeholder(tf.bool, name='is_training')
    return x, y, is_training

# <codecell>

x, y, is_training = setup_input()

# <markdowncell>

# ### Convolutional and pooling nodes
# Next we start defining the main "body" of the network.
# We start by adding a single convolutional layer with bias and ReLU activation.
#
# We use [tf.layers API](https://www.tensorflow.org/api_docs/python/tf/layers) to generate a whole layer by a single function call.
# It is also possible to create each parameter and operation node separately, and connect them together, but that quickly becomes
# cumbersome for bigger networks.
#
# TensorFlow also provides other high-level APIs, such as [TensorFlow-Slim](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/slim), which
# has become very popular, with a large number of [pretrained models](https://github.com/tensorflow/models/tree/master/research/slim) written in it.
# Fortunately, TF-Slim models can be quite easily combined with other TensorFlow APIs such as tf.layers.
#
# Convolutional layer is created by calling `tf.layers.conv2d`. Returned object is of type `tf.Tensor` and represents output activations of the layer.
#
# Bias is enabled by default, so it is not explicitly specified. `padding='SAME'` means that we allow padding of roughly half the kernel size
# (TensorFlow computes this value automatically), to avoid reduction in output size due to boundary effects. The other option is `padding='VALID'`, which means
# that padding is zero.

# <codecell>

conv1 = tf.layers.conv2d(inputs=x, filters=32, kernel_size=[7, 7], strides=2, padding='SAME', activation=tf.nn.relu, name='conv1')

# <markdowncell>

# Next we add a max-pooling node.

# <codecell>

pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2, padding='SAME', name='pool1')

# <markdowncell>

# ### View default graph in TensorBoard
#
# We can write graph data to event file we create above. A graph can be passed to `FileWriter` constructor as well, in which case it is written to file immediately after the file is created.

# <codecell>

writer.add_graph(tf.get_default_graph())

# <markdowncell>

# Now you should be able to run `tensorboard --logdir=./logs` from console (with your Python environment activated), and see the graph visualized in browser at `http://localhost:6006`.
#
# For more details please see official tutorial on [graph visualization](https://www.tensorflow.org/get_started/graph_viz).
#
# Note: graph visualization seems to work best in Google Chrome.

# <markdowncell>

# ### Examining static information
# Most information about the network which is static (i.e. independent of input data)
# can be seen from TensorBoard graph visualization -- node names, connectivity, tensor sizes,
# some layer parameters, but not tensor values.
#
# This data is also acessible from Python code, which is useful in some situations.
#
# If we already have handles on nodes, for example because we just created them, such as `conv1`
# and `pool1` above, we can query their properties.

# <codecell>

print(conv1.shape)
print(pool1.shape)

# <markdowncell>

# Note that shapes of activation tensors are computed automatically. Also, these tensors
# may have unknown dimensions which become known only when acutal input is presented.

# We may not have a handle on some nodes, for example internal parameters of `conv1` (kernel and bias).
# Those tensors are "hidden" inside the layer, because we used layers API. We can still access them
# using name lookup.

# <codecell>

conv1_kernel = tf.get_default_graph().get_tensor_by_name('conv1/kernel:0')
conv1_bias = tf.get_default_graph().get_tensor_by_name('conv1/bias:0')

# <markdowncell>

# The names can be found from TensorBoard visualization, or by listing all operations (nodes) in the network.

# <codecell>
tf.get_default_graph().get_operations()

# <markdowncell>

# The `conv1` prefix in both names (*name scope*) refers to the `name` parameter specified when creating
# the layer, and NOT the Python variable `conv1` that we assigned the result to. The `:0` suffix means that
# the tensor is the first (index 0) output of the node that produces it (`conv/kernel` and `conv1/bias`, respectively).
#
# We can get the shapes of kernel and bias as follows

# <codecell>

print(conv1_kernel.shape)
print(conv1_bias.shape)

# <markdowncell>

# Finally, we can also access internal parameters of operations (rather than their tensor outputs) using
# `get_operation_by_name` followed by `get_attr`.

# <codecell>

print(tf.get_default_graph().get_operation_by_name('conv1/Conv2D').get_attr('strides'))

# <markdowncell>

# ### Fully connected layers
# Next we append a fully connected layer with 1024 output neurons and ReLU activation.
# In order to determine the shape of its parameter tensor, we need to know the number of input neurons, which depends on the shape of the `relu1` activation tensor.

# <codecell>

fc1_input_count = int(pool1.shape[1] * pool1.shape[2] * pool1.shape[3])
fc1_output_count = 1024
print([fc1_input_count, fc1_output_count])

# <markdowncell>

# In order to append a fully connected layer, we need to flatten the spatial dimensions of `relu1`.

# <codecell>

pool1_flat = tf.reshape(pool1, [-1, fc1_input_count])

# <markdowncell>

# Now we are ready to add a fully connected layer.

# <codecell>

fc1 = tf.layers.dense(inputs=pool1_flat, units=fc1_output_count, activation=tf.nn.relu, name='fc1')

# <markdowncell>

# Finally, we add another fully connected layer with bias to output scores for 10 output classes. This layer has no nonlinearity following it, but it will be followed by a softmax function to convert scores to probabilities.

# <codecell>

fc2 = tf.layers.dense(inputs=fc1, units=num_classes, name='fc2')

# <markdowncell>

# ### Final classification
# We append a softmax layer to convert the scores coming from `fc2` into probabilities, as well as a "top-k" layer to get the three most probable guesses.

# <codecell>

prob = tf.nn.softmax(fc2)
(guess_prob, guess_class) = tf.nn.top_k(prob, k=3)

# <markdowncell>

# ### Visualizing parameters and activations
# TensorBoard supports visualizing tensors as images using `tf.summary.image` function.
# This function adds a subnetwork that computes images for a given tensor.

# <codecell>

def setup_image_summary(tensor, name):
    with tf.variable_scope(name):
        # Normalize to [0 1].
        x_min = tf.reduce_min(tensor)
        x_max = tf.reduce_max(tensor)
        normalized = (tensor - x_min) / (x_max - x_min)

        # Display random 3 slices.
        return tf.summary.image('tensor', normalized, max_outputs=3)

# <markdowncell>

# Using this we visualize some of the weights in `conv1_kernel`. Since each filter has 3
# input channels, it be visualized as an RGB image. Note that we need to transpose the
# tensor from (H, W, C, N) to (N, H, W, C) layout.

# <codecell>

conv1_kernel_summary = setup_image_summary(
    tf.transpose(conv1_kernel, [3, 0, 1, 2]),
    name='conv1_kernel_summary')

# <markdowncell>

# Similarly, we can visualize some of the activations in `conv1`. Use `setup_image_summary`
# in code below to visualize a subset of channels for example 0 in minibatch. Each channel
# should be visualized as a grayscale image. You can use [tf.slice](https://www.tensorflow.org/api_docs/python/tf/slice)
# to get example 0. If necessary, transpose into appropriate layout.

# <codecell>

# TODO

# <markdowncell>

# Summaries can also be *merged*. We add a special `image_summaries` node that outputs the union of our two image summaries.
# Evaluating this node causes both summaries to be computed.

# <codecell>

image_summaries = tf.summary.merge([conv1_kernel_summary, conv1_output_summary])

# <markdowncell>

# ### Update graph visualization
# We have added some new nodes, and we need to check if the new graph is OK.
# To update TensorBoard visualization, we just add a new graph to the event file.
# The visualizer will pick up the latest graph when its browser tab is refreshed.

# <codecell>

writer.add_graph(tf.get_default_graph())

# <markdowncell>

# ### Forward pass
# Next we run one CIFAR-10 frame through the network.

# <codecell>

def choose_random_image():
    index = np.random.randint(0, num_train)
    return index, x_train[[index]], y_train[[index]]

# <codecell>

random_index, random_image, random_label = choose_random_image()

# <markdowncell>

# A TensorFlow graph is executed by creating a `tf.Session` object and calling its `run` method.
# A session object encapsulates the control and state of the TensorFlow runtime.
# The `run` method requires a list of output tensors that should be computed, and a mapping of input tensors to actual data that should be used. For more information, see the TensorFlow [Getting started](https://www.tensorflow.org/get_started/get_started) guide.
#
# Optionally we can also specify a device context such as `/cpu:0` or `/gpu:0`. For documentation on this see [this TensorFlow guide](https://www.tensorflow.org/tutorials/using_gpu). The default device is a GPU if available, and a CPU otherwise, so we can skip the device specification from now on.
#
# Note: if GPU is explicitly specified, but not available, a Python exception is thrown; current graph is invalidated, and needs to be cleared and rebuilt.

# <codecell>

with tf.Session() as sess:
    with tf.device("/cpu:0") as dev: #"/cpu:0" or "/gpu:0"
        # Initialize weights.
        sess.run(tf.global_variables_initializer())

        # Map inputs to data.
        feed_dict = { x : random_image, y : random_label }

        # Set up variables we want to compute.
        variables = [guess_prob, guess_class, image_summaries]

        # Perform forward pass.
        guess_prob_value, guess_class_value, img_summ_value = sess.run(variables, feed_dict=feed_dict)

# <markdowncell>

# First let's see the image that was chosen, and networks predictions for it.

# <codecell>

def visualize_classification(image, guess_class, guess_prob):
    plt.imshow(image)
    plt.axis("off")
    plt.show()
    for i in range(3):
        ind = guess_class[0, i]
        prob = guess_prob[0, i]
        print("Class: {0}\tProbability: {1:0.0f}%".format(class_names[ind], prob * 100))
    print("Ground truth: {0}".format(class_names[random_label[0]]))

# <codecell>

visualize_classification((random_image[0] + mean_image) / 255.0, guess_class_value, guess_prob_value)

# <markdowncell>

# We write generated images to file. After running the next cell the images should be visible in TensorBoard.

# <codecell>

writer.add_summary(img_summ_value)

# <markdowncell>

# ### Loss and metric(s)
#
# We append more nodes to compute loss value, and the number of correctly predicted pixels.
# For loss we use `tf.sparse_softmax_cross_entropy_with_logits`. For other loss functions available
# out of the box in TensorFlow, see https://www.tensorflow.org/api_guides/python/nn#Losses and
# https://www.tensorflow.org/api_guides/python/nn#Classification.
# Of course, you can always build your own custom loss functions from simpler operations.

# <codecell>

def setup_metrics(y, y_out):
    # Define loss function.
    total_loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=y_out)
    mean_loss = tf.reduce_mean(total_loss)

    # Add top three predictions.
    prob = tf.nn.softmax(y_out)
    (guess_prob, guess_class) = tf.nn.top_k(prob, k=3)

    # Compute number of correct predictions.
    is_correct = tf.equal(tf.argmax(y_out, 1), y)
    accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))

    return mean_loss, accuracy, guess_prob, guess_class

# <markdowncell>

# We will be reusing this function later for other architectures.
# Now we create metrics for our current network.

# <codecell>

mean_loss, accuracy, guess_prob, guess_class = setup_metrics(y, fc2)

# <markdowncell>

# ### Visualizing loss and metric(s)
# We would like to use TensorBoard to visualize loss value and correct count.
# We add special nodes that generate those logs.

# <codecell>

def setup_scalar_summaries():
    mean_loss_summary = tf.summary.scalar('mean_loss', mean_loss)
    accuracy_summary = tf.summary.scalar('accuracy', accuracy)
    return tf.summary.merge([mean_loss_summary, accuracy_summary])

# <codecell>

scalar_summaries = setup_scalar_summaries()

# <markdowncell>

# ### Optimizer
#
# Finally, we define the optimization algorithm to be used for training. We use the Adam optimizer with learning rate 5e-4. For other choices see https://www.tensorflow.org/api_guides/python/train#Optimizers.
#
# Optimizer's `minimize` method essentially generates a network that performs backward pass based on the forward pass network that we defined, and passed to the optimizer via argument to `minimize`.
# The result of this method is a dummy node `train_step` which, when evaluated triggers execution of backward pass.

# <codecell>

def setup_optimizer(loss, learning_rate):
    optimizer = tf.train.AdamOptimizer(learning_rate)
    # Batch normalization in TensorFlow requires this extra dependency
    extra_update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
    with tf.control_dependencies(extra_update_ops):
        train_step = optimizer.minimize(loss)
    return train_step

# <markdowncell>

# We will be reusing this function for other architectures. Now we create optimizer for our current network.

# <codecell>

train_step = setup_optimizer(mean_loss, 5e-4)

# <markdowncell>

# ### Adding an optional backward pass
# Above we saw how to execute forward pass using `tf.Session.run`. Now we wrap that into a function
# (since we will be calling it in a loop to train the network). We also add an option to execute a
# backward pass by passing the extra argument `training`. That way we can use the same function for
# both training (forward + backward), and evaluation (forward only).

# <codecell>

def run_iteration(session, variables, x_data, y_data, training=None):
    if training != None:
        variables += [training]

    # Map inputs to data.
    feed_dict = {x : x_data, y : y_data, is_training : training != None}

    # Compute variable values, and perform training step if required.
    values = session.run(variables, feed_dict=feed_dict)

    # Return loss value and number of correct predictions.
    return values[:-1] if training != None else values

# <markdowncell>

# ### Main training/evaluation loop
# The following is a simple function which trains or evaluates current model for a given number
# of epochs by repeatedly calling the `run_iteration` function defined above. It also takes care of:
# * aggregating loss and accuracy values over all minibatches
# * plotting loss and accuracy values over time.
#
# The code below assumes that `mean_loss`, `accuracy`, and `scalar_summaries` are defined externally.
# Optionally, the caller may also define `image_summaries`, if they want some image summaries (like kernel
# and activation visualizations in the form of images) to be also included.
# All those definitions will change depending on the network.

# <codecell>

def run_model(session, x, y, epochs, batch_size, print_every, training):

    # Number of examples in dataset.
    dataset_size = x.shape[0]

    # Count iterations since the beginning of training.
    iter_cnt = 0

    for e in range(epochs):
        # Randomize training examples for each epoch.
        train_indices = np.random.permutation(dataset_size)

        # Keep track of performance stats (loss and accuracy) in current epoch.
        total_loss = 0
        total_correct = 0

        # Iterate over the dataset once.
        for start_idx in range(0, dataset_size, batch_size):

            # Indices for current batch.
            idx = train_indices[start_idx : min(start_idx + batch_size, dataset_size)]

            # Get batch size (may not be equal to batch_size near the end of dataset).
            actual_batch_size = y[idx].shape[0]

            # Set up variables that we want to compute.
            variables = [mean_loss, accuracy, scalar_summaries]
            has_image_summaries = image_summaries != None
            if has_image_summaries:
                variables += [image_summaries,]

            # Compute loss, accuracy, and scalar summaries, and optionally perform backward pass.
            results = run_iteration(session, variables, x[idx], y[idx], training)

            # Unpack results.
            loss, acc, scl_summ = results[:3]
            if has_image_summaries:
                img_summ = results[3]

            # Update performance stats.
            total_loss += loss * actual_batch_size
            total_correct += acc * actual_batch_size

            # Add scalar summaries to event file.
            if (training is not None):
                writer.add_summary(scl_summ, iter_cnt)

            # Print status, and add image summaries (if any).
            if (training is not None) and (iter_cnt % print_every) == 0:
                print("Iteration {0}: with minibatch training loss = {1:.3g} and accuracy of {2:.2f}%".format(iter_cnt, loss, acc * 100))
                if has_image_summaries:
                    writer.add_summary(img_summ, iter_cnt)

            iter_cnt += 1

        # Compute performance stats for current epoch.
        avg_accuracy = total_correct / dataset_size
        avg_loss = total_loss / dataset_size

        print("Epoch {2}, Overall loss = {0:.3g} and accuracy of {1:.2f}%".format(avg_loss, avg_accuracy * 100, e + 1))

# <markdowncell>

# ### Training the model for one epoch

# <codecell>

sess = tf.Session()
sess.run(tf.global_variables_initializer())
print('Training')
run_model(sess, x=x_train, y=y_train, epochs=1, batch_size=64, print_every=100, training=train_step)
print('Validation')
run_model(sess, x=x_val, y=y_val, epochs=1, batch_size=64, print_every=100, training=None)

# <markdowncell>

# ### View summaries in TensorBoard log
# Now you should be able to refresh your TensorBoard tab and see the summaries.
# For more details please see [official tutorial](https://www.tensorflow.org/get_started/summaries_and_tensorboard) on summaries.
# TensorFlow also supports other kinds of summaries, such as [histograms](https://www.tensorflow.org/get_started/tensorboard_histograms).

# <markdowncell>

# ### Visualize some predictions
# Accuracy should be somewhat better now.

# <codecell>

random_index, random_image, random_label = choose_random_image()
guess_class_value, guess_prob_value = run_iteration(sess, [guess_class, guess_prob], random_image, random_label)
visualize_classification((random_image[0] + mean_image) / 255.0, guess_class_value, guess_prob_value)

# <markdowncell>

# ## Adding batch normalization
#
# Now we will modify the simple architecture by adding batch normalization. We expect this network to train faster, and achieve better
# accuracy for the same number of weight updates.
#
# Your task is to implement `bn_net` function below so that it creates the same network as in the previous section (with convolution, pooling,
# and two fully connected layers), except that there should also be a batch normalization layer after the convolution layer.
#
# Arguments of `bn_net` are placeholders for data (`x`), labels (`y`), and boolean flag (`is_training`) for indicating if the network should
# perform training or validation version of the computation on the input data. While `is_training` was completely ignored in the previous
# network, *here it should actually matter* (why?). `bn_net` should return the output (prediction) tensor of the network.
#
# API reference for batch normalization is at https://www.tensorflow.org/api_docs/python/tf/layers/batch_normalization.

# <codecell>

def bn_net(x, y, is_training):
    # TODO
    pass

# <markdowncell>

# Input, metrics and optimizer are the same as before, so we can assemble the whole network.
# If you implemented `bn_net` correctly, this code should run without modification.

# <codecell>

tf.reset_default_graph()
writer = tf.summary.FileWriter(os.path.join(log_dir, 'bn_net'))
x, y, is_training = setup_input()
y_out = bn_net(x, y, is_training)
mean_loss, accuracy, guess_prob, guess_class = setup_metrics(y, y_out)
scalar_summaries = setup_scalar_summaries()
image_summaries = None
train_step = setup_optimizer(mean_loss, 5e-4)

# <markdowncell>

# Now we are ready to train and validate the network with batch normalization as before.

# <codecell>

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print('Training')
    run_model(sess, x=x_train, y=y_train, epochs=1, batch_size=64, print_every=100, training=train_step)
    print('Validation')
    run_model(sess, x=x_val, y=y_val, epochs=1, batch_size=64, print_every=100, training=None)

# <markdowncell>

# ## Training a more complex network
#
# Please download TensorBoard event file from [here](https://1drv.ms/u/s!ApgLrbSPQF9XirQrPV7L1GOhxkc-ig).
# Your task is to build the architecture contained in the event file `cifar10_net_log\events.out.tfevents.1500743084.localhost`, and
# train it on CIFAR-10. You should train for 8 epochs with batch size 100 and learning rate 0.001.

# <codecell>

def cifar10_net(x, y, is_training):
    # TODO
    pass

# <codecell>

tf.reset_default_graph()
writer = tf.summary.FileWriter(os.path.join(log_dir, 'complex_net'))
x, y, is_training = setup_input()
y_out = cifar10_net(x, y, is_training)
mean_loss, accuracy, guess_prob, guess_class = setup_metrics(y, y_out)
scalar_summaries = setup_scalar_summaries()
image_summaries = None
train_step = setup_optimizer(mean_loss, 1e-3)

# <codecell>

writer.add_graph(tf.get_default_graph())

# <codecell>

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print('Training')
    run_model(sess, x=x_train, y=y_train, epochs=8, batch_size=100, print_every=100, training=train_step)
    print('Validation')
    run_model(sess, x=x_val, y=y_val, epochs=1, batch_size=100, print_every=100, training=None)

# <markdowncell>

# ## Saving and loading models
#
# Saving is done using `tf.train.Saver` class:
# * `save` method saves both network definition and weights.
# * `export_meta_graph` method saves only network definition.
#
# Loading is done in two stages:
# * `tf.train.import_meta_graph` function loads network definition, and returns a saver object that was used to save the model.
# * `restore` method of the returned saver object loads the weights.
#
# Note that since weights are available only inside a session, `save` and `restore` methods above require a session object as a parameter.
#
# Official TensorFlow documentation: [Saving and Restoring Variables](https://www.tensorflow.org/api_guides/python/state_ops#Saving_and_Restoring_Variables), [tf.train.Saver class](https://www.tensorflow.org/api_docs/python/tf/train/Saver), [tf.train.import_meta_graph function](https://www.tensorflow.org/api_docs/python/tf/train/import_meta_graph).
#
# Useful unofficial tutorial on saving and loading: http://cv-tricks.com/tensorflow-tutorial/save-restore-tensorflow-models-quick-complete-tutorial/
#
# Many pretrained models found online do not contain network definition (meta-graph) files. The authors instead provide TensorFlow
# code to create the network graph. For such graphs, weights can also be restored from checkpoint file. Network definition file is
# just a way to avoid copying the code.

# <markdowncell>

# ## Transfer learning
#
# In this section we will take a model which is pretrained on ImageNet for 1000-class image classification task, and
# finetune it for our 10-class CIFAR-10 classification task.
#
# Pretrained model is given by meta-graph file (containing network definition), and checkpoint file (containing weights).
# Please download pretrained model from [here](https://1drv.ms/u/s!ApgLrbSPQF9XirQpuN32y0577zey8g).

# <codecell>

pretrained_meta_graph = os.path.join('mobilenet_v2_1.0_224', 'mobilenet_v2_1.0_224.meta')
pretrained_checkpoint = os.path.join('mobilenet_v2_1.0_224', 'mobilenet_v2_1.0_224.ckpt')

# <markdowncell>

# For the CIFAR-10 task we need to perform the following two modifications to the pretrained model at the very minimum:
# * Process CIFAR-10 images so that their size becomes what pretrained model expects
# * Adapt the layers which perform final classification so that the number of output neurons is 10 (the number of
# classes in the CIFAR-10 classification task)

# <markdowncell>

# ### Get names of relevant nodes
#
# Modifying input part of a pretrained network is somewhat cumbersome. It must be done simultaneously with loading network
# definition, by passing to `tf.train.import_meta_graph` a mappping from input tensors of the pretrained network to new input tensors.
#
# First we load pretrained network definition only to get the names of input placeholder nodes that we want to replace.
# This step can be skipped if these names are already known.

# <codecell>

tf.reset_default_graph()
_ = tf.train.import_meta_graph(pretrained_meta_graph)

# <markdowncell>

# The easiest way to get the nodes' names is using TensorBoard. It can also be done programmatically, as explained above
# (for example, using `tf.get_default_graph().get_operations()`).

# <codecell>

writer = tf.summary.FileWriter(os.path.join(log_dir, 'transfer_net'))
writer.add_graph(tf.get_default_graph())

# <markdowncell>

# After inspecting TensorFlow graph visualization, we find that
# * the input nodes are `image` and `is_training`
# * final classification is performed in the node group (subgraph) `MobilenetV2/Logits/Conv2d_1c_1x1` which implements 1x1 convolution.

# <markdowncell>

# ### Modify input and output

# Next we clear the default graph, and start creating new one, with modified input subnetwork which upsamples input image
# to match the size expected by pretrained network.

# <codecell>

tf.reset_default_graph()
writer = tf.summary.FileWriter(os.path.join(log_dir, 'transfer_net'))
x, y, is_training = setup_input()
x_upsampled = tf.image.resize_images(x, [224, 224])

# <markdowncell>

# Finally, we reload pretrained network definition, replacing pretrained input placeholders with new tensors we just created.

# <codecell>

saver = tf.train.import_meta_graph(pretrained_meta_graph, input_map={'image:0' : x_upsampled, 'is_training:0' : is_training})

# <markdowncell>

# We want to replace the node group `MobilenetV2/Logits/Conv2d_1c_1x1` by a 1x1 convolution with different number of output
# channels. To that end, we get a handle to the tensor immediately preceding `MobilenetV2/Logits/Conv2d_1c_1x1` in the graph.

# <codecell>

feat = tf.get_default_graph().get_tensor_by_name('MobilenetV2/Logits/Dropout/cond/Merge:0')

# <markdowncell>

# Attach a new prediction layer for modified task.

# <codecell>

with tf.variable_scope('MobilenetV2/Logits'):
      conv_1x1 = tf.layers.conv2d(inputs=feat, filters=num_classes, kernel_size=[1, 1], strides=1, name='conv2d_1x1_modified')
      output = tf.squeeze(conv_1x1, [1, 2], name='output_modified')

# <markdowncell>

# ### Complete network definition
# Add metrics and optimizer as before.

# <codecell>

mean_loss, accuracy, guess_prob, guess_class = setup_metrics(y, output)
scalar_summaries = setup_scalar_summaries()
image_summaries = None
train_step = setup_optimizer(mean_loss, 5e-4)

# <markdowncell>

# Once again write out the graph to make sure surgery succeeded.

# <codecell>

writer.add_graph(tf.get_default_graph())

# <markdowncell>

# ### Train and validate network (at your own risk!)

# <markdowncell>

# Only now we can restore weights from checkpoint, because weights exist only inside a session.
#
# Then we train the network as before. Note that this training is *extremely slow on CPU*, due to bigger network.
# Also observe that we still need to initialize variables, because we have introduced new ones, i.e. not all
# variables that exist in the network are restored from checkpoint.

# <codecell>

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    saver.restore(sess, pretrained_checkpoint)
    print('Training')
    run_model(sess, x_train, y_train, epochs=1, batch_size=64, print_every=100, training=train_step)
    print('Validation')
    run_model(sess, x_val, y_val, epochs=1, batch_size=64, print_every=100, training=None)


import godafoss as gf
edge = gf.edge()

display = gf.lcd(
    chip = "st7789",
    size = gf.xy( 128, 128 ),
    spi = edge.soft_spi(),
    data_command = edge.data_command,
    chip_select = edge.chip_select,
    reset = edge.reset,
    backlight = edge.backlight,
    background = gf.colors.black,
    #invert = False,
    color_order = "RBG",
    #xy_swap = True,
    #x_reverse = True,
    margin = gf.xy( 2, 1 ),
    x_deadband = 0
)

display.clear( gf.colors.red )
gf.line( gf.xy( 100, 100 ) ).write( display )
gf.text( "XXXXX" ).write( display )
display.flush()
display.demo()


# -*- coding: utf-8 -*-
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from core.utils import render_to_response
from core.decorators import admin_required

from .models import Point, PointForm

@admin_required
def world_map(request):
    context = {
        'points': Point.objects.all(),
    }

    return render_to_response(request, 'world/index.html', context)

@admin_required
def add_point(request):
    if request.POST:
        form = PointForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('world_map'))

    else:
        form = PointForm(request.GET)

    return render_to_response(request, 'world/add_point.html', {'form': form})

@admin_required
def delete_point(request, point_id):
    point = Point.objects.get(pk=point_id)
    if request.POST:
        point.delete()
        return HttpResponseRedirect(reverse('world_map'))

    return render_to_response(request, 'world/delete_point.html', {'point': point})


'''
Created on 2011-10-11
@author: jacekf

Responsible for converting return values into cleanly serializable dict/tuples/lists
for JSON/XML/YAML output
'''

import inspect, collections
from jinja2 import Template

xmlListTemplate = Template("""<list>{% for item in items %}<item>{% for prop,val in item.iteritems() %}<{{prop}}>{{val}}</{{prop}}>{% endfor %}</item>{% endfor %}</list>""")
xmlTemplate = Template("""<item>{% for prop,val in item.iteritems() %}<{{prop}}>{{val}}</{{prop}}>{% endfor %}</item>""")

def convertForSerialization(object):
    """Converts anything (clas,tuples,list) to the safe serializable equivalent"""
    if isinstance(object, dict):
        return traverseDict(object)
    elif isClassInstance(object):
        return convertClassToDict(object)
    elif isinstance(object,collections.Iterable):
        # iterable
        values = []
        for val in object:
            values.append(convertForSerialization(val))
        return values
    else:
        # return as-is
        return object

def convertClassToDict(clazz):
    """Converts a class to a dictionary"""
    properties = {}

    for prop,val in clazz.__dict__.iteritems():
        #omit private fields
        if not prop.startswith("_"):
            properties[prop] = val

    return traverseDict(properties)

def traverseDict(dictObject):
    """Traverses a dict recursively to convertForSerialization any nested classes"""
    newDict = {}
    for prop,val in dictObject.iteritems():
        if inspect.isclass(val):
            # call itself recursively
            val = convertClassToDict(val)

        newDict[prop] = val

    return newDict

def generateXml(object):
    """Generates basic XML from an object that has already been converted for serialization"""
    if isinstance(object,dict):
        return str(xmlTemplate.render(item=object.keys()))
    elif isinstance(object,collections.Iterable):
        return str(xmlListTemplate.render(items=object))
    else:
        raise RuntimeError("Unable to convert to XML: %s" % object)

def isClassInstance(object):
    """Checks if a given object is a class instance"""
    return getattr(object, "__class__",None) != None and not isinstance(object,dict) and not isinstance(object,tuple) and not isinstance(object,list)


from django.core.management.base import AppCommand, CommandError
from django.db.models import get_models
from django.template import loader as TemplateLoader, Context
from optparse import make_option
from bricklayer.utils import ColoredOutputMixin
import os
import re
import imp

class Command(AppCommand,ColoredOutputMixin):
    help = """Creates and populates a basic admin.py file if it doesn't exists. For each model in the app,
            create an AdminModel class (if it doesn't exists) and then register the model on the admin site."""

    args = '[appname ...]'

    option_list = AppCommand.option_list + (
        make_option('--models', '-m',
                    dest='models',
                    type="string",
                    default=None,
                    help='Model/s name. Multiple models can be passed separated by a comma. E.g. -m User,Post,Comment'),
    )

    def handle_app(self, app, **options):
        app_label = app.__name__.split('.')[-2]
        app_path = os.path.dirname(app.__file__)
        admin_filename = os.path.join(os.path.dirname(app.__file__), 'admin.py')
        is_verbose = options['verbosity'] >= 1

        # Get the name of each model in the app
        app_models = [model._meta.object_name for model in get_models(app)]
        # if models
        template = TemplateLoader.get_template("admin.py.template")
        if options['models']:
            # Accept list of models as CSV
            requested_models = options["models"].split(",")
            app_models = filter(lambda model: model in requested_models, app_models)

        if not os.path.exists(admin_filename):
            self.stdout.write(app_label+".admin.py created")
            mode = "w"
            context = Context({"app": app_label,
                               "import_admin": True,
                                "models": app_models,
                                "models_to_define": app_models,
                                "models_to_register": app_models,
                                "models_to_import":app_models})

        else:
            self.stdout.write("admin.py already exists")
            """
            Check if each model is already registered.
            For each model which hasn't been registered yet,
            create an AdminClass and register the model.
            """
            admin_module = imp.load_source("admins",os.path.join(app_path, 'admin.py'))
            with open(admin_filename, 'r') as admin_file:
                admin_file_lines = admin_file.readlines()
                admin_file_text = "".join( admin_file_lines  )

            mode = 'a'
            models_to_define = []
            models_to_register = []
            models_to_import = []
            for model in app_models:
                regex = re.compile("(admin.site.register\(\s*"+model+"\s*,)",re.MULTILINE)

                #if them model wasn't loaded yet...
                if model not in dir(admin_module):
                    models_to_import.append(model)

                #if model has not been registered yet..
                if not regex.search(admin_file_text):
                    models_to_register.append(model)

                # If admin model name doesn't collide with an existing one
                if not "%sAdmin"%model in admin_file_text:
                    models_to_define.append(model)

            context = Context({"app": app_label,
                               "import_admin": "admin" in dir(admin_module),
                               "models": app_models,
                               "models_to_define": models_to_define,
                               "models_to_import":models_to_import,
                               "models_to_register": models_to_register})

        # Write admin.py
        with open(admin_filename, mode) as admin_file:
                admin_file.write(template.render(context))

        if is_verbose:
            if mode == "w":
                msg = "%s.admins created"
            else:
                msg = "%s.admins updated"
            self.print_success(msg%app_label)


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

s_lista = sorted(lista) # ordena em ordem ascendente
print(s_lista)

r_lista = sorted(lista, reverse=True) # ordena em ordem decrescente
print(r_lista)

lista.sort()
print(lista)

tup = (9, 1, 8, 2, 7, 3, 6, 4, 5)
s_tup = sorted(tup)
print(s_tup)

dic = {'nome': 'Gabriel', 'trabalho': 'programador', 'idade': None, 'os': 'ubuntu'}
s_dic = sorted(dic)
print(s_dic)

li = [-6, -5, -4, 1, 2, 3]
s_li = sorted(li, key=abs)
print(s_li)

from operator import attrgetter

class Empregado():
	def __init__(self, name, age, salary):
		self.name = name
		self.age = age
		self.salary = salary

	def __repr__(self):
		return '({},{},{})'.format(self.name, self.age, self.salary)

e1 = Empregado('Jose', 44, 60000)
e2 = Empregado('Paula', 25, 63000)
e3 = Empregado('Ricardo', 29, 55000)

empregados = [e1,e2,e3]

def e_sort(emp):
	return emp.name

s_empregados = sorted(empregados, key=e_sort)
s_empregado = sorted(empregados, key=lambda e: e.age)
s_emp = sorted(empregados, key=attrgetter('salary'))
print(s_empregados)
print(s_empregado)
print(s_emp)


from newlines import *
from convertitem import *

weapons = ["Melee", "Ranged", "Magic"]

def useItem(player, lvlmax):
    bigboi()
    player.printInv()
    newline()
    which = input("What item do you want to use? \n")

    item = convertItem(which)

    bigboi()
    if item[0] != 0:
        finished = 0
        item = item[1]

        if item.type in weapons:
            if item == player.equipped["main"] or item == player.equipped["side"]:
                if item.type == "Melee":
                    dmgType = "swing at"

                elif item.type == "Ranged":
                    dmgType = "shoot"

                elif item.type == "Magic":
                    dmgType = "curse"

                print("You %s the air and do %s damage." % (dmgType, item.power))
                newline()

            else:
                print("You cant use that item. You dont have it equipped.")
                newline()

        elif item.type == "Potion":
            for itm in range(len(player.inv)):
                if finished == 1:
                    do = "nothing"

                elif player.inv[itm].name == item.name:
                    player.inv.pop(itm)
                    finished = 1

            if finished != 1:
                print("You dont have that item.")
                newline()
                return()

            player.health += item.power
            if player.health >= lvlmax[player.level]["health"]:
                player.health = lvlmax[player.level]["health"]
                print("You healed to full health!")
            else:
                print("You healed for %s health." % item.power)
            newline()

        else:
            print("You cant use armor. Its only for defense.")
            newline()

    else:
        print("You dont have that item.")
        newline()