        # TODO:  Add existing file check
        self.project_path += self.string_session_name.get()
        self.sec.save_project(self.project_path)

import tkinter as tk  # gui interface creator
import tkinter.ttk as ttk
import tkinter.filedialog
from tkinter import messagebox
import matplotlib  # graphing functions
matplotlib.use("TkAgg")
import time  # http://example.com  # time functions
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import random
import os  # http://example.com
import Environment.spot_environment_controller  # condensed modules/commands from spot_env_model
import Simulator.spot_market_period

import Institution.spot_system as sys
import random
import csv
import matplotlib.pyplot as plt
import numpy as np
import os
from win32api import GetSystemMetrics  # gets your computer's resolution size
import time
import Trader.trader as tdr
from timeit import default_timer as timer
import scipy.stats as stats
import GUI.spot_environment_gui
import inspect
instant_shock_status = 0

# TODO IMPORTANT: need to clear contract list in spot system or double auction institution...
# --> causing problems with transactions graph when re-running the simulator in market_gui
# --> maybe clearing lists in run_sim() or spot_market_period?

# TODO IMPORTANT: need to fix Supply and Demand graph error in run_sim()...
# --> when re-running simulator in market_gui plots trader efficiency distribution in supply demand graph

# --> p_shock_list (line 739 below) can be used within for loop (line 775 below)

# TODO IMPORTANT: fix tkinter error after sim run...
# --> getting "ttk:ThemeChanged... event destroyed" error after sim run --> don't know why

# TODO OPTIONAL: add ability for all info to be stored in json files...
# --> could then let user choose between using excel csv or json files

class MarketGui():
    def __init__(self, root, sec, project_path, output_path, name, debug=False):
        assert name != "", "Gui must have a name"

        self.root = root  # root builds tkinter app
        '''Can maybe add an os function for resolutions to adapt'''
        self.root.geometry(str(GetSystemMetrics(0)) + "x" + str(GetSystemMetrics(1)))
        self.sec = sec  # will bring in spot_env_model and use debugger
        self.name = name  # name of gui
        self.debug = debug  # used as error checker...when false will return errors or warnings
        self.root.title(name)  # giving root a name
        '''Added a messagebox for when escape button pressed... calls on_escape_chosen()'''
        self.root.protocol("WM_DELETE_WINDOW", self.on_quit_chosen)

        self.num_periods = 0  # setting number of buyers to 0
        self.num_rounds = 0
        self.num_p_shocks = 0  # setting number of sellers to 0
        self.num_r_shocks = 0  # setting number of units to 0
        self.num_buyers = 0  # setting number of buyers to 0
        self.num_sellers = 0  # setting number of sellers to 0
        self.num_units = 0  # setting number of units to 0
        self.floor = 0
        self.ceiling = 0
        self.session = None
        self.traders = []
        self.strategies = []

        self.strategy_string = tk.StringVar()
        self.string_institution = tk.StringVar()
        self.string_periods = tk.StringVar()    # creates a tkinter variable
        self.string_rounds = tk.StringVar()
        self.string_round_shocks = tk.StringVar()   # StringVar() returns either an ASCII string or Unicode string
        self.string_period_shocks = tk.StringVar()     # can also be used to trace when changes made to variables
        self.price_floor = tk.StringVar()
        self.price_ceiling = tk.StringVar()
        self.string_session_name = tk.StringVar()  # BooleanVar() will return 0 for false and 1 for true...
        self.string_data = tk.StringVar()
        self.new_string_data = tk.StringVar()
        self.string_num_buyers = tk.StringVar()  # creates a tkinter variable
        self.string_num_sellers = tk.StringVar()  # StringVar() returns either an ASCII string or Unicode string
        self.string_num_units = tk.StringVar()
        self.string_eq = tk.StringVar()
        self.string_pl = tk.StringVar()
        self.string_ph = tk.StringVar()
        self.string_ms = tk.StringVar()

        self.buyer_ids = None
        self.seller_ids = None
        self.p_shock_ids = None
        self.p_shock_files = None
        self.r_shock_ids = None
        self.r_shock_files = None
        self.buyer_shift = tk.StringVar()
        self.seller_shift = tk.StringVar()
        self.buyer_replace_strategy = tk.StringVar()
        self.seller_replace_strategy = tk.StringVar()

        self.trader_replace = tk.IntVar()

        self.current_row = 0  # setting current read row to 0... self.current_row+1 would read next row
        self.current_row_contents = []
        self.ids = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # is this limiting the amount to 10 buyers and 10 sellers?
        self.file_name = None  # none is a placeholder to be filled

        # have to build matrices for future tkinter display
        self.pshock_values = self.build_array(self.num_r_shocks, self.num_r_shocks)  # matrix of buyers and number of units
        self.rshock_values = self.build_array(self.num_p_shocks, self.num_p_shocks)  # matrix of sellers and number of units
        self.buyer_values = self.build_array(self.num_buyers, self.num_units)  # matrix of buyers and number of units
        self.seller_costs = self.build_array(self.num_sellers, self.num_units)  # matrix of sellers and number of units

        self.project_path = project_path
        self.output_path = output_path + '/'

        self.autofill_strat = tk.StringVar()

        # have to build menu and start the project
        self.show_menu()  # executes menu build with toolbar and help/action messages
        self.show_shortcut()  # executes frame build in tkinter
        self.show_infobar()  # executes sub-frame for user entering number buyers, number sellers, units

        self.parameters_trigger = False
        self.save_file_trigger = False
        self.strategy_trigger = False  # safety feature when rerunning in gui: if same parameters used = True
        self.r_shock_trigger = False
        self.p_shock_trigger = False
        self.replace_trader_trigger = False
        self.display_sd_info = False

    def build_array(self, num_1, num_2):  # builds an array for buyers:values and sellers:costs
        x = []
        for j in range(num_1):
            a_row = []
            for k in range(num_2):
                a_row.append(k)
            x.append(a_row)
        return x

    def show_menu(self):
        # getting icons ready for compound menu
        menu_bar = tk.Menu(self.root)  # menu begins

        # create file menu item
        file_menu = tk.Menu(menu_bar, tearoff=0)
        file_menu.add_command(label='New', accelerator='Ctrl+N',
                              compound='left', underline=0, command=None)
        file_menu.add_command(label='Open', accelerator='Ctrl+O',
                              compound='left', underline=0, command=self.open_file)
        file_menu.add_command(label='Save', accelerator='Ctrl+S',
                              compound='left', underline=0, command=self.save)
        file_menu.add_separator()
        file_menu.add_command(label='Quit', accelerator='Alt+F4', command=self.on_quit_chosen)
        menu_bar.add_cascade(label='File', menu=file_menu)  # allows toolbar tab to drop down with multiple choices

        # create about/help menu
        about_menu = tk.Menu(menu_bar, tearoff=0)
        about_menu.add_command(label='About', command=self.display_about_messagebox)  # click = display about message
        about_menu.add_command(label='Help', command=self.display_help_messagebox)  # click = display help message
        # TODO add link to github readme in display_help_messagebox
        menu_bar.add_cascade(label='Misc', menu=about_menu)  # drop down menu
        self.root.config(menu=menu_bar)  # makes menu setup final

    def show_shortcut(self):
        shortcut_bar = tk.Frame(self.root)  # creates a frame within the tkinter object
        shortcut_bar.grid(row=0, column=0, columnspan=4, sticky='W')  # setting parameters of frame

    def show_infobar(self):
        for name, obj in inspect.getmembers(tdr):  # obtains all trader strategies from Trader.trader
            if inspect.isclass(obj):
                self.strategies.append(obj.__name__)  # appends to list
        # creates a frame in gui to set project paths

        # creates a frame in gui to enter in new session data for simulator run
        info_bar = tk.LabelFrame(self.root, height=15, text=str(self.name))  # creates a label frame for initial inputs
        info_bar.grid(row=1, column=0, columnspan=3, sticky='W', padx=5, pady=5)  # set parameters

        # create project name label and entry
        tk.Label(info_bar, text="Session Name").grid(row=0, column=0, padx=10)
        tk.Entry(info_bar, width=15, justify=tk.LEFT, textvariable=self.string_session_name).grid(row=1, column=0)

        # create number of buyers label and entry
        tk.Label(info_bar, text="Period Shocks").grid(row=0, column=1, padx=10)
        tk.Entry(info_bar, width=5, justify=tk.CENTER, textvariable=self.string_period_shocks).grid(row=1, column=1)
        self.string_period_shocks.set(str(self.num_p_shocks))

        # create number of sellers label and entry
        tk.Label(info_bar, text="Round Shocks").grid(row=0, column=2, padx=10)
        tk.Entry(info_bar, width=5, justify=tk.CENTER, textvariable=self.string_round_shocks).grid(row=1, column=2)
        self.string_round_shocks.set(str(self.num_r_shocks))

        # create number of units label and entry
        tk.Label(info_bar, text="Periods").grid(row=0, column=3, padx=10)
        tk.Entry(info_bar, width=5, justify=tk.CENTER, textvariable=self.string_periods).grid(row=1, column=3)
        self.string_periods.set(str(self.num_periods))

        # create number of rounds label and entry
        tk.Label(info_bar, text="Rounds").grid(row=0, column=4, padx=10)
        tk.Entry(info_bar, width=5, justify=tk.CENTER, textvariable=self.string_rounds).grid(row=1, column=4)
        self.string_rounds.set(str(self.num_rounds))

        # create price floor label and entry
        tk.Label(info_bar, text="Price Floor").grid(row=0, column=5, padx=10)
        tk.Entry(info_bar, width=5, justify=tk.CENTER, textvariable=self.price_floor).grid(row=1, column=5)
        self.price_floor.set(str(self.floor))

        # create price ceiling label and entry
        tk.Label(info_bar, text="Price Ceiling").grid(row=0, column=6, padx=10)
        tk.Entry(info_bar, width=5, justify=tk.CENTER, textvariable=self.price_ceiling).grid(row=1, column=6)
        self.price_ceiling.set(str(self.ceiling))

        # create data file label and drop down menu to choose from
        tk.Label(info_bar, text="Starting Data File: ").grid(row=2, column=0, pady=10)  # create/grid location
        ttk.Combobox(info_bar, values=os.listdir(self.project_path), textvariable=self.string_data, state='readonly').grid(row=2, column=1)

        # create drop down menu for choosing institution to use
        tk.Label(info_bar, text="Market Institution: ").grid(row=3, column=0, pady=10)
        ttk.Combobox(info_bar, values=["DoubleAuction", "SealedBid", "Vickrey"], textvariable=self.string_institution, state='readonly').grid(row=3, column=1)
        self.string_institution.set("DoubleAuction")

        # creates button to explain institution settings
        explain1_button = tk.Button(info_bar, text="Explain", width=7, command=self.explain1_clicked)
        explain1_button.grid(row=3, column=2)

        # creates a button to show the chosen file's supply/demand graph
        plot_button = tk.Button(info_bar, text="Show", width=4, command=self.on_show_clicked)
        plot_button.grid(row=2, column=2)

        # creates frame for set parameters button
        set_frame = tk.LabelFrame(info_bar, text="Set Parameters")
        set_frame.grid(row=0, column=7, padx=5, pady=5)
        set_button = tk.Button(set_frame, text="Set", width=4, command=self.on_set_parms_clicked)
        set_button.grid(row=0, column=0, padx=30)  # creates grids in both built frames

        # creates a frame in gui to create a new supply/demand data file by pressing a button
        file_frame = tk.LabelFrame(info_bar, height=15, text="New Data File")
        file_frame.grid(row=1, column=7, padx=5, pady=5)
        # create a button with action input (command = click)
        create_button = tk.Button(file_frame, text="Create", width=6, command=self.on_create_clicked)
        create_button.grid(row=0, column=0, padx=30)

        # creates a frame within the infobar to set instant shocks
        '''If this feature is enabled will execute code in spot_system.py ---> run_system() --> line 133'''
        trader_replace_frame = tk.LabelFrame(info_bar, text="Trader Replacement")
        trader_replace_frame.grid(row=3, column=3, columnspan=5)

        # creates a check box for enabling instantaneous market shocks
        enable_button = tk.Checkbutton(trader_replace_frame, text="Enabled", variable=self.trader_replace)
        enable_button.grid(row=0, column=0)

        # button that displays info about instantaneous market shocks
        explain2_button = tk.Button(trader_replace_frame, text="Explain", width=7, command=self.explain2_clicked)
        explain2_button.grid(row=0, column=1)

        # creates button to explain the different trading strategies that the user can use
        strat_info_button = tk.Button(trader_replace_frame, text="Strategy Descriptions", width=16,
                                      command=self.strategy_message)
        strat_info_button.grid(row=0, column=2)

        tk.Label(trader_replace_frame, text="Strategy").grid(row=1, column=1, pady=5)
        tk.Label(trader_replace_frame, text="New Buyer: ").grid(row=2, column=0)
        ttk.Combobox(trader_replace_frame, values=self.strategies, textvariable=self.buyer_replace_strategy, state='readonly').grid(row=2, column=1)
        tk.Label(trader_replace_frame, text="New Seller: ").grid(row=3, column=0)
        ttk.Combobox(trader_replace_frame, values=self.strategies, textvariable=self.seller_replace_strategy, state='readonly').grid(row=3, column=1)

    '''The comand below generates a tk messagebox to explain the different institutions that can be used'''
    def explain1_clicked(self):
        explain1_message = "\nChanging this variable will change the institution that is used," \
                           " which will set the rules of trading in the market simulation." \
                           "\n \nA Double Auction is when multiple sellers and buyers can trade" \
                           " a single unit or multiple units. The bidding will continue until a" \
                           " seller's and bidder's bid converge, at which point a contract will be made." \
                           "\n \nA Sealed Bid Auction is when there are either one seller and multiple buyers" \
                           " OR one buyer and multiple sellers. The group of multiple traders each submit" \
                           " a sealed bid, at which point the single seller or buyer accepts one of the bids " \
                           " and a contract is made." \
                           "\n \nA Vickrey Auction is a type of sealed bid auction where the highest bidder wins," \
                           " but the price paid is that of the second highest bid." \
                           "\n \nMore institutions can be added"
        tk.messagebox.askokcancel("MARKET INSTITUTIONS", explain1_message)

    '''The command below generates a tk messagebox to explain the use of instantaneous market shocks'''
    def explain2_clicked(self):
        explain2_message = "\nEnabling this feature will disable period and round shocks" \
                          "\n \nTrader replacement will occur when a contract is made" \
                          " between a seller and a buyer." \
                          "\n \nThis will result in those buyer's and seller's private values" \
                          " being recycled to the new buyer and new seller entering the market." \
                          "\n \nThis will result in keeping the supply and demand constant."
        tk.messagebox.askokcancel("TRADER REPLACEMENT", explain2_message)

    '''The command below generates a tk messagebox to explain the use of strategies that the user can use'''
    def strategy_message(self):
        # TODO fill in with descriptions of each strategy.. maybe include link or ref to paper?
        strat_explain = "\nTrader_AA: ..... \n \n Trader_AI: .... \n \n Trader_GD: .... \n \n Trader_Kaplan: ..." \
                        "\n \n Trader_PS: ... \n \n Trader ZIP: ... \n \n Trader_ZIC: ..."
        tk.messagebox.askokcancel("STRATEGY DESCRIPTIONS", strat_explain)

    def on_create_clicked(self):
        '''Added ability to run spot_environment_gui in market_gui... allows new SD creation'''
        create_root = tk.Toplevel()
        create_debug_test = True
        if create_debug_test:
            print("In Gui -> START")
        create_sec = Environment.spot_environment_controller.SpotEnvironmentController(create_debug_test)
        GUI.spot_environment_gui.SpotEnvironmentGui(create_root, create_sec, self.project_path, "File Creation", create_debug_test)
        create_root.mainloop()
        if debug_test:
            print("In Gui -> END")

    def on_quit_chosen(self):
        """This gives a messagebox when either quit or escape is chosen"""
        if tkinter.messagebox.askokcancel("Exit?", "Have you saved your work?"):
            self.root.destroy()  # closes window and destroys tkinter object

    def on_show_clicked(self):
        '''When clicked will show the supply/demand graph and seller/buyer values of chosen data file'''
        self.sec.load_file(self.project_path + "/" + str(self.string_data.get()))
        data_frame = tk.LabelFrame(self.root, text="Data File Information")
        data_frame.grid(row=2, column=0)
        tk.Label(data_frame, text="Buyers: " + str(self.sec.get_num_buyers())).grid(row=0, column=0)
        tk.Label(data_frame, text="Sellers: " + str(self.sec.get_num_sellers())).grid(row=0, column=1)
        tk.Label(data_frame, text="Units: " + str(self.sec.get_num_units())).grid(row=0, column=2)
        tk.Label(data_frame, text=str(self.sec.get_supply_demand_list())).grid(row=1, column=0, columnspan=4)
        self.num_buyers = self.sec.get_num_buyers()
        self.num_sellers = self.sec.get_num_sellers()
        self.num_units = self.sec.get_num_units()
        self.sec.plot_gui(self.string_data.get())
        self.display_sd_info = True

    '''The function below checks to make sure that each variable is an integer'''
    def check_integer(self, check_var):
        try:
            int(check_var)  # check if integer
        except ValueError:  # value error raised if not
            self.parameters_trigger = True  # triggers parameter alarm
            messagebox.askokcancel("INTEGER ERRORS", "A parameter requiring a number is not a number \n --> Please check")
        else:
            pass  # else pass to next variable check

    '''The function below checks all parameters are set correctly, if not will throw error message boxes to user'''
    def on_set_parms_clicked(self):
        if self.display_sd_info == False:  # if show was not clicked will still display supply/demand information
            self.sec.load_file(self.project_path + "/" + str(self.string_data.get()))
            data_frame = tk.LabelFrame(self.root, text="Data File Information")
            data_frame.grid(row=2, column=0)
            tk.Label(data_frame, text="Buyers: " + str(self.sec.get_num_buyers())).grid(row=0, column=0)
            tk.Label(data_frame, text="Sellers: " + str(self.sec.get_num_sellers())).grid(row=0, column=1)
            tk.Label(data_frame, text="Units: " + str(self.sec.get_num_units())).grid(row=0, column=2)
            tk.Label(data_frame, text=str(self.sec.get_supply_demand_list())).grid(row=1, column=0, columnspan=4)
            self.num_buyers = self.sec.get_num_buyers()
            self.num_sellers = self.sec.get_num_sellers()
            self.num_units = self.sec.get_num_units()
        else:
            pass

        """The below message boxes will appear to the user if the parameters are not set"""
        self.parameters_trigger = False  # sets alarm to catch parameter errors
        # check that session name is not blank
        if self.string_session_name.get() == "":  # if blank
            self.parameters_trigger = True  # trigger alarm
            messagebox.askokcancel("SESSION ERROR", "Session name has not been set \n --> Please type a session name")

        # check that starting data file has been set
        elif self.string_data.get() == "":  # if blank
            self.parameters_trigger = True  # trigger alarm
            messagebox.askokcancel("DATA ERROR", "A starting data file was not selected \n --> Please choose one or create one")

        # check that period shocks are not blank
        elif self.string_period_shocks.get() == "":  # if blank
            self.parameters_trigger = True  # trigger alarm
            messagebox.askokcancel("PERIOD SHOCK ERROR", "This entry is blank \n Please set to 0 if no shocks desired or enter the number of shocks")

        # check that round shocks are not blank
        elif self.string_round_shocks.get() == "":  # if blank
            self.parameters_trigger = True  # trigger alarm
            messagebox.askokcancel("ROUND SHOCK ERROR", "This entry is blank \n Please set to 0 if no shocks desired or enter the number of shocks")

        # check that number of periods has been set
        elif self.string_periods.get() == "" or self.string_periods.get() == '0':  # if blank or 0
            self.parameters_trigger = True  # trigger alarm
            messagebox.askokcancel("PERIOD ERROR", "Periods have not been set \n --> Please set the number of trading periods to run")

        # check that number of rounds are set
        elif self.string_rounds.get() == "" or self.string_rounds.get() == '0':
            self.parameters_trigger = True
            messagebox.askokcancel("ROUND ERROR", "Rounds have not been set \n --> Please set the number of rounds to run in each period")

        # check that minimum price floor has been set
        elif self.price_floor.get() == "":  # ok if price floor = 0 but if blank
            self.parameters_trigger = True  # trigger alarm
            messagebox.askokcancel("MIN PRICE ERROR", "Price floor does not have an entry \n --> Please set a minimum price for the market")

        # check that maximum price ceiling has been set
        elif self.price_ceiling.get() == "" or self.price_ceiling.get() == '0':
            self.parameters_trigger = True
            messagebox.askokcancel("MAX PRICE ERROR", "Price ceiling has not been set \n --> Please set a maximum price for the market")

        elif self.trader_replace.get() == 1:  # if instant shocks enabled
            if self.buyer_replace_strategy.get() == "":
                self.parameters_trigger = True
                tk.messagebox.askokcancel("TRADER REPLACEMENT ERROR", "New Buyer Strategy is not set \n --> Please set buyer shift strategy")
            elif self.seller_replace_strategy.get() == "":
                self.parameters_trigger = True
                tk.messagebox.askokcancel("TRADER REPLACEMENT ERROR", "New Seller Strategy is not set \n --> Please set seller shift strategy")
            else:
                parameters = [self.string_period_shocks.get(), self.string_round_shocks.get(), self.string_periods.get(),
                              self.string_rounds.get(), self.price_floor.get(), self.price_ceiling.get()]
                for i in parameters:
                    self.check_integer(i)
        else:  # check each parameter = integer
            parameters = [self.string_period_shocks.get(), self.string_round_shocks.get(), self.string_periods.get(), self.string_rounds.get(), self.price_floor.get(), self.price_ceiling.get()]
            for i in parameters:
                self.check_integer(i)

        if self.parameters_trigger == False:  # last error check is if price ceiling is greater than price floor
            if int(self.price_floor.get()) >= int(self.price_ceiling.get()):
                self.parameters_trigger = True
                messagebox.askokcancel("MAX/MIN ERROR", "Minimum price floor exceeds or equals maximum price ceiling \n --> Please correct this")

        # lastly ask user to make sure all parameters have been set correctly
        if self.parameters_trigger == False:  # if all error checks clean
            # if instant shocks enabled ask user to do parameter check
            if self.trader_replace.get() == 1:
                box = messagebox.askyesno("PROCEED?",
                                          "Check that parameters are set correctly \n Do you wish to continue?")
            # if round shocks and period shocks not set and instant shocks disabled...
            # ... let user know that no market shocks specified
            elif self.string_round_shocks.get() == "" or self.string_period_shocks.get() == "":
                box = messagebox.askyesno("PROCEED?", "No market shocks have been set \n Do you wish to continue?")
            # if instant shocks disabled but p shocks and r shocks set ask user to check params
            else:
                box = messagebox.askyesno("PROCEED?",
                                          "Check that parameters are set correctly \n Do you wish to continue?")
            if box == True:  # if user clicked "OK" on proceed message
                self.num_periods = int(self.string_periods.get())
                self.num_r_shocks = int(self.string_round_shocks.get())
                self.num_p_shocks = int(self.string_period_shocks.get())
                self.root.title(self.string_session_name.get())

                if self.num_p_shocks > 0:  # Build array if useful
                    self.pshock_values = self.build_array(self.num_p_shocks, self.num_p_shocks)
                if self.num_r_shocks > 0:  # Build array if useful
                    self.rshock_values = self.build_array(self.num_r_shocks, self.num_r_shocks)

                self.show_shock_frames()  # calls show_player_frames --> builds frame
                self.sec.set_market_parms([self.string_session_name.get(), self.num_buyers, self.num_sellers, self.num_units])
                run_frame = tk.LabelFrame(self.root, text="Run Simulation")
                run_frame.grid(row=1, column=3)
                run_button = tk.Button(run_frame, text="Run", width=4, command=self.check_sim)
                run_button.grid(row=0, column=0, padx=30)

            else:  # if user clicked "Cancel" on proceed message
                print("Continuing")
        else:
            pass  # hold for user to fix parameter errors

    def show_shock_frames(self):
        self.show_pshock_frame()  # show round shocks frame for entries
        self.show_rshock_frame()  # show period shocks frame for entries
        self.show_trader_strategies()  # show trader strategy frame for entries

    '''The function below creates ability for user to auto fill strategies in gui'''
    def auto_fill_strategies(self):
        for i in range(len(self.buyer_ids)):
            self.buyer_ids[i].set(self.autofill_strat.get())

        for i in range(len(self.seller_ids)):
            self.seller_ids[i].set(self.autofill_strat.get())

    '''The function below builds a frame within the gui so that the user can set strategies for the traders'''
    def show_trader_strategies(self):
        trader_frame = tk.LabelFrame(self.root, text="Trader Strategies")
        trader_frame.grid(row=2, column=3, sticky=tk.W +
                                        tk.E + tk.N + tk.S, padx=15, pady=4)
        # creates a button to explain strategies to user
        strat_info_button = tk.Button(trader_frame, text="Strategy Descriptions", width=16, command=self.strategy_message)
        strat_info_button.grid(row=0, column=0)

        self.buyer_ids = [k for k in range(self.num_buyers)]  # will become list of tk.stringvar()
        self.seller_ids = [k for k in range(self.num_sellers)]  # will become list of tk.stringvar()

        # create labels to identify entries
        tk.Label(trader_frame, text="ID").grid(row=1, column=0)
        tk.Label(trader_frame, text="Strategy").grid(row=1, column=1)
        tk.Label(trader_frame, text="ID").grid(row=1, column=2)
        tk.Label(trader_frame, text="Strategy").grid(row=1, column=3)

        autofill_button = tk.Button(trader_frame, text="Auto-fill Strategies", width=16, command=self.auto_fill_strategies)
        autofill_button.grid(row=0, column=2)
        autofill_strategy = ttk.Combobox(trader_frame, values=self.strategies, textvariable=self.autofill_strat, state='readonly')
        autofill_strategy.grid(row=0, column=3)
        self.autofill_strat.set(self.strategies[0])
        # these create cascading entries for the user to pick trader strategies
        for i in range(self.num_buyers):
            buyer_num = "Buyer" + str(i)
            tk.Label(trader_frame, text=buyer_num).grid(row=i + 2, column=0)

        for i in range(self.num_buyers):
            self.buyer_ids[i] = tk.StringVar()
            ttk.Combobox(trader_frame, values=self.strategies, textvariable=self.buyer_ids[i], state='readonly').grid(row=i + 2, column=1)

            # creates a drop down menu to choose strategies

        for i in range(self.num_sellers):
            seller_num = "Seller" + str(i)
            tk.Label(trader_frame, text=seller_num).grid(row=i + 2, column=2)

        for i in range(self.num_sellers):
            self.seller_ids[i] = tk.StringVar()
            ttk.Combobox(trader_frame, values=self.strategies, textvariable=self.seller_ids[i], state='readonly').grid(row=i + 2, column=3)

            # creates a drop down menu to choose strategies

    '''The function below creates a frame within the gui so that the user can set period shocks'''
    def show_pshock_frame(self):
        # TODO add these shocks to rum_sim()
        pf = tk.LabelFrame(self.root, text="Period Shock Entries")
        pf.grid(row=2, column=1, sticky=tk.W +
                                        tk.E + tk.N + tk.S, padx=15, pady=4)

        if self.num_p_shocks == 0: return   # Nothing to show

        self.p_shock_ids = [k for k in range(self.num_p_shocks)]  # will become list of tk.stringvar()
        self.p_shock_files = [k for k in range(self.num_p_shocks)]  # will become list of tk.stringvar()

        tk.Label(pf, text="ID").grid(row=0, column=0)
        tk.Label(pf, text="Shock Data File").grid(row=0, column=2)
        tk.Label(pf, text="Period").grid(row=0, column=1)
        for i in range(self.num_p_shocks):
            pshock_num = "P Shock #" + str(i + 1)
            tk.Label(pf, text=pshock_num).grid(row=i+1, column=0)
        for i in range(self.num_p_shocks):
            self.p_shock_ids[i] = tk.StringVar()
            tk.Entry(pf, width=5, justify=tk.CENTER,
                     textvariable=self.p_shock_ids[i]).grid(row=i + 1, column=1)

        for i in range(self.num_p_shocks):
            self.p_shock_files[i] = tk.StringVar()
            # TODO needs to be a way to refresh combobox values if user creates new file in market_gui
            ttk.Combobox(pf, values=os.listdir(self.project_path), textvariable=self.p_shock_files[i], state='readonly').grid(row=i + 1, column=2)
            plot_button = tk.Button(pf, text="Show", width=4, command=self.on_show_clicked)
            plot_button.grid(row=i + 1, column=3)

    '''The function below creates a frame within the gui so that the user can set round shocks'''
    def show_rshock_frame(self):
        # TODO add these shocks to run_sim()
        rf = tk.LabelFrame(self.root, text="Round Shock Entries")
        rf.grid(row=2, column=2, sticky=tk.W +
                                        tk.E + tk.N + tk.S, padx=15, pady=4)
        if self.num_r_shocks == 0: return  # Nothing to show

        self.r_shock_ids = [k for k in range(self.num_r_shocks)]
        self.r_shock_files = [k for k in range(self.num_r_shocks)]

        tk.Label(rf, text="ID").grid(row=0, column=0)
        tk.Label(rf, text="Shock Data File").grid(row=0, column=2)
        tk.Label(rf, text="Round").grid(row=0, column=1)
        for i in range(self.num_r_shocks):
            rshock_num = "R Shock #" + str(i + 1)
            tk.Label(rf, text=rshock_num).grid(row=i + 1, column=0)
        for i in range(self.num_r_shocks):
            self.r_shock_ids[i] = tk.StringVar()
            tk.Entry(rf, width=5, justify=tk.CENTER,
                     textvariable=self.r_shock_ids[i]).grid(row=i + 1, column=1)

        for i in range(self.num_r_shocks):
            self.r_shock_files[i] = tk.StringVar()
            ttk.Combobox(rf, values=os.listdir(self.project_path), textvariable=self.r_shock_files[i], state='readonly').grid(row=i + 1, column=2)
            plot_button = tk.Button(rf, text="Show", width=4, command=self.on_show_clicked)
            plot_button.grid(row=i+1, column=3)

    def show_info_bar_parms(self):
        self.string_period_shocks.set(str(self.num_p_shocks))
        self.string_round_shocks.set(str(self.num_r_shocks))
        self.string_periods.set(str(self.num_periods))

    def open_file(self, event=None):
        input_file_name = tkinter.filedialog.askopenfilename(defaultextension=".csv",
                                                             filetypes=[("All Files", "*.*"),
                                                                        ("Text Documents", "*.txt")])  # accepts chosen file
        if input_file_name:
            self.file_name = input_file_name
            self.name = os.path.basename(self.file_name)
            index = self.name.find(".")   # look for start of .csv
            self.name = self.name[:index]  # pulls file from directory/file path
            self.root.title('{}'.format(self.name))
            self.sec.load_file(self.file_name)

    def save(self, event=None):  # saves file in it's path location
        """We want to create a messagebox that asks if the user wants to overwrite the current file"""

    def display_about_messagebox(self, event=None):
        # displays about message
        tkinter.messagebox.showinfo("About", "{}{}".format(self.name,
                                                           "\n\nCenter for the Study of Neuroeconomics\n\n Created by Alex Payne"))

    def display_help_messagebox(self, event=None):  # displays help messages when message link clicked
        help_msg = "Quick Help: \n\n"
        help_msg += "   Getting Started \n"
        help_msg += "      1). Enter unique session name \n"
        help_msg += "      2). Enter number of period shocks \n"
        help_msg += "      3). Enter number of round shocks \n"
        help_msg += "      4). Enter starting data set \n"
        help_msg += "      5). Click Set Button \n"
        help_msg += "          a). Say yes to message box \n"
        help_msg += "          b). Period and round shock entries displayed \n"
        tkinter.messagebox.showinfo("Help", help_msg)

    '''The function below activates when the user clicks the Run button within the gui...
    ... it checks for errors in the secondary parameters such as trader strategies, round shocks, and period shocks
    ... also creates data folder for all simulation results to be stored in'''
    def check_sim(self):
        # code below checks that the user has set all trading strategies
        self.strategy_trigger = False  # alarm off
        for i in range(self.num_buyers):
            if self.buyer_ids[i].get() == "":
                self.strategy_trigger = True  # alarm triggered

        for i in range(self.num_sellers):
            if self.seller_ids[i].get() == "":
                self.strategy_trigger = True  # alarm triggered

        if self.strategy_trigger == False:
            # the error checks below will look to see if trader replace is enabled...
            # ... if they are: will check to make sure shift strategy is set
            # ... if not: will check to make sure period and round shocks ids and files are set
            self.r_shock_trigger = False  # sets alarms for p shocks and r shocks
            for i in range(self.num_r_shocks):  # look through round shock entries
                if self.r_shock_ids[i].get() == "":  # if any entries blank
                    self.r_shock_trigger = True  # trigger alarm

                elif self.r_shock_files[i].get() == "":  # if blank
                    self.r_shock_trigger = True  # trigger alarm

                else:
                    try:
                        int(self.r_shock_ids[i].get())  # check if integer
                        if int(self.r_shock_ids[i].get()) > int(self.string_rounds.get()):  # round to shock <= rounds
                            self.r_shock_trigger = True
                    except ValueError:  # value error raised if not
                        self.r_shock_trigger = True  # triggers parameter alarm
                        raise
                    else:
                        pass  # else pass to next variable check

            if self.r_shock_trigger == False:
                self.p_shock_trigger = False
                for i in range(self.num_p_shocks):
                    if self.p_shock_ids[i].get() == "":
                        self.p_shock_trigger = True

                    elif self.p_shock_files[i].get() == "":
                        self.p_shock_trigger = True

                    else:
                        try:
                            int(self.r_shock_ids[i].get())  # check if integer
                            if self.p_shock_ids[i].get() > self.string_periods.get():
                                self.p_shock_trigger = True
                        except ValueError:  # value error raised if not
                            self.p_shock_trigger = True  # triggers parameter alarm
                            raise
                        else:
                            pass  # else pass to next variable check

                if self.p_shock_trigger == False:
                    self.replace_trader_trigger = False  # set alarm
                    if self.trader_replace.get() == 1:  # if trader replacement enabled after clicking the Set button
                        if self.buyer_replace_strategy.get() == "":  # if blank
                            self.replace_trader_trigger = True  # trigger trader replace error
                            tk.messagebox.askokcancel("TRADER REPLACE ERROR",
                                                      "New Buyer Strategy has not been set \n --> Please check")
                        elif self.seller_replace_strategy.get() == "":  # if blank
                            self.replace_trader_trigger = True  # trigger trader replace error
                            tk.messagebox.askokcancel("TRADER REPLACE ERROR",
                                                      "New Seller Strategy has not been set \n --> Please check")
                        else:
                            pass  # else no errors found

                    else:
                        pass

                    if self.replace_trader_trigger == False:  # if no blanks in trader strategies
                        time_stamp = time.strftime("_h%Im%Ms%S", time.localtime())
                        session_name = self.string_session_name.get() + str(time_stamp)
                        os.makedirs(self.output_path + session_name)
                        self.run_sim(session_name)
                    else:
                        pass  # else hold for trader replacement fixes or cancellation from user
                else:
                    tk.messagebox.askokcancel("PERIOD SHOCK ERROR",
                                              "Error found in period shocks, please check the below requirements: \n"
                                              "--> period to shock is set \n"
                                              "--> period to shock is a whole number \n"
                                              "--> period shock data file is set \n"
                                              "To cancel these shocks, set Period Shocks to 0 and re-click the Set button")
                    pass  # else hold for period shock fixes or cancellation from user
            else:
                tk.messagebox.askokcancel("ROUND SHOCK ERROR",
                                          "Error found in round shocks, please check the below requirements: \n"
                                          "--> round to shock is set \n"
                                          "--> round to shock is a whole number \n"
                                          "--> round shock data file is set \n"
                                          "To cancel these shocks, set Round Shocks to 0 and re-click the Set button")
                pass  # else hold for round shock fixes or cancellation from user
        else:
            tk.messagebox.askokcancel("TRADER STRATEGIES ERROR",
                                      "Error found in trader strategies, please check the below requirements: \n"
                                      "--> each buyer has a strategy set \n"
                                      "--> each seller has a strategy set \n")
            pass  # else hold for trader strategy fixes from user

    '''This function will execute if no errors found in check_sim()...
    ... will create a new window to display simulation results to the user
    ... will also save all simulation results to a data folder'''
    def run_sim(self, session_name):
        '''Adding in a new window to run simulations in market_gui'''

        run_root = tk.Toplevel()  # creates top level root on main root
        # finds the dimension of user's computer screen and sizes root to fit
        run_root.geometry(str(GetSystemMetrics(0)) + "x" + str(GetSystemMetrics(1)))

        # This obtains the strategies that the user sets in market_gui
        # returns a list of the trader's strategies
        strat_list = []
        for i in range(len(self.buyer_ids)):
            trader = self.buyer_ids[i].get()
            strat_list.append(trader)
        for i in range(len(self.seller_ids)):
            trader = self.seller_ids[i].get()
            strat_list.append(trader)

        p_shock_list = []
        r_shock_list = []
        for i in range(len(self.p_shock_ids)):
            p_shock_list.append([int(self.p_shock_ids[i].get()), self.p_shock_files[i].get()])
        for i in range(len(self.r_shock_ids)):
            r_shock_list.append([int(self.r_shock_ids[i].get()), self.r_shock_files[i].get()])

        print("Trader Strategies: " + str(strat_list))
        print("Length: " + str(len(strat_list)))
        print("\n")
        print("Period Shocks: " + str(p_shock_list))
        print("Round Shocks: " + str(r_shock_list))
        print("\n")
        eff = []
        periods_list = []
        act_surplus = []
        maxi_surplus = []
        session = session_name

        num_periods = int(self.string_periods.get())  # periods or trading days
        limits = (int(self.price_ceiling.get()), int(self.price_floor.get()))  # price ceiling, price floor
        rounds = int(self.string_rounds.get())  # rounds in each period (can substitute time clock)
        name = "trial"
        period = 0  # ...??
        '''The code below creates a file for your session name for market run info to be dumped into...
        ... will raise file error if session name not changed --> prevents overwriting previous runs'''

        smp = Simulator.spot_market_period.SpotMarketPeriod(session, num_periods, limits)

        # --> could put this in json data file
        trader_names = strat_list

        smp.init_spot_system_gui(name, limits, rounds, self.project_path, self.string_data.get(), self.output_path,
                                session)
        rnd_traders = trader_names  # because shuffle shuffles the list in place, returns none
        times = []
        for k in range(int(self.string_periods.get())):  # iterates through number of periods or "trading days"
            timer_start = timer()
            periods_list.append(k)
            # random.shuffle(rnd_traders)  # shuffles trader order per period  # TODO random shuffle or no?
            smp.init_traders(rnd_traders, k)
            print("**** Running Period {}".format(k))  # provides visual effect in editor
            smp.run_period(period, session, self.trader_replace.get(),
                           self.buyer_replace_strategy.get(), self.seller_replace_strategy.get(),
                           self.num_buyers, self.num_sellers)  # runs period
            timer_stop = timer()  # ends period timer
            results = smp.eval()  # evaluation function called --> returns data
            '''the below data is appended into global dictionaries'''
            eff.append(results[8])  # appends the efficiencies per period
            act_surplus.append(results[7])  # appends actual surplus per period
            maxi_surplus.append(results[6])  # appends maximum surplus per period
            smp.get_contracts()  # gets transaction prices and period endpoints
            session_folder = self.output_path + session + "\\"  # establishes file path for session data folder
            smp.record_session_data(session_folder)  # records session data in excel csv
            time = timer_stop - timer_start  # period execution time
            times.append(time)  # list of execution times --> sum=total execution time

        # TODO add the tk.label information below to a json file so information can be saved to period data

        # creates frame to display parameters that were set before running the simulation
        info_frame = tk.LabelFrame(run_root, text="Parameters", font='bold')
        info_frame.grid(row=1, column=0)
        tk.Label(info_frame, text="Session Name: " + str(session)).grid(row=0, column=0)
        tk.Label(info_frame, text="Data Used: " + str(self.string_data.get())).grid(row=1, column=0)
        tk.Label(info_frame, text="Execution Time: " + str(sum(times))).grid(row=2, column=0)
        tk.Label(info_frame, text="Buyers: " + str(self.num_buyers) + "   " + "Sellers: " + str(self.num_sellers)).grid(row=3, column=0)
        tk.Label(info_frame, text="Periods: " + str(self.num_periods) + "   " + "Rounds: " + str(self.num_rounds)).grid(row=4, column=0)
        tk.Label(info_frame, text="Units: " + str(self.num_units)).grid(row=5, column=0)

        # creates a frame to display instant shocks if enabled
        if self.trader_replace.get() == 1:
            move_column = 2  # will change column position of next tk.labelframe
            trader_replace_frame = tk.LabelFrame(run_root, text="Trader Replacement", font='bold')
            trader_replace_frame.grid(row=1, column=1)
            tk.Label(trader_replace_frame, text="New Buyer Strategy: " + str(self.buyer_replace_strategy.get())).grid(row=1, column=0)
            tk.Label(trader_replace_frame, text="New Seller Strategy: " + str(self.seller_replace_strategy.get())).grid(row=3, column=0)
        else:
            move_column = 1  # if no instant shocks then next tk.labelframe column =1
            pass

        # creates a frame for simulation results
        run_frame = tk.LabelFrame(run_root, text="Simulation Results", font='bold')
        run_frame.grid(row=1, column=move_column)  # set parameters
        tk.Label(run_frame, text="Maximum Surpluses: " + str(maxi_surplus)).grid(row=0, column=0)
        tk.Label(run_frame, text="Actual Surpluses: " + str(act_surplus)).grid(row=1, column=0)
        tk.Label(run_frame, text="Avg. Market Surplus: " + str(sum(act_surplus)/len(act_surplus))).grid(row=2, column=0)
        tk.Label(run_frame, text="\nMarket Efficiencies: " + str(eff)).grid(row=3, column=0)
        tk.Label(run_frame, text="Avg. Efficiency: " + str(sum(eff) / num_periods)).grid(row=4, column=0)

        # prints to editor
        print("Period Times: " + str(times))
        print("Market Efficiencies:" + str(eff))  # print market efficiencies
        print("Avg. Efficiency:" + str(sum(eff) / num_periods))  # print avg efficiency
        print("Actual Surpluses:" + str(act_surplus))  # print actual surpluses
        print("Maximum Surpluses:" + str(maxi_surplus))  # print max surpluses
        print()

        # TODO trader info below can be functionalized further
        # builds frame for trader total earnings
        trader_frame1 = tk.LabelFrame(run_root, text="Strategy Total Earnings (per period)", font='bold')
        trader_frame1.grid(row=1, column=2)  # set parameters
        tk.Label(trader_frame1, text="Trader_AA: " + str(smp.total_earns('AA'))).grid(row=0, column=0)
        tk.Label(trader_frame1, text="Trader_GD: " + str(smp.total_earns('GD'))).grid(row=1, column=0)
        tk.Label(trader_frame1, text="Trader_PS: " + str(smp.total_earns('PS'))).grid(row=2, column=0)
        tk.Label(trader_frame1, text="Trader_ZIP: " + str(smp.total_earns('ZIP'))).grid(row=3, column=0)
        tk.Label(trader_frame1, text="Trader_Kaplan: " + str(smp.total_earns('KP'))).grid(row=4, column=0)
        tk.Label(trader_frame1, text="Trader_Shaver: " + str(smp.total_earns('SI'))).grid(row=5, column=0)
        tk.Label(trader_frame1, text="Trader_ZIC: " + str(smp.total_earns('ZIC'))).grid(row=6, column=0)

        # print to editor
        print("Strategy Total Earnings")
        print("Trader_AA: " + str(smp.total_earns('AA')))
        print("Trader_GD: " + str(smp.total_earns('GD')))  #
        print("Trader_PS: " + str(smp.total_earns('PS')))  # ADDED: section to list total avg earns
        print("Trader_ZIP: " + str(smp.total_earns('ZIP')))  #
        print("Trader_ZIC: " + str(smp.total_earns('ZIC')))  #
        print("Trader_Kaplan: " + str(smp.total_earns('KP')))
        print("Trader_Shaver: " + str(smp.total_earns('SI')))
        print()

        # builds frame for trader avg earnings
        trader_frame2 = tk.LabelFrame(run_root, text="Strategy Total Avg. Earnings (per trader)", font='bold')
        trader_frame2.grid(row=1, column=3)  # set parameters
        tk.Label(trader_frame2, text="Trader_AA: " + str(smp.total_avg_earns('AA', trader_names.count('Trader_AA') * num_periods))).grid(row=0, column=0)
        tk.Label(trader_frame2, text="Trader_GD: " + str(smp.total_avg_earns('GD', trader_names.count('Trader_GD') * num_periods))).grid(row=1, column=0)
        tk.Label(trader_frame2, text="Trader_PS: " + str(
            smp.total_avg_earns('PS', trader_names.count('Trader_PS') * num_periods))).grid(row=2, column=0)
        tk.Label(trader_frame2, text="Trader_ZIP: " + str(smp.total_avg_earns('ZIP', trader_names.count('Trader_ZIP') * num_periods))).grid(row=3, column=0)
        tk.Label(trader_frame2, text="Trader_Kaplan: " + str(smp.total_avg_earns('KP', trader_names.count('Trader_Kaplan') * num_periods))).grid(row=4, column=0)
        tk.Label(trader_frame2, text="Trader_Shaver: " + str(smp.total_avg_earns('SI', trader_names.count('Trader_Shaver') * num_periods))).grid(row=5, column=0)
        tk.Label(trader_frame2, text="Trader_ZIC: " + str(smp.total_avg_earns('ZIC', trader_names.count('Trader_ZIC') * num_periods))).grid(row=6, column=0)

        # prints to editor
        print("Strategy Total Avg. Earnings (per trader)")
        print("Trader_AA: " + str(smp.total_avg_earns('AA', trader_names.count('Trader_AA') * num_periods)))  #
        print("Trader_GD: " + str(smp.total_avg_earns('GD', trader_names.count('Trader_GD') * num_periods)))  #
        print("Trader_PS: " + str(
            smp.total_avg_earns('PS', trader_names.count('Trader_PS') * num_periods)))  # ADDED: section to list total avg earns
        print("Trader_ZIP: " + str(smp.total_avg_earns('ZIP', trader_names.count('Trader_ZIP') * num_periods)))  #
        print("Trader_ZIC: " + str(smp.total_avg_earns('ZIC', trader_names.count('Trader_ZIC') * num_periods)))  #
        print("Trader_Kaplan: " + str(smp.total_avg_earns('KP', trader_names.count('Trader_Kaplan') * num_periods)))
        print("Trader_Shaver: " + str(smp.total_avg_earns('SI', trader_names.count('Trader_Shaver') * num_periods)))

        """The below function calls create graphs from the simulation results and save in project path"""
        smp.get_avg_trade_ratio()  # prints avg trade ratio for all periods
        smp.graph_trader_eff(self.output_path, session)  # plots individual efficiency
        smp.graph_efficiency_gui(self.output_path, session, eff, periods_list)  # plots period efficiency
        smp.get_endpoints()  # obtains endpoints of periods for graph
        smp.graph_contracts(self.output_path, session)  # graphs contract transactions and avg transaction per period
        # smp.graph_surplus()  # graphs actual and max surplus
        smp.graph_alphas(self.output_path, session, periods_list)  # graphs Smith's Alpha of convergence
        smp.graph_distribution(self.output_path, session)  # graphs normal distribution of trader efficiencies

        from PIL import Image  # import python image library
        new_size = 400, 225  # makes original image smaller
        temp_folder = self.output_path + session + "\\" + "Mini Images"
        os.makedirs(temp_folder)  # makes a temp folder to store mini images

        # below builds a frame to display the smaller transactions graph
        graph_frame1 = tk.LabelFrame(run_root, text="Transactions", font='bold')  # frame created in run root
        graph_frame1.grid(row=2, column=1)
        edit = Image.open(self.output_path + session + '\\' + "Transactions.png")  # accesses original image
        edit.thumbnail(new_size, Image.ANTIALIAS)  # turns the image into a thumbnail size
        edit.save(temp_folder + '\\' + "Transactions Mini.png")  # saves new file
        photo = tk.PhotoImage(file=temp_folder + '\\' + "Transactions Mini.png")  # accesses new image
        label = tk.Label(graph_frame1, image=photo)  # turns into tk label and packs into frame
        label.pack()

        # below builds a frame to display the smaller SD graph
        sd_frame = tk.LabelFrame(run_root, text="Supply and Demand", font='bold')  # new frame in run root
        sd_frame.grid(row=2, column=0)
        edit2 = Image.open(self.output_path + session + '\\' + "SD Before.png") # old image accessed
        edit2.thumbnail(new_size, Image.ANTIALIAS)  # old image turned into thumbnail
        edit2.save(temp_folder + '\\' + "SD Mini.png")  # new image saved
        photo2 = tk.PhotoImage(file=temp_folder + '\\' + "SD Mini.png")  # new image accessed
        label2 = tk.Label(sd_frame, image=photo2)  # turned into label and packed into frame
        label2.pack()

        # below builds a frame to display the distribution of trader efficiencies
        stat_frame = tk.LabelFrame(run_root, text="Trader Efficiency Distribution", font='bold')  # new frame in run root
        stat_frame.grid(row=2, column=2)
        edit3 = Image.open(self.output_path + session + '\\' + "Efficiency Distribution.png")  # old image accessed
        edit3.thumbnail(new_size, Image.ANTIALIAS)  # old image turned into thumbnail
        edit3.save(temp_folder + '\\' + "Eff Dist Mini.png")  # new image saved
        photo3 = tk.PhotoImage(file=temp_folder + '\\' + "Eff Dist Mini.png")  # new image accessed
        label3 = tk.Label(stat_frame, image=photo3)  # turned into label and packed into frame
        label3.pack()

        t_eff_frame = tk.LabelFrame(run_root, text="Efficiency by Trader", font='bold')
        t_eff_frame.grid(row=3, column=2)
        edit4 = Image.open(self.output_path + session + "\\" + "Trader Efficiencies.png")
        edit4.thumbnail(new_size, Image.ANTIALIAS)
        edit4.save(temp_folder + "\\" + "T Eff Mini.png")
        photo4 = tk.PhotoImage(file=temp_folder + "\\" + "T Eff Mini.png")
        label4 = tk.Label(t_eff_frame, image=photo4)
        label4.pack()

        p_eff_frame = tk.LabelFrame(run_root, text="Efficiency by Period", font='bold')
        p_eff_frame.grid(row=3, column=1)
        edit5 = Image.open(self.output_path + session + "\\" + "Period Efficiencies.png")
        edit5.thumbnail(new_size, Image.ANTIALIAS)
        edit5.save(temp_folder + "\\" + "P Eff Mini.png")
        photo5 = tk.PhotoImage(file=temp_folder + "\\" + "P Eff Mini.png")
        label5 = tk.Label(p_eff_frame, image=photo5)
        label5.pack()

        alpha_frame = tk.LabelFrame(run_root, text="Convergence Alphas", font='bold')
        alpha_frame.grid(row=3, column=0)
        edit6 = Image.open(self.output_path + session + "\\" + "Convergence Alphas.png")
        edit6.thumbnail(new_size, Image.ANTIALIAS)
        edit6.save(temp_folder + "\\" + "Alphas Mini.png")
        photo6 = tk.PhotoImage(file=temp_folder + "\\" + "Alphas Mini.png")
        label6 = tk.Label(alpha_frame, image=photo6)
        label6.pack()

        import shutil  # allows removal of non-empty directory
        shutil.rmtree(temp_folder)  # remove temp folder of resized images... no longer needed
        # below creates a statistics frame to display next to trader efficiency distribution graph
        stat_frame = tk.LabelFrame(run_root, text="Trader Efficiency Statistics")
        stat_frame.grid(row=2, column=3)

        t_effs = smp.trader_eff_gui()  # list of trader efficiencies
        mean = np.mean(t_effs)  # numpy function to get average
        std_dev = np.std(t_effs)  # numpy function to get standard deviation
        median = np.median(t_effs)  # numpy function to get median
        max = np.max(t_effs)  # numpy function to get maximum value
        min = np.min(t_effs)  # numpy function to get minimum value

        for i in range(t_effs.count(0)):  # removes traders with 0 efficiency
            t_effs.remove(0)
        mean2 = np.mean(t_effs)  # numpy function to get average
        std_dev2 = np.std(t_effs)  # numpy function to get standard deviation
        median2 = np.median(t_effs)  # numpy function to get median
        max2 = np.max(t_effs)  # numpy function to get maximum value
        min2 = np.min(t_effs)  # numpy function to get minimum value

        tk.Label(stat_frame, text="All Traders", font='bold').grid(row=0, column=0)
        tk.Label(stat_frame, text="Mean: {}   Std Dev: {}".format(mean, std_dev)).grid(row=1, column=0)
        tk.Label(stat_frame, text="Median: {}   Max: {}   Min: {}".format(median, max, min)).grid(row=2, column=0)
        tk.Label(stat_frame, text="").grid(row=3, column=0)
        tk.Label(stat_frame, text="Out of Market Traders Removed", font='bold').grid(row=4, column=0)
        tk.Label(stat_frame, text="Mean: {}   Std Dev: {}".format(mean2, std_dev2)).grid(row=5, column=0)
        tk.Label(stat_frame, text="Median: {}   Max: {}   Min: {}".format(median2, max2, min2)).grid(row=6, column=0)

        eff.clear()
        strat_list.clear()
        periods_list.clear()  # not sure if clearing all these does anything...
        maxi_surplus.clear()
        act_surplus.clear()

        run_root.mainloop()  # continues running run root

if __name__ == "__main__":
    # setup gui
    # TODO can add this into mkt_root somehow...
    class file_path():
        def __init__(self, path_root):
            self.path_root = path_root
            self.project_path = None
            self.output_path = None
            self.path_frame = tk.LabelFrame(self.path_root, text="SET FILE PATHS")
            self.path_frame.grid(row=1, column=0, columnspan=3)
            tk.Label(self.path_frame, text="Project Path: ").grid(row=1, column=0)
            tk.Button(self.path_frame, text="Link Path", command=self.project_set_button).grid(row=1, column=2)

            tk.Label(self.path_frame, text="Simulation Save Path: ").grid(row=2, column=0)
            tk.Button(self.path_frame, text="Link Path", command=self.sim_path_set_button).grid(row=2, column=2)

            tk.Button(self.path_frame, text="Submit", command=self.submit_button).grid(row=3, column=0)
            self.path_root.mainloop()

        def project_set_button(self):
            chosen_file = tk.filedialog.askdirectory()
            self.project_path = chosen_file
            tk.Label(self.path_frame, text=self.project_path).grid(row=1, column=1)

        def sim_path_set_button(self):
            sim_save_file = tk.filedialog.askdirectory()
            self.output_path = sim_save_file
            tk.Label(self.path_frame, text=self.output_path).grid(row=2, column=1)

        def submit_button(self):
            if self.project_path == None:
                tk.messagebox.askokcancel("ERROR", "Project path has not been set \n Please set this file path")
            elif self.output_path == None:
                tk.messagebox.askokcancel("ERROR", "Sim Save Path has not been set \n Please set this file path")
            else:
                box = tk.messagebox.askyesno("File Check", "Have you set the right file paths?")
                if box == True:
                    self.path_root.destroy()
                else:
                    print("Continuing")

        def return_paths(self):
            return self.project_path, self.output_path

    path_root = tk.Tk()
    results = file_path(path_root)
    final_project_path = results.return_paths()[0]
    final_output_path = results.return_paths()[1]
    mkt_root = tk.Tk()
    debug_test = True
    if debug_test:
        print("In Gui -> START")
        print("---> Project Path: " + str(final_project_path))
        print("---> Output Path: " + str(final_output_path))
        sec = Environment.spot_environment_controller.SpotEnvironmentController(debug_test)
        gui = MarketGui(mkt_root, sec, final_project_path, final_output_path, "Data Entry", debug_test)
        mkt_root.mainloop()
    if debug_test:
        print("In Gui -> END")


while not isinstance(env, gym.wrappers.Monitor):  # and not isinstance()
        if not isinstance(env, gym.Wrapper):
            assert False
        env = env.env

from utils import *

# Shared utilities

def compute_cumulative_returns(rewards, baselines, discount):
    # This method builds up the cumulative sum of discounted rewards for each time step:
    # Note that we use γ^(t'-t) instead of γ^t'. This gives us a biased gradient but lower variance
    returns = []
    # Use the last baseline prediction to back up
    cum_return = baselines[-1]
    for reward in rewards[::-1]:
        cum_return = cum_return * discount + reward
        returns.append(cum_return)
    return returns[::-1]

def compute_advantages(rewards, baselines, discount, gae_lambda):
    # Given returns R_t and baselines b(s_t), compute (generalized) advantage estimate A_t
    deltas = rewards + discount * baselines[1:] - baselines[:-1]
    advs = []
    cum_adv = 0
    multiplier = discount * gae_lambda
    for delta in deltas[::-1]:
        cum_adv = cum_adv * multiplier + delta
        advs.append(cum_adv)
    return advs[::-1]

def compute_pg_vars(trajs, policy, baseline, discount, gae_lambda):
    """
    Compute chainer variables needed for various policy gradient algorithms
    """
    for traj in trajs:
        # Include the last observation here, in case the trajectory is not finished
        baselines = baseline.predict(np.concatenate(
            [traj["observations"], [traj["last_observation"]]]))
        if traj['finished']:
            # If already finished, the future cumulative rewards starting from the final state is 0
            baselines[-1] = 0.
        # This is useful when fitting baselines. It uses the baseline prediction of the last state value to perform
        # Bellman backup if the trajectory is not finished.
        traj['returns'] = compute_cumulative_returns(
            traj['rewards'], baselines, discount)
        traj['advantages'] = compute_advantages(
            traj['rewards'], baselines, discount, gae_lambda)
        traj['baselines'] = baselines[:-1]

    # First, we compute a flattened list of observations, actions, and advantages
    all_obs = np.concatenate([traj['observations'] for traj in trajs], axis=0)
    all_acts = np.concatenate([traj['actions'] for traj in trajs], axis=0)
    all_advs = np.concatenate([traj['advantages'] for traj in trajs], axis=0)
    all_dists = {
        k: np.concatenate([traj['distributions'][k] for traj in trajs], axis=0)
        for k in trajs[0]['distributions'].keys()
    }

    # Normalizing the advantage values can make the algorithm more robust to reward scaling
    all_advs = (all_advs - np.mean(all_advs)) / (np.std(all_advs) + 1e-8)

    # Form chainer variables
    all_obs = Variable(all_obs)
    all_acts = Variable(all_acts)
    all_advs = Variable(all_advs.astype(np.float32, copy=False))
    all_dists = policy.distribution.from_dict(
        {k: Variable(v) for k, v in all_dists.items()})

    return all_obs, all_acts, all_advs, all_dists

# Helper methods for logging

def log_reward_statistics(env):
    # keep unwrapping until we get the monitor

    # env.unwrapped
    assert isinstance(env, gym.wrappers.Monitor)
    all_stats = None
    for _ in range(10):
        try:
            all_stats = gym.wrappers.monitoring.load_results(env.directory)
        except FileNotFoundError:
            time.sleep(1)
            continue
    if all_stats is not None:
        episode_rewards = all_stats['episode_rewards']
        episode_lengths = all_stats['episode_lengths']

        recent_episode_rewards = episode_rewards[-100:]
        recent_episode_lengths = episode_lengths[-100:]

        if len(recent_episode_rewards) > 0:
            logger.logkv('AverageReturn', np.mean(recent_episode_rewards))
            logger.logkv('MinReturn', np.min(recent_episode_rewards))
            logger.logkv('MaxReturn', np.max(recent_episode_rewards))
            logger.logkv('StdReturn', np.std(recent_episode_rewards))
            logger.logkv('AverageEpisodeLength',
                         np.mean(recent_episode_lengths))
            logger.logkv('MinEpisodeLength', np.min(recent_episode_lengths))
            logger.logkv('MaxEpisodeLength', np.max(recent_episode_lengths))
            logger.logkv('StdEpisodeLength', np.std(recent_episode_lengths))

        logger.logkv('TotalNEpisodes', len(episode_rewards))
        logger.logkv('TotalNSamples', np.sum(episode_lengths))

def log_baseline_statistics(trajs):
    # Specifically, compute the explained variance, defined as
    baselines = np.concatenate([traj['baselines'] for traj in trajs])
    returns = np.concatenate([traj['returns'] for traj in trajs])
    logger.logkv('ExplainedVariance',
                 explained_variance_1d(baselines, returns))

def log_action_distribution_statistics(dists):
    with chainer.no_backprop_mode():
        entropy = F.mean(dists.entropy()).data
        logger.logkv('Entropy', entropy)
        logger.logkv('Perplexity', np.exp(entropy))
        if isinstance(dists, Gaussian):
            logger.logkv('AveragePolicyStd', F.mean(
                F.exp(dists.log_stds)).data)
            for idx in range(dists.log_stds.shape[-1]):
                logger.logkv('AveragePolicyStd[{}]'.format(
                    idx), F.mean(F.exp(dists.log_stds[..., idx])).data)
        elif isinstance(dists, Categorical):
            probs = F.mean(F.softmax(dists.logits), axis=0).data
            for idx in range(len(probs)):
                logger.logkv('AveragePolicyProb[{}]'.format(idx), probs[idx])


return c.run("which {}".format(name), hide=True, warn=True)

"""
Shell environment introspection, e.g. binaries in effective $PATH, etc.
"""

def have_program(c, name):
    """
    Returns whether connected user has program ``name`` in their ``$PATH``.
    """


		return {
			"instances": [instance.to_json() for instance in self.instances],
			"multiInstances": [multi_instance.to_json() for multi_instance in self.multi_instances]
		}

from __future__ import annotations

from typing import Callable, Sequence

from typing_extensions import TypedDict

from ..utils import basic_repr
from .instance import Instance, InstanceJson
from .multi_instance import MultiInstance, MultiInstanceJson

__pdoc__ = { "ClassAnnotation.__add__": True }

class ClassAnnotationJson(TypedDict, total = False):
	"""
	The serialized JSON representation of a class annotation.
	"""
	instances: Sequence[InstanceJson]
	multiInstances: Sequence[MultiInstanceJson]

class ClassAnnotation:
	"""
	A `ClassAnnotation` represents the set of detections for a given
	class. These may either be individual instances, or "multi instances"
	that describe a visual clustering of the class.
	"""

	instances: Sequence[Instance]
	"""
	A sequence of individual instances of this class.
	"""

	multi_instances: Sequence[MultiInstance]
	"""
	A sequence of multi-instances of this class. An example of a
	multi instance would be a crowd of people (labeled as such).
	"""

	@staticmethod
	def from_json(json: ClassAnnotationJson) -> ClassAnnotation:
		"""
		Constructs a `ClassAnnotation` from a `ClassAnnotationJson`.
		"""
		return ClassAnnotation(
			instances = [Instance.from_json(instance) for instance in json["instances"]] if "instances" in json else [],
			multi_instances = [MultiInstance.from_json(multi_instance) for multi_instance in json["multiInstances"]] if "multiInstances" in json else []
		)

	def __init__(self, *, instances: Sequence[Instance], multi_instances: Sequence[MultiInstance] = []):
		self.instances = instances
		self.multi_instances = multi_instances

	def filter_detections(
		self,
		*,
		instance_filter: Callable[[Instance], bool],
		multi_instance_filter: Callable[[MultiInstance], bool]
	) -> ClassAnnotation:
		"""
		Returns a new class annotation consisting only of the instances and
		multi-instances that meet the given constraints.
		"""
		return ClassAnnotation(
			instances = [
				instance
				for instance in self.instances
				if instance_filter(instance)
			],
			multi_instances = [
				multi_instance
				for multi_instance in self.multi_instances
				if multi_instance_filter(multi_instance)
			]
		)

	def __repr__(self) -> str:
		return basic_repr("ClassAnnotation", instances = self.instances, multi_instances = self.multi_instances)

	def __eq__(self, other: ClassAnnotation) -> bool:
		if not isinstance(other, ClassAnnotation): # type: ignore - pyright complains about the isinstance check being redundant
			return NotImplemented
		return self.instances == other.instances and self.multi_instances == other.multi_instances

	def __add__(self, other: ClassAnnotation) -> ClassAnnotation:
		if not isinstance(other, ClassAnnotation): # type: ignore - pyright complains about the isinstance check being redundant
			return NotImplemented

		instances = list(self.instances) + list(other.instances)
		multi_instances = list(self.multi_instances) + list(other.multi_instances)

		return ClassAnnotation(
			instances = instances,
			multi_instances = multi_instances,
		)

	def to_json(self) -> ClassAnnotationJson:
		"""
		Serializes this `ClassAnnotation` into a `ClassAnnotationJson`.
		"""


while i <10:
    if i % 2 == 0:
        print ("第 %d 行"%i,end = '\t')
        for j in range (1,i+1):
            print('{}*{}={}'.format (i,j,i*j),end = '\t')
    else:
        print()
    i +=1

#day4 2019年7月10日 23点36分
#author:Taskow

print ("九九乘法表")

for i in range (1,10):
    print ('第{}列'.format(i),end = '\t')
    for j in range (1,i+1):
        print ('{}*{}={}'.format (i,j,i*j ), end= '\t')
        #vserion1.0 出现的问题：
        #上面程序运行后不会自动回车，不是紧跟tab效果。
        '''解决办法：
           end = '\t'
        '''
    print()

#仅打印奇数行的话，
#就ok

#问：仅打印偶数列如何解决？
'''方法1：
        for i in range (2,10,2)
        完美解决！
    方法2：
        利用教学视频中的方法，代码如下：
'''
print('跳过奇数列的九九乘法表--方法2')
for i in range (1,10):
    if i % 2 == 0:
        print ("第 %d 行"%i,end = '\t')
        for j in range (1,i+1):
            print('{}*{}={}'.format (i,j,i*j),end = '\t')
    else:
        print()

#或者方法3：

print('跳过奇数列的九九乘法表--方法3')
i = 1


    terminal.log.config(verbose=verbose)
    terminal.log.info('this is a info message')
    terminal.log.verbose.info('this is a verbose message')

import terminal

class MyCommand(terminal.Command):

    def print_title(self, title):
        if 'Option' in title:
            print(terminal.magenta(title))
        elif 'Command' in title:
            print(terminal.green(title))
        return self

program = MyCommand('terminal')

# add actions

subcommand = terminal.Command('build', 'build the site')
program.action(subcommand)

@program.action
def log(verbose=False):
    """
    print a log test

    :param verbose: show more logs
    """

# add options
program.option('-f, --force', 'force to do something')
program.option('-o, --output <output>', 'output directory')

program.parse()

if program.output:
    print('output is %s' % program.output)


        self._metadata = MetadataCatalog.get(dataset_name)
        self._cpu_device = torch.device("cpu")
        self._logger = logging.getLogger(__name__)

import glob
import logging
import os
import tempfile
from collections import OrderedDict
import torch
from fvcore.common.file_io import PathManager
from PIL import Image

from detectron2.data import MetadataCatalog
from detectron2.utils import comm

from .evaluator import DatasetEvaluator

class CityscapesEvaluator(DatasetEvaluator):
    """
    Evaluate instance segmentation results using cityscapes API.

    Note:
        * It does not work in multi-machine distributed training.
        * It contains a synchronization, therefore has to be used on all ranks.
        * Only the main process runs evaluation.
    """

    def __init__(self, dataset_name):
        """
        Args:
            dataset_name (str): the name of the dataset.
                It must have the following metadata associated with it:
                "thing_classes", "gt_dir".
        """

    def reset(self):
        self._working_dir = tempfile.TemporaryDirectory(prefix="cityscapes_eval_")
        self._temp_dir = self._working_dir.name
        # All workers will write to the same results directory
        # TODO this does not work in distributed training
        self._temp_dir = comm.all_gather(self._temp_dir)[0]
        if self._temp_dir != self._working_dir.name:
            self._working_dir.cleanup()
        self._logger.info(
            "Writing cityscapes results to temporary directory {} ...".format(self._temp_dir)
        )

    def process(self, inputs, outputs):
        from cityscapesscripts.helpers.labels import name2label

        for input, output in zip(inputs, outputs):
            file_name = input["file_name"]
            basename = os.path.splitext(os.path.basename(file_name))[0]
            pred_txt = os.path.join(self._temp_dir, basename + "_pred.txt")

            output = output["instances"].to(self._cpu_device)
            num_instances = len(output)
            with open(pred_txt, "w") as fout:
                for i in range(num_instances):
                    pred_class = output.pred_classes[i]
                    classes = self._metadata.thing_classes[pred_class]
                    class_id = name2label[classes].id
                    score = output.scores[i]
                    mask = output.pred_masks[i].numpy().astype("uint8")
                    png_filename = os.path.join(
                        self._temp_dir, basename + "_{}_{}.png".format(i, classes)
                    )

                    Image.fromarray(mask * 255).save(png_filename)
                    fout.write("{} {} {}\n".format(os.path.basename(png_filename), class_id, score))

    def evaluate(self):
        """
        Returns:
            dict: has a key "segm", whose value is a dict of "AP" and "AP50".
        """
        comm.synchronize()
        if comm.get_rank() > 0:
            return
        import cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling as cityscapes_eval

        self._logger.info("Evaluating results under {} ...".format(self._temp_dir))

        # set some global states in cityscapes evaluation API, before evaluating
        cityscapes_eval.args.predictionPath = os.path.abspath(self._temp_dir)
        cityscapes_eval.args.predictionWalk = None
        cityscapes_eval.args.JSONOutput = False
        cityscapes_eval.args.colorized = False
        cityscapes_eval.args.gtInstancesFile = os.path.join(self._temp_dir, "gtInstances.json")

        # These lines are adopted from
        gt_dir = PathManager.get_local_path(self._metadata.gt_dir)
        groundTruthImgList = glob.glob(os.path.join(gt_dir, "*", "*_gtFine_instanceIds.png"))
        assert len(
            groundTruthImgList
        ), "Cannot find any ground truth images to use for evaluation. Searched for: {}".format(
            cityscapes_eval.args.groundTruthSearch
        )
        predictionImgList = []
        for gt in groundTruthImgList:
            predictionImgList.append(cityscapes_eval.getPrediction(gt, cityscapes_eval.args))
        results = cityscapes_eval.evaluateImgLists(
            predictionImgList, groundTruthImgList, cityscapes_eval.args
        )["averages"]

        ret = OrderedDict()
        ret["segm"] = {"AP": results["allAp"] * 100, "AP50": results["allAp50%"] * 100}
        self._working_dir.cleanup()
        return ret


while np.sum(fill_map) != np.sum(input):
        changing_map_new = ndimage.binary_erosion(changing_map)
        fill_map[np.where(changing_map_new!=changing_map)] = True
        output[np.where(changing_map_new!=changing_map)] = sdf_in
        changing_map = changing_map_new.copy()
        sdf_in -= 1

"""
This script is for generating training/testing data, which is saved as a h5 file.
"""

import sys
sys.path.append('./')

import numpy as np
import os
import h5py
import json
import scipy.ndimage as ndimage
from tqdm import tqdm

import util.binvox_rw as binvox_rw
from util.vox_utils import Cartesian2Voxcoord
from util.rigging_parser.skel_parser import Skel
from util.rigging_parser.obj_parser import Mesh_obj

def unique_rows(a):
    # remove repeat rows from numpy array
    a = np.ascontiguousarray(a)
    unique_a = np.unique(a.view([('', a.dtype)]*a.shape[1]))
    return unique_a.view(a.dtype).reshape((unique_a.shape[0], a.shape[1]))

def draw_jointmap(img, pt, sigma):
    # Draw a 3D gaussian
    # Check that any part of the gaussian is in-bounds
    ul = [int(pt[0] - 3 * sigma), int(pt[1] - 3 * sigma), int(pt[2] - 3*sigma)]
    br = [int(pt[0] + 3 * sigma + 1), int(pt[1] + 3 * sigma + 1), int(pt[2] + 3*sigma +1)]
    if (ul[0] >= img.shape[0] or ul[1] >= img.shape[1] or ul[2] >= img.shape[2] or
            br[0] < 0 or br[1] < 0 or br[2] < 0):
        return img

    # Generate gaussian
    size = 6 * sigma + 1
    x = np.arange(0, size, 1, float)
    y = x[:, np.newaxis]
    z = y[..., np.newaxis]
    x0 = y0 = z0 = size // 2
    # The gaussian is not normalized, we want the center value to equal 1
    g = np.exp(- ((x - x0) ** 2 + (y - y0) ** 2 + (z - z0) ** 2) / (2 * sigma ** 2))

    # Usable gaussian range
    g_x = max(0, -ul[0]), min(br[0], img.shape[0]) - ul[0]
    g_y = max(0, -ul[1]), min(br[1], img.shape[1]) - ul[1]
    g_z = max(0, -ul[2]), min(br[2], img.shape[2]) - ul[2]
    # Image range
    img_x = max(0, ul[0]), min(br[0], img.shape[0])
    img_y = max(0, ul[1]), min(br[1], img.shape[1])
    img_z = max(0, ul[2]), min(br[2], img.shape[2])

    img[img_x[0]:img_x[1], img_y[0]:img_y[1], img_z[0]:img_z[1]] = \
        np.maximum(img[img_x[0]:img_x[1], img_y[0]:img_y[1], img_z[0]:img_z[1]],
                   g[g_x[0]:g_x[1], g_y[0]:g_y[1], g_z[0]:g_z[1]])
    return img

def draw_bonemap(heatmap, p_pos, c_pos, output_resulotion):
    # create 3D bone heatmap. Voxels along the bone have value 1, otherwise 0
    c_pos = np.asarray(c_pos)
    ray = c_pos - p_pos
    i_step = np.arange(1, 100)
    unit_step = (ray / 100)[np.newaxis,:]
    unit_step = np.repeat(unit_step, 99, axis=0)
    pos_step = p_pos + unit_step * i_step[:,np.newaxis]
    pos_step = np.round(pos_step).astype(np.uint8)
    pos_step = np.array([p for p in pos_step if np.all(p >= 0) and np.all(p < output_resulotion)])
    if len(pos_step) != 0:
        heatmap[pos_step[:, 0], pos_step[:, 1], pos_step[:, 2]] += 1
    np.clip(heatmap, 0.0, 1.0, out=heatmap)
    return heatmap

def getConditions(fs_filename):
    '''
    Read in our feature size file and return the 5th percentile of all feature size
    Our feature size file contains one bone sample per row.
    The first three numbers are coordinates of the sample. The last number is the feature size.
    Feature size is calculated in cpp, where we shoot rays on a plane perpendicular to the bone.
    For each bone sample, its "feature size" is the median distance to all nearest hits of the rays from it.
    Our feature size file use "new bone" to seperate samples from different bones.
    :param fs_filename: filename of our feature size file.
    :return: 5th percentile of all the feature size, used as contional variable during training and testing.
    '''
    with open(fs_filename, 'r') as f:
        lines = f.readlines()
    if len(lines) == 0:
        return 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
    fs_all = []
    fs_list_bone = []
    for li in lines:
        if li.strip() == 'new bone':
            fs_all.append(fs_list_bone)
            fs_list_bone = []
        else:
            words = li.split()
            fs_list_bone.append(float(words[3]))
    min_fs = []
    for i in fs_all:
        min_fs += i
    min_fs = np.array(min_fs)
    min_5_fs = np.percentile(min_fs, 5)
    return min_5_fs

def center_vox(volumn_input):
    #put the occupied voxels at the center instead of corner
    pos = np.where(volumn_input > 0)
    x_max, x_min = np.max(pos[0]), np.min(pos[0])
    y_max, y_min = np.max(pos[1]), np.min(pos[1])
    z_max, z_min = np.max(pos[2]), np.min(pos[2])
    side_length = volumn_input.shape[0]
    mid_len = int(side_length / 2)
    xr_low = int((x_max - x_min + 1) / 2)
    xr_high = x_max - x_min + 1 - xr_low
    yr_low = int((y_max - y_min + 1) / 2)
    yr_high = y_max - y_min + 1 - yr_low
    zr_low = int((z_max - z_min + 1) / 2)
    zr_high = z_max - z_min + 1 - zr_low
    content = volumn_input[x_min: x_max + 1, y_min: y_max + 1, z_min: z_max + 1]
    volumn_output = np.zeros((volumn_input.shape), dtype=np.bool)
    center_trans = [x_min - mid_len + xr_low, y_min - mid_len + yr_low, z_min - mid_len + zr_low]
    center_trans = list(map(int, center_trans))
    volumn_output[mid_len - xr_low:mid_len + xr_high, mid_len - yr_low:mid_len + yr_high,
    mid_len - zr_low:mid_len + zr_high] = content
    return volumn_output, center_trans

def bin2sdf(input):
    '''
    convert binary voxels into sign distance function field. Negetive for interior. Positive for exterior. Normalized.
    :param input: binary voxels
    :return: SDF representation of voxel.
    '''
    fill_map = np.zeros(input.shape, dtype=np.bool)
    output = np.zeros(input.shape, dtype=np.float16)
    # fill inside
    changing_map = input.copy()
    sdf_in = -1

    # fill outside. No need to fill all of them, since during training, outside part will be masked.
    changing_map = input.copy()
    sdf_out = 1
    while np.sum(fill_map) != np.size(input):
        changing_map_new = ndimage.binary_dilation(changing_map)
        fill_map[np.where(changing_map_new!=changing_map)] = True
        output[np.where(changing_map_new!=changing_map)] = sdf_out
        changing_map = changing_map_new.copy()
        sdf_out += 1
        if sdf_out == -sdf_in:
            break
    # Normalization
    output[np.where(output < 0)] /= (-sdf_in-1)
    output[np.where(output > 0)] /= (sdf_out-1)
    return output

def cal_avg_edge_length(mesh):
    # calculate the average length of all edges of the mesh, which is used as a multiplier when generating vertice density maps.
    points = mesh.v
    edge_index = np.concatenate((mesh.f[:, 0:2], mesh.f[:, 1:], mesh.f[:, [0, 2]]), axis=0)
    edge_index -= 1
    edge_index = unique_rows(edge_index)
    edge_length = np.linalg.norm(points[edge_index[:, 0]] - points[edge_index[:, 1]], axis=1)
    return points, np.mean(edge_length)

def get_surface_vertice(mesh, trans, scale, center_trans, dim_ori=82, r=3, dim_pad=88):
    points, avg_edge = cal_avg_edge_length(mesh)
    res = np.zeros((dim_pad, dim_pad, dim_pad), dtype=np.uint8)
    vc = (points - np.array([trans])) / scale * dim_ori
    vc = np.round(vc).astype(int)
    vc = vc - np.array([center_trans]) + r
    for v in vc:
        if np.all((dim_pad - v) > 0) and np.all(v > 0):
            res[v[0], v[1], v[2]] += 1
    return res, avg_edge

def genDataset_inner(root_folder, model_id, subset, dim_ori=82, r=3, dim_pad=88):
    '''
    generate necessary data for one sample
    :param root_folder: directory with all raw data
    :param model_id: model ID
    :param subset: 'train', 'val' or 'test'
    :param dim_ori: original voxel grid resolution
    :param r: padding added to the original voxel grid
    :param dim_pad: padded voxel grid resolution
    :return: input representation for one sample, including SDF-voxelization, k1, k2 curvature maps, shape diameter, vertice density
    '''
    mesh_file = os.path.join(root_folder, 'obj/{:d}.obj'.format(model_id))
    vox_file = os.path.join(root_folder, 'vox_82/{:d}.binvox'.format(model_id))
    skel_file = os.path.join(root_folder, 'skel/{:d}.txt'.format(model_id))
    fs_file = os.path.join(root_folder, 'fs/{:d}_featuresize.txt'.format(model_id))

    # read original voxels and pad it.
    with open(vox_file, 'rb') as f:
        mesh_vox = binvox_rw.read_as_3d_array(f)
    mesh_vox_padded = np.zeros((mesh_vox.dims[0] + 2 * r, mesh_vox.dims[1] + 2 * r, mesh_vox.dims[2] + 2 * r), dtype=np.float16)
    mesh_vox_padded[r:mesh_vox.dims[0] + r, r:mesh_vox.dims[1] + r, r:mesh_vox.dims[2] + r] = mesh_vox.data
    # put the occupied voxels at the center instead of left-top corner
    mesh_vox_padded, center_trans = center_vox(mesh_vox_padded)
    # convert binary voxels to SDF representation
    mesh_vox_padded = bin2sdf(mesh_vox_padded)

    mesh = Mesh_obj(mesh_file)
    min_5_fs = getConditions(fs_file) # get 5th-percentile control parameter
    skel = Skel(skel_file) # read in ground-truth skeleton
    heatmap_joint = np.zeros((int(mesh_vox.dims[0] + 2 * r), int(mesh_vox.dims[1] + 2 * r),
                              int(mesh_vox.dims[2] + 2 * r)), dtype=np.float16)
    heatmap_bones = np.zeros((int(mesh_vox.dims[0] + 2 * r), int(mesh_vox.dims[1] + 2 * r),
                              int(mesh_vox.dims[2] + 2 * r)), dtype=np.float16)
    # create vertice density heatmap.
    heatmap_verts, avg_edge = get_surface_vertice(mesh, mesh_vox.translate, mesh_vox.scale, center_trans, dim_ori, r, dim_pad)
    # start to create target joint&bone heatmaps. BFS iteration.
    this_level = [skel.root]
    while this_level:
        next_level = []
        for p_node in this_level:
            pos = Cartesian2Voxcoord(np.array(p_node.pos), mesh_vox.translate, mesh_vox.scale, mesh_vox.dims[0])
            pos = (pos[0] - center_trans[0] + r, pos[1] - center_trans[1] + r, pos[2] - center_trans[2] + r)
            pos = np.clip(pos, a_min=0, a_max=dim_pad-1)
            draw_jointmap(heatmap_joint, pos, sigma=0.6)
            next_level += p_node.children
            for c_node in p_node.children:
                ch_pos = Cartesian2Voxcoord(np.array(c_node.pos), mesh_vox.translate, mesh_vox.scale, mesh_vox.dims[0])
                ch_pos = (ch_pos[0] - center_trans[0] + r, ch_pos[1] - center_trans[1] + r, ch_pos[2] - center_trans[2] + r)
                draw_bonemap(heatmap_bones, pos, ch_pos, output_resulotion=dim_pad)
        this_level = next_level

    # read original curvature
    curvature_raw = np.load(os.path.join(root_folder, 'curvature/{:d}_curvature.npy'.format(model_id)))
    curvature_surface = np.zeros((2, dim_pad, dim_pad, dim_pad), dtype=np.float16)
    # read original shape diameter
    sd_raw = np.load(os.path.join(root_folder, 'shape_diameter/{:d}_sd.npy'.format(model_id)))
    sd_surface = np.zeros((1, dim_pad, dim_pad, dim_pad), dtype=np.float16)
    # only preserve values at surface voxels.
    data_bin = (mesh_vox_padded < 0)
    changing_map_new = data_bin.copy()
    changing_map_new = ndimage.binary_erosion(changing_map_new)
    fill_map = (changing_map_new != data_bin)
    coord_v = np.argwhere(fill_map)
    coord_v_trans = coord_v + np.array([center_trans]) - r
    curvature_surface[:, coord_v[:, 0], coord_v[:, 1], coord_v[:, 2]] = \
        curvature_raw[:, coord_v_trans[:, 0], coord_v_trans[:, 1], coord_v_trans[:, 2]]
    sd_surface[:, coord_v[:, 0], coord_v[:, 1], coord_v[:, 2]] = \
        sd_raw[coord_v_trans[:, 0], coord_v_trans[:, 1], coord_v_trans[:, 2]]

    anno = {'name': str(model_id), 'min_5_fs': min_5_fs, 'translate': mesh_vox.translate, 'scale': mesh_vox.scale,
            'subset': subset, 'center_trans': center_trans, 'avg_edge': avg_edge}
    return mesh_vox_padded, heatmap_joint, heatmap_bones, heatmap_verts, curvature_surface, sd_surface, anno

def genDataset(root_folder, dim_ori=82, padding=3, dim_pad=88):
    # read in train/val/test split
    train_id_list = np.loadtxt(os.path.join(root_folder, 'train_final.txt'), dtype=int)
    val_id_list = np.loadtxt(os.path.join(root_folder, 'val_final.txt'), dtype=int)
    test_id_list = np.loadtxt(os.path.join(root_folder, 'test_final.txt'), dtype=int)

    num_train = len(train_id_list)
    num_val = len(val_id_list)
    num_test = len(test_id_list)

    # create sub-datasets
    hf = h5py.File(os.path.join(root_folder, 'model-resource-volumetric.h5'), 'w')
    hf.create_dataset('train_data', (num_train, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('train_vert', (num_train, dim_pad, dim_pad, dim_pad), np.uint8)
    hf.create_dataset('train_curvature', (num_train, 2, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('train_sd', (num_train, 1, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('train_label_joint', (num_train, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('train_label_bone', (num_train, dim_pad, dim_pad, dim_pad), np.float16)

    hf.create_dataset('val_data', (num_val, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('val_vert', (num_val, dim_pad, dim_pad, dim_pad), np.uint8)
    hf.create_dataset('val_curvature', (num_val, 2, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('val_sd', (num_val, 1, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('val_label_joint', (num_val, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('val_label_bone', (num_val, dim_pad, dim_pad, dim_pad), np.float16)

    hf.create_dataset('test_data', (num_test, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('test_vert', (num_test, dim_pad, dim_pad, dim_pad), np.uint8)
    hf.create_dataset('test_curvature', (num_test, 2, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('test_sd', (num_test, 1, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('test_label_joint', (num_test, dim_pad, dim_pad, dim_pad), np.float16)
    hf.create_dataset('test_label_bone', (num_test, dim_pad, dim_pad, dim_pad), np.float16)

    # start to fill all sub-datasets
    anno_all = []
    for train_id in tqdm(range(len(train_id_list))):
        model_id = train_id_list[train_id]
        mesh_vox, heatmap_joint, heatmap_bones, heatmap_verts, curvature, sd, anno = \
            genDataset_inner(root_folder, model_id, subset='train', dim_ori=dim_ori, r=padding, dim_pad=dim_pad)
        hf['train_data'][train_id, :, :, :] = mesh_vox
        hf['train_vert'][train_id, :, :, :] = heatmap_verts
        hf['train_label_joint'][train_id, :, :, :] = heatmap_joint
        hf['train_label_bone'][train_id, :, :, :] = heatmap_bones
        hf['train_curvature'][train_id, ...] = curvature[np.newaxis, ...]
        hf['train_sd'][train_id, ...] = sd[np.newaxis, ...]
        anno_all.append(anno)

    for val_id in tqdm(range(len(val_id_list))):
        model_id = val_id_list[val_id]
        mesh_vox, heatmap_joint, heatmap_bones, heatmap_verts, curvature, sd, anno = \
            genDataset_inner(root_folder, model_id, subset='val', dim_ori=dim_ori, r=padding, dim_pad=dim_pad)
        hf['val_data'][val_id, :, :, :] = mesh_vox
        hf['val_vert'][val_id, :, :, :] = heatmap_verts
        hf['val_label_joint'][val_id, :, :, :] = heatmap_joint
        hf['val_label_bone'][val_id, :, :, :] = heatmap_bones
        hf['val_curvature'][val_id, ...] = curvature[np.newaxis, ...]
        hf['val_sd'][val_id, ...] = sd[np.newaxis, ...]
        anno_all.append(anno)

    for test_id in tqdm(range(len(test_id_list))):
        model_id = test_id_list[test_id]
        mesh_vox, heatmap_joint, heatmap_bones, heatmap_verts, curvature, sd, anno = \
            genDataset_inner(root_folder, model_id, subset='test', dim_ori=dim_ori, r=padding, dim_pad=dim_pad)
        hf['test_data'][test_id, :, :, :] = mesh_vox
        hf['test_vert'][test_id, :, :, :] = heatmap_verts
        hf['test_label_joint'][test_id, :, :, :] = heatmap_joint
        hf['test_label_bone'][test_id, :, :, :] = heatmap_bones
        hf['test_curvature'][test_id, ...] = curvature[np.newaxis, ...]
        hf['test_sd'][test_id, ...] = sd[np.newaxis, ...]
        anno_all.append(anno)

    hf.close()

    # save accompanying information as json
    with open(os.path.join(root_folder, 'model-resource-volumetric.json'), 'w') as outfile:
        json.dump(anno_all, outfile)

if __name__ == '__main__':
    root_folder = 'model_resource_data/' # the directory to put raw data and generated dataset
    genDataset(root_folder, dim_ori=82, padding=3, dim_pad=88)