from tkinter import *
from tkinter import messagebox
from database import Database
from customer import Customer
from history import History
from sqlite3 import IntegrityError
from PIL import ImageTk, Image
from enum import Enum
from tkcalendar import DateEntry

class Window:
    def __init__(self):
        self.database = Database()
        self.root = Tk()
        self.root.title("Patient Record")
        self.__create_top_button()
        self.__create_search_bar()
        self.__create_customer_frame()
        self.__init_customer_table()
        self.root.state('zoomed')
        self.root.mainloop()

    def __create_top_button(self):
        add_patient_btn = Button(self.root, text="Add Patient", padx=10, pady=5, command=lambda: self.customer_form(OperationType.INSERT))
        add_patient_btn.pack(padx=30, pady=10, anchor=W)

    def __create_search_bar(self):
        search_frame = Frame(self.root)
        search_frame.pack(padx=30, pady=10, anchor=W)
        search_label = Label(search_frame, text="Search")
        search_label.grid(row=0, column=0)
        search_box = Entry(search_frame, width=30)
        search_box.grid(row=0, column=1)

        def _search():
            data = self.database.search_customer(search_box.get())
            try:
                self.create_customer_table(data)
            except KeyError:
                print("No search result")

        self.search_img = ImageTk.PhotoImage(Image.open("search.png").resize((20, 20)))
        search_button = Button(search_frame, text="Click", image=self.search_img, command=_search)
        search_button.grid(row=0, column=2)

    def __init_customer_table(self):
        try:
            self.create_customer_table(self.database.get_all_customer())
        except KeyError:
            print("Database is empty")

    def __create_customer_frame(self):
        main_frame = Frame(self.root, relief=GROOVE, bd=1)
        main_frame.pack(padx=30, pady=50, expand=True, fill=BOTH, anchor=W)

        canvas = Canvas(main_frame)
        self.customer_scrollable_frame = Frame(canvas)
        scrollbar = Scrollbar(main_frame, orient="vertical", command=canvas.yview)

        self.customer_scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

        canvas.create_window((0, 0), window=self.customer_scrollable_frame, anchor='nw')
        canvas.configure(yscrollcommand=scrollbar.set)

        canvas.pack(side=LEFT, expand=True, fill=BOTH)
        scrollbar.pack(side=RIGHT, fill=Y)

        def _on_mousewheel(event):
            canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

        def _bound_to_mousewheel(event):
            canvas.bind_all("<MouseWheel>", _on_mousewheel)

        def _unbound_to_mousewheel(event):
            canvas.unbind_all("<MouseWheel>")

        self.customer_scrollable_frame.bind('<Enter>', _bound_to_mousewheel)
        self.customer_scrollable_frame.bind('<Leave>', _unbound_to_mousewheel)

    def __create_history_frame(self, parent):
        main_frame = Frame(parent, relief=GROOVE, bd=1)
        main_frame.pack(padx=30, pady=50, expand=True, fill=BOTH, anchor=W)

        canvas = Canvas(main_frame)
        self.history_scrollable_frame = Frame(canvas)
        scrollbar = Scrollbar(main_frame, orient="vertical", command=canvas.yview)

        self.history_scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

        canvas.create_window((0, 0), window=self.history_scrollable_frame, anchor='nw')
        canvas.configure(yscrollcommand=scrollbar.set)

        canvas.pack(side=LEFT, expand=True, fill=BOTH)
        scrollbar.pack(side=RIGHT, fill=Y)

        def _on_mousewheel(event):
            canvas.yview_scroll(int(-1 * (event.delta / 120)), "units")

        def _bound_to_mousewheel(event):
            canvas.bind_all("<MouseWheel>", _on_mousewheel)

        def _unbound_to_mousewheel(event):
            canvas.unbind_all("<MouseWheel>")

        self.history_scrollable_frame.bind('<Enter>', _bound_to_mousewheel)
        self.history_scrollable_frame.bind('<Leave>', _unbound_to_mousewheel)

    def create_customer_table(self, data):
        for widget in self.customer_scrollable_frame.winfo_children():
            widget.destroy()
        name_lbl = Label(self.customer_scrollable_frame, text="Name", relief="raised", width=30)
        name_lbl.grid(row=0, column=0, sticky='nsew')
        ic_lbl = Label(self.customer_scrollable_frame, text="IC", relief="raised", width=20)
        ic_lbl.grid(row=0, column=1, sticky='nsew')
        phone_number_lbl = Label(self.customer_scrollable_frame, text="Phone Number", relief="raised", width=20)
        phone_number_lbl.grid(row=0, column=2, sticky='nsew', ipadx=5)
        address_lbl = Label(self.customer_scrollable_frame, text="Address", relief="raised", width=70)
        address_lbl.grid(row=0, column=3, sticky='nsew')

        if len(data) == 0:
            raise KeyError("Empty Data")

        def create_history_callback(ic):
            def callback():
                self.history_window(ic)
            return callback

        def create_edit_callback(ic):
            def callback():
                row = self.database.get_customer(ic)
                self.customer_form(OperationType.EDIT, row)
                try:
                    self.create_customer_table(self.database.get_all_customer())
                except KeyError:
                    print("Database is empty")
            return callback

        def create_delete_callback(ic):
            def callback():
                self.database.delete_customer(ic)
                try:
                    self.create_customer_table(self.database.get_all_customer())
                except KeyError:
                    print("Database is empty")
            return callback

        for row in range(len(data)):
            for column in range(len(data[0])):
                label = Label(self.customer_scrollable_frame, text=data[row][column], borderwidth=2, relief="ridge")
                label.grid(row=row+1, column=column, sticky="nsew")
            history_btn = Button(self.customer_scrollable_frame, text="History", padx=8, command=create_history_callback(int(data[row][1])))
            history_btn.grid(row=row+1, column=len(data[0])+1)
            edit_btn = Button(self.customer_scrollable_frame, text="Edit", padx=13, command=create_edit_callback(int(data[row][1])))
            edit_btn.grid(row=row + 1, column=len(data[0]) + 2)
            delete_btn = Button(self.customer_scrollable_frame, text="Delete", padx=8, command=create_delete_callback(int(data[row][1])))
            delete_btn.grid(row=row+1, column=len(data[0])+3)

    def create_history_table(self, data):
        for widget in self.history_scrollable_frame.winfo_children():
            widget.destroy()
        date_lbl = Label(self.history_scrollable_frame, text="Date", relief="raised", width=15)
        date_lbl.grid(row=0, column=0, sticky='nsew')
        symptom_lbl = Label(self.history_scrollable_frame, text="Symptom", relief="raised", width=20)
        symptom_lbl.grid(row=0, column=1, sticky='nsew')
        medicine_lbl = Label(self.history_scrollable_frame, text="Medicine", relief="raised", width=20)
        medicine_lbl.grid(row=0, column=2, sticky='nsew', ipadx=5)
        note_lbl = Label(self.history_scrollable_frame, text="Note", relief="raised", width=110)
        note_lbl.grid(row=0, column=3, sticky='nsew')

        if len(data) == 0:
            raise KeyError("Empty History table")

        ic = data[0][5]

        def create_edit_callback(rowid):
            def callback():
                row = self.database.get_history(rowid)
                self.history_form(ic, OperationType.EDIT, row, rowid)
                self.create_history_table(self.database.get_customer_history(ic))
            return callback

        def create_delete_callback(rowid):
            def callback():
                self.database.delete_history(rowid)
                try:
                    self.create_history_table(self.database.get_customer_history(ic))
                except KeyError:
                    print("Database is empty")
            return callback

        for row in range(len(data)):
            for column in range(len(data[0]) - 2):
                label = Label(self.history_scrollable_frame, text=data[row][column+1], borderwidth=2, relief="ridge")
                label.grid(row=row+1, column=column, sticky="nsew")
            edit_btn = Button(self.history_scrollable_frame, text="Edit", padx=13, command=create_edit_callback(int(data[row][0])))
            edit_btn.grid(row=row+1, column=len(data[0])+1)
            delete_btn = Button(self.history_scrollable_frame, text="Delete", padx=11, command=create_delete_callback(int(data[row][0])))
            delete_btn.grid(row=row+1, column=len(data[0])+2)

    def customer_form(self, operation_type, data=None):
        insert_window = Toplevel()
        insert_window.title("Insert Form")

        #labels
        name_lbl = Label(insert_window, text="Name")
        name_lbl.grid(row=0, column=0)
        ic_lbl = Label(insert_window, text="IC")
        ic_lbl.grid(row=1, column=0)
        phone_number_lbl = Label(insert_window, text="Phone Number")
        phone_number_lbl.grid(row=2, column=0)
        address_lbl = Label(insert_window, text="Address")
        address_lbl.grid(row=3, column=0)

        #Insert boxes
        name_entry = Entry(insert_window)
        name_entry.grid(row=0, column=1)
        ic_entry = Entry(insert_window)
        ic_entry.grid(row=1, column=1)
        phone_number_entry = Entry(insert_window)
        phone_number_entry.grid(row=2, column=1)
        address_entry = Entry(insert_window)
        address_entry.grid(row=3, column=1)

        if operation_type == OperationType.EDIT:
            name_entry.insert(0, data[0][0])
            ic_entry.insert(0, data[0][1])
            ic_entry.config(state='disabled')
            phone_number_entry.insert(0, data[0][2])
            address_entry.insert(0, data[0][3])

        #Submit button
        def _patient_submit_btn():
            name = name_entry.get()
            ic = ic_entry.get()
            phone_number = phone_number_entry.get()
            address = address_entry.get()
            customer = Customer(name, ic, phone_number, address)
            if name == "" or ic == "":
                messagebox.showwarning("Empty field", "Must fill in Name and IC")
                insert_window.state('zoomed')
                insert_window.state('normal')
                return
            if len(ic) != 12:
                messagebox.showwarning("IC incorrect length", "Please insert 12 digits IC")
                insert_window.state('zoomed')
                insert_window.state('normal')
                return

            try:
                int(ic)
            except ValueError:
                messagebox.showwarning("IC incorrect format", "Please insert 12 digits IC integer")
                insert_window.state('zoomed')
                insert_window.state('normal')
                return

            try:
                if operation_type == OperationType.INSERT:
                    self.database.insert_customer(customer)
                elif operation_type == OperationType.EDIT:
                    self.database.edit_customer(customer)
            except IntegrityError:
                messagebox.showwarning("IC value error", "IC existed in database")
                insert_window.state('zoomed')
                insert_window.state('normal')
                return
            insert_window.destroy()
            data = self.database.get_all_customer()
            self.create_customer_table(data)
        submit_btn = Button(insert_window, text="Submit", command=_patient_submit_btn)
        submit_btn.grid(row=4, column=2)

    def history_window(self, ic):
        history_window = Toplevel()
        history_window.title("History")
        history_window.state("zoomed")

        add_history_btn = Button(history_window, text="Add history", padx=10, pady=5, command=lambda: self.history_form(ic, OperationType.INSERT))
        add_history_btn.pack(padx=30, pady=10, anchor=W)

        self.__create_history_frame(history_window)
        try:
            self.create_history_table(self.database.get_customer_history(ic))
        except KeyError:
            print("Empty History table")

        back_btn = Button(history_window, text="Back", padx=30, pady=10, command=history_window.destroy)
        back_btn.pack(padx=30, pady=10, anchor=W)

    def history_form(self, ic, operation_type, data=None, rowid=None):
        insert_window = Toplevel()
        insert_window.title("Insert Form")

        #labels
        date_lbl = Label(insert_window, text="Date")
        date_lbl.grid(row=0, column=0)
        symptom_lbl = Label(insert_window, text="Symptom")
        symptom_lbl.grid(row=1, column=0)
        medicine_lbl = Label(insert_window, text="Medicine")
        medicine_lbl.grid(row=2, column=0)
        note_lbl = Label(insert_window, text="Note")
        note_lbl.grid(row=3, column=0)

        #Insert boxes
        cal = DateEntry(insert_window, width=12, background='darkblue', foreground='white', borderwidth=2)
        cal.grid(row=0, column=1)
        symptom_entry = Entry(insert_window)
        symptom_entry.grid(row=1, column=1)
        medicine_entry = Entry(insert_window)
        medicine_entry.grid(row=2, column=1)
        note_entry = Text(insert_window, height=7, width=20)
        note_entry.grid(row=3, column=1)

        if operation_type == OperationType.EDIT:
            cal.delete(0, END)
            cal.insert(0, data[0][1])
            symptom_entry.insert(0, data[0][2])
            medicine_entry.insert(0, data[0][3])
            note_entry.insert(INSERT, data[0][4])

        #Submit button
        def _insert_history_btn(rowid=None):
            calDate = cal.get()
            symptom = symptom_entry.get()
            medicine = medicine_entry.get()
            note = note_entry.get("1.0", "end-1c")
            history = History(ic, calDate, symptom, medicine, note)

            if calDate == "":
                messagebox.showwarning("Empty field", "Must fill in Date")
                insert_window.state('zoomed')
                insert_window.state('normal')
                return

            if operation_type == OperationType.INSERT:
                self.database.insert_history(history)
            elif operation_type == OperationType.EDIT:
                self.database.edit_history(history, rowid)
            insert_window.destroy()
            data = self.database.get_customer_history(ic)
            self.create_history_table(data)
        if operation_type == OperationType.INSERT:
            submit_btn = Button(insert_window, text="Submit", command=_insert_history_btn)
        elif operation_type == OperationType.EDIT:
            submit_btn = Button(insert_window, text="Submit", command=lambda: _insert_history_btn(rowid))
        submit_btn.grid(row=4, column=2)

class OperationType(Enum):
    INSERT = 1
    EDIT = 2

if __name__ == "__main__":
    window = Window()


import folium
import pandas as pd
import urllib.request
import datetime
import time
import json
import webbrowser
import numpy as np
import firebase_admin
import matplotlib.pyplot as plt
import os, ssl

from folium.plugins import HeatMap
from firebase_admin import credentials, initialize_app
from firebase_admin import firestore
from matplotlib import font_manager, rc

class initFunc:
    def __init__(self):
        self.initialize()

    def initialize(self):
        font_path = "C:/Windows/Fonts/NGULIM.TTF"
        font = font_manager.FontProperties(fname=font_path).get_name()
        rc('font', family=font)

        if (not os.environ.get('PYTHONHTTPSVERIFY', '') and
            getattr(ssl, '_create_unverified_context', None)):
            ssl._create_default_https_context = ssl._create_unverified_context

class NaverAPI:
    def __init__(self):
        print('Naver API Used')

    def get_request_url(address, url, clientId, clientSecret):
        req = urllib.request.Request(url)
        req.add_header("X-NCP-APIGW-API-KEY-ID", clientId)
        req.add_header("X-NCP-APIGW-API-KEY", clientSecret)
        try:
            response = urllib.request.urlopen(req)
            if response.getcode() == 200:
                print ("[%s] Url Request Success : [%s]" % (datetime.datetime.now(), address))
                return response.read().decode('utf-8')
        except Exception as e:
            print(e)
            print("[%s] Error for URL : %s" % (datetime.datetime.now(), url))
            return None

    def getGeoData(address):
        base = "https://naveropenapi.apigw.ntruss.com/map-geocode/v2/geocode"

        try:
            parameters = "?query=%s" % urllib.parse.quote(address)
        except:
            return None

        url = base + parameters

        retData = NaverAPI.get_request_url(address, url, 'qh2cl6dxpw', 'u5b7JtT9I2bs7cvWnFrOKDwoPTf6rs0UnNuW4sLF')
        if retData == None:
            return None

        jsonAddress = json.loads(retData)

        if 'addresses' in jsonAddress.keys():
            latitude = jsonAddress['addresses'][0]['y']
            longitude = jsonAddress['addresses'][0]['x']
        else:
            return None

        return [latitude, longitude]

class FoilumMap:

    def drawHeatMap(df):

        geoData = []
        npGeoData= []

        map = folium.Map(location=[37.5103, 126.982], zoom_start=12)

        for i, row in df.iterrows():
            address = row['주소']
            geoData = NaverAPI.getGeoData(address)

            if geoData != None:
                folium.Marker(
                    geoData,
                    popup=row['장소'],
                    icon=folium.Icon(color='red')
                ).add_to(map)
                npGeoData.append(geoData)

        npGeoData = np.array(npGeoData)
        HeatMap(npGeoData).add_to(map)

        svFilename = 'date.html'
        map.save(svFilename)

class Chart:

    def drawPieChart(self, keys, values):
        explode = [0.05, 0.05, 0.05, 0.05]
        plt.pie(values, labels=keys, autopct='%.1f%%', startangle=260, counterclock=False, explode=explode)
        plt.show()

class DataFrameClass:
    def __init__(self, path):
        self.path = path

    def makeCallDF(self):
        df = pd.DataFrame(columns = ["date", "day", "time", "type", 'phone', 'second'])
        with open(self.path, 'rt', encoding='utf8') as f:
            lines = f.readlines()
            for line in lines:
                splitLine = line.split(" : ")
                df.loc[len(df)] = splitLine

        return df

class commonClass:
    def dataCleaning(self, indir, outdir, deltxt):
        edited_lines = []

        with open(indir, 'rt', encoding='utf8') as f:
            lines = f.readlines()
            for line in lines:
                if deltxt in line:
                    edited_lines.append(line)

        with open(outdir, 'w', encoding='utf8') as f:
            f.writelines(edited_lines)

        print('done')


class Solution:
    def isPowerOfThree(self, n: int) -> bool:
        # Boundary case
        if n == 0: return False
        if n < 0: return False

        logThree = log(n) / log(3)

        #print(logThree, round(logThree))
        if isclose(logThree, round(logThree), rel_tol=1e-12): return True
        else: return False


from PyQt5 import QtWidgets
import numpy as np
import scipy.optimize as optimize
from scipy import signal
from scipy.spatial.distance import cdist
import warnings
warnings.simplefilter("ignore", optimize.OptimizeWarning)

# Get the velocity and delay values based on the current source...
# ...used for the simple locator (see simpleLocatorFunc for the equation, units in km and s)
# ...Vp=P-velocity,Vs=S-velocity,Dp=P-delay,Ds=S-delay
# ...tResThresh is the time residual which is residual value beyond which is considered poor
def getVelDelay(sourceTag):
    sources={'testing':{'Vp':5.0,'Vs':2.9,'Dp':0,'Ds':0,'tResThresh':0.5},
             'NX':{'Vp':6.19,'Vs':3.57,'Dp':0.74,'Ds':1.13,'tResThresh':1.0},
            }
    if sourceTag in sources.keys():
        return sources[sourceTag]
    else:
        return '$pass'

# Function to calculate the arrival times
# ti=function((xi,yi,zi,vi,di),x0,y0,t0)
# ti=t0+(Dist+Vel*Delay)/Vel
def simpleLocatorFunc(data,x0,y0,z0,t0):
    return t0+((((data[:,0]-x0)**2+(data[:,1]-y0)**2+(data[:,2]-z0)**2)**0.5)+data[:,3]*data[:,4])/data[:,3]

# Gather the information to be sent to the location minimization function
def getPickData(pickSet,staLoc,vdInfo):
    # Form array to be able to send to the 0 dimensional travel time function
    data,stas=[],[] #[xi,yi,zi,vi,ti],[]
    for aPick in pickSet:
        # Ensure that this pick has station metadata present
        idx=np.where(staLoc[:,0]==aPick[0])[0]
        if len(idx)<1:
            continue
        aStaX,aStaY,aStaZ=staLoc[idx[0],1:4].astype(float)
        # Assign appropriate velocity values
        if aPick[1]=='P':
            aVel=vdInfo['Vp']
            aDelay=vdInfo['Dp']
        elif aPick[1]=='S':
            aVel=vdInfo['Vs']
            aDelay=vdInfo['Ds']
        else:
            continue
        aTime=float(aPick[2]) ## Give more sig digs?... rough approximation anyways
        data.append([aStaX,aStaY,aStaZ,aVel,aDelay,aTime])
        stas.append(aPick[0])
    return np.array(data),np.array(stas,dtype=str)

# Locate event using a straight ray path, and non-linear least squares curve fitting
# Also assign residual coloring to the stations
def simpleLocator(pickSet,staLoc,mapCurEve,staSort,sourceTag):
    vdInfo=getVelDelay(sourceTag)
    # If there was no vpInfo defined, pass these updates
    if vdInfo=='$pass':
        return '$pass','$pass','$pass'
    # Use only P and S picks
    if len(pickSet)>=4:
        pickSet=pickSet[np.where((pickSet[:,1]=='P')|(pickSet[:,1]=='S'))]
    # Set up the pen assignment arrays
    traceBgPenAssign={'noStaData':[sta for sta in staSort if sta not in staLoc[:,0]]}
    if len(staSort)==0:
        mapStaPenAssign={'noTraceData':[row[0] for row in staLoc]}
    else:
        mapStaPenAssign={'noTraceData':[row[0] for row in staLoc if row[0] not in staSort]}
    mapStaPenAssign['goodMap']=list(np.unique(pickSet[:,0]))
    data,stas=getPickData(pickSet,staLoc,vdInfo)
    # If there are too few picks, do not try to locate
    if len(data)<4:
        return np.empty((0,5)),traceBgPenAssign,mapStaPenAssign
    # Compute value to take away from the very large travel times (easier on the curve fitting)
    Tref=np.min(pickSet[:,2].astype(float))
    data[:,5]-=Tref
    # Solve for event origin parameters
    try:
        params, pcov = optimize.curve_fit(simpleLocatorFunc, data[:,:5], data[:,5],(0,0,0,0))
        x0,y0,z0,t0=params
    except:
        print('simpleLocator failed')
        return np.empty((0,5)),traceBgPenAssign,mapStaPenAssign
    # Get the residual values...
    # ...predicted arrival times
    ti=simpleLocatorFunc(data[:,:5],x0,y0,z0,t0)
    # Get the difference with the actual arrival times
    resids=np.abs(ti-data[:,5])
    poorResidStas=np.unique(stas[np.where(resids>=vdInfo['tResThresh'])])
    goodResidStas=np.unique(stas[np.where(resids<vdInfo['tResThresh'])])
    # Add the good/bad stations to proper pen assignments
    mapStaPenAssign['poorMap']=list(poorResidStas)
    mapStaPenAssign['goodMap']=list(goodResidStas)
    # Add back the time offset
    t0=Tref+t0
    return np.array([[0,x0,y0,z0,t0]]),traceBgPenAssign,mapStaPenAssign

# Function to calculate PS delay with a given hypocentral distance (all units in km, and seconds)
def calcPSarrivalTimes(vdInfo,hypDist):
    tp=(hypDist+vdInfo['Vp']*vdInfo['Dp'])/vdInfo['Vp']
    ts=(hypDist+vdInfo['Vs']*vdInfo['Ds'])/vdInfo['Vs']
    return tp,ts

# Calculate the hypocentral distance given a PS delay
def calcHypDist_viaDelayPS(vdInfo,delayPS):
    hyp=vdInfo['Vp']*vdInfo['Vs']*(delayPS+vdInfo['Dp']-vdInfo['Ds'])/(vdInfo['Vp']-vdInfo['Vs'])
    hyp=np.clip(hyp,0,np.max(hyp))
    return hyp

# For each pick give it a pIdx, dIdx, and probability (many trios are formed for each pick)
def getOriginInfo(vdInfo,pickSet,staLoc,probSpread,tDelta,maxSampShift):
    maxSampShift+=1 # makes it easier for array generation, starts from zero
    # Make a dictionary of the station locations
    staLocDict={}
    for entry in staLoc:
        staLocDict[entry[0]]=entry[1:].astype(float)
    # Sort by pick type (so the P-picks are processed first)
    pickSet=pickSet[np.argsort(pickSet[:,1])]
    # Form arrays to be used for all picks...
    # ...for station locations
    locs=np.ones((maxSampShift,len(pickSet),3)).astype(float)
    # ...ps delay indicies
    dIdxs=np.arange(0,maxSampShift).reshape(maxSampShift,1)
    # ...hypocentral distances
    hypDists=calcHypDist_viaDelayPS(vdInfo,dIdxs*tDelta).reshape(maxSampShift,1)
    tp=calcPSarrivalTimes(vdInfo,hypDists)[0].T[0]
    # Increase the shape to account for the number of picks
    hypDists=hypDists*np.ones((maxSampShift,len(pickSet)))
    dIdxs=dIdxs*np.ones((maxSampShift,len(pickSet)),dtype=int)
    pIdxs=np.ones((maxSampShift,len(pickSet)),dtype=int)
    probs=np.ones((maxSampShift,len(pickSet)),dtype=float)
    origTimes=np.ones((maxSampShift,len(pickSet)),dtype=float)
    stas=np.ones((maxSampShift,len(pickSet)),dtype='a5')
    # Digitize the pick times
    pickTimes=pickSet[:,2].astype(float)
    minT,maxT=np.min(pickTimes),np.max(pickTimes)
    tBins=np.arange(minT-(0.5+len(probSpread)/2+maxSampShift)*tDelta,
                    maxT+(0.5+len(probSpread)/2)*tDelta,tDelta)
    pickIdxs=np.digitize(pickTimes,tBins)
    for j,sta,phase,xIdx in zip(range(len(pickSet)),pickSet[:,0],pickSet[:,1],pickIdxs):
        # Get the station location for later reference
        aLoc=staLocDict[sta]
        if phase=='P':
            pIdx=np.ones(maxSampShift,dtype=int)*xIdx
        else:
            pIdx=np.arange(xIdx,xIdx-maxSampShift,-1,dtype=int)
        # Calculate the origin times
        origTime=tBins[pIdx]-0.5*tDelta-tp
        # Assign these values to proper location
        locs[:,j,:]=aLoc
        pIdxs[:,j]=pIdx
        origTimes[:,j]=origTime
        stas[:,j]=sta
    # Create the origin info, for the high probability
    originInfo={}#'t':outInfo[:,0],'sta':np.array(stas,dtype=str),'dist':outInfo[:,1],'probE':outInfo[:,2],
                #'pIdx':outInfo[:,3].astype(int),'dIdx':outInfo[:,4].astype(int),'loc':outInfo[:,5:8]}
    # Assign probabilities for each of these values based on the probSpread
    for tIdxShift,tProb in zip(np.arange(len(probSpread))-len(probSpread)/2,probSpread):
        for key,entry in [['t',origTimes],['sta',stas],['dist',hypDists],
                          ['prob',probs],['pIdx',pIdxs],['dIdx',dIdxs],['loc',locs]]:
            # Reshape so one long list of trios
            if key=='loc':
                shape=entry.shape
                entry=entry.reshape(shape[0]*shape[1],shape[2])
            else:
                entry=entry.flatten()
            # If the values is dependent on the probSpread, change it
            if key=='prob':
                entry[:]*=tProb
            elif key=='t':
                entry[:]+=tIdxShift*tDelta
            elif key=='pIdx':
                entry[:]+=tIdxShift
            # Add the entry if not already there...
            if key not in originInfo.keys():
                originInfo[key]=entry
            # ...otherwise concatenate
            else:
                originInfo[key]=np.concatenate((originInfo[key],entry))
    return originInfo

# Make a subset of rings which can be used later to stack the probabilities (and another base for the ROI)
def getStackRings(roi,vdInfo,maxHypDist,maxVertDist,maxDelayPS,timeDelta):
    # Figure out which boundaries should be applied
    ringBounds=[]
    for aDelay in np.arange(0,maxDelayPS+timeDelta,timeDelta):
        aHyp=calcHypDist_viaDelayPS(vdInfo,aDelay)
        if aHyp<0:
            aHyp=0
        if aHyp in ringBounds:
            continue
        ringBounds.append(aHyp)
    halfRingWidth=(ringBounds[-1]-ringBounds[-2])/2.0
    ringBounds.append(halfRingWidth*2+ringBounds[-1])
    ringBounds=np.array(ringBounds,dtype=float)-halfRingWidth
    # Create the base map
    gridSpace=halfRingWidth/2.0 # Choose this ratio for wanted ring resolution
    baseX,baseY,baseZ=np.meshgrid(np.arange(roi[0],roi[1],gridSpace),
                                  np.arange(roi[2],roi[3],gridSpace),
                                  np.arange(roi[4],roi[5],gridSpace))
    baseProb=np.zeros(baseX.shape)
    # Create the station rings, centered on zero
    usedMaxDist=maxHypDist-maxHypDist%gridSpace+gridSpace
    usedMaxVert=maxVertDist-maxVertDist%gridSpace+gridSpace
    layX,layY,layZ=np.meshgrid(np.arange(-usedMaxDist,usedMaxDist+0.1*gridSpace,gridSpace),
                               np.arange(-usedMaxDist,usedMaxDist+0.1*gridSpace,gridSpace),
                               np.arange(-usedMaxVert,usedMaxVert+0.1*gridSpace,gridSpace))
    layD=(layX**2+layY**2+layZ**2)**0.5
    rings=[]
    for i in range(len(ringBounds)-1):
        rings.append(np.array((layD>=ringBounds[i])&(layD<ringBounds[i+1]),dtype=np.uint8))
    return rings,ringBounds,baseX,baseY,baseZ,baseProb,halfRingWidth

# Assign specific layer indicies to the originInfo, as well as how they would fit into the baseLayer
def assignLayerInfo(originInfo,ringLay,ringBounds,baseX,baseY,baseZ,baseProb):
    # Assign which layer
    layIdx=np.digitize(originInfo['dist'],ringBounds)-1
    originInfo['layIdx']=layIdx
    # Figure out all the shift/trim for each station
    shapeBase=baseX.shape
    shapeLay=ringLay[0].shape
    centerLayIdx=np.array(shapeLay)/2
    unqStas,staIdxs=np.unique(originInfo['sta'],return_index=True)
    staPosDict={}
    for aSta,aStaIdx in zip(unqStas,staIdxs):
        # Calculate where the station is centered on the base layer
        sX,sY,sZ=originInfo['loc'][aStaIdx]
        argPos=np.argmin((baseX.flatten()-sX)**2+(baseY.flatten()-sY)**2+(baseZ.flatten()-sZ)**2)
        xArg,yArg,zArg=np.unravel_index(argPos,shapeBase)
        # Figure out what bounds to use to trim
        # These naming conventions based on plan view of the rings
        topArg=np.min([(shapeBase[0])-xArg,centerLayIdx[0]])
        botArg=np.min([xArg,centerLayIdx[0]])
        rightArg=np.min([(shapeBase[1])-yArg,centerLayIdx[1]])
        leftArg=np.min([yArg,centerLayIdx[1]])
        outArg=np.min([(shapeBase[2])-zArg,centerLayIdx[2]])
        inArg=np.min([zArg,centerLayIdx[2]])
        # Contains the position within the base array, and how far up/down/left/right/out/in should be used
        staPosDict[aSta]=np.array([xArg,yArg,zArg,botArg,topArg,leftArg,rightArg,inArg,outArg],dtype=int)
    # Assign these to each origin entry
    gridShifts=[]
    for aSta in originInfo['sta']:
        gridShifts.append(staPosDict[aSta])
    originInfo['gridShift']=gridShifts
    return originInfo

def stackDetectAlgorithm(originInfo,rings,baseX,baseY,baseZ,base,timeDelta,
                         featAvgIdxLen,maxTT,eveProbMinSum,vdInfo,maxEveCount=999999):
    outTimes,outLocs,outProbs,outArgs=[],[],[],[]
    # Get the center index of the ring arrays
    lcx,lcy,lcz=np.array(rings[0].shape)/2 # Array length is odd, so this will yield the center index
    # Make a list of all timestamps, with the feature interval (timeDelta) as a spacing
    queryTimes=np.arange(np.min(originInfo['t'])-0.5*timeDelta,
                         np.max(originInfo['t'])+timeDelta,timeDelta)
    # Make list of stations which are used in this time range, and a reference index for each station
    segmentStas=np.unique(originInfo['sta'])
    refInfo={}
    for aSta in segmentStas:
        refIdx=list(originInfo['sta']).index(aSta)
        tp,ts=calcPSarrivalTimes(vdInfo,originInfo['dist'][refIdx])
        refpIdx=originInfo['pIdx'][refIdx]-int(np.round(tp/timeDelta))
        refOrig=originInfo['t'][refIdx]-int(np.round(tp/timeDelta))
        refInfo[aSta]=[refIdx,refOrig,refpIdx]
    # Now scan through all of these times, removing the duplicates...
    # ... each pass the maximums over a sweeping window of length "maxTT" is extracted, and any indices on the two diagonals...
    # ... and two verticals are removed from the other potential events - and then their probability is updated
    search=True
    seenArgs=np.zeros(len(originInfo['t']))
    # Only scan through queryTimes which actually have trios
    origArgs=np.digitize(originInfo['t'],queryTimes)
    unqOrigArgs=np.unique(origArgs)-1
    while search:
        # For each segment of times, find the location of the maximum probability (and get its index)
        eveProbs,eveLocs,eveTimes,usedOrigArgs=[],[],[],[]
        for i in unqOrigArgs:
            t1,t2=queryTimes[i],queryTimes[i+1]
            aEveTime=(t1+t2)/2.0
            wantArgs=np.where((originInfo['t']>t1)&(originInfo['t']<=t2)&(seenArgs==0))[0]
            stack=np.copy(base)
            for anArg in wantArgs:
                # p has entries: cx,cy,cz,bot,top,left,right,in,out
                p,layIdx,prob=originInfo['gridShift'][anArg],originInfo['layIdx'][anArg],originInfo['prob'][anArg]
                stack[p[0]-p[3]:p[0]+p[4],p[1]-p[5]:p[1]+p[6],p[2]-p[7]:p[2]+p[8]]+=(
                    prob*rings[layIdx][lcx-p[3]:lcx+p[4],lcy-p[5]:lcy+p[6],lcz-p[7]:lcz+p[8]])
            argMaxX,argMaxY,argMaxZ=np.unravel_index(np.argmax(stack.flatten()),stack.shape)
            aEveProb=stack[argMaxX,argMaxY,argMaxZ]
            # If there were no events during this time, skip
            if aEveProb<eveProbMinSum:
                continue
            # If there was more than one entry at this value, then get the median position
            if len(np.where(stack==aEveProb)[0])>1:
                argXs,argYs,argZs=np.unravel_index(np.where(stack.flatten()==aEveProb)[0],stack.shape)
                # Since grid spacing is equal in all directions, can take median of index
                argMaxX,argMaxY,argMaxZ=int(np.round(np.median(argXs))),int(np.round(np.median(argYs))),int(np.round(np.median(argZs)))
            aEveX,aEveY,aEveZ=baseX[argMaxX,argMaxY,argMaxZ],baseY[argMaxX,argMaxY,argMaxZ],baseZ[argMaxX,argMaxY,argMaxZ]
            # Given this maximum location, figure out which originInfo rows were used
            aUsedOrigArg=[]
            for anArg in wantArgs:
                p,layIdx=originInfo['gridShift'][anArg],originInfo['layIdx'][anArg]
                # If the ring layers do not cover the entire stack area...
                # ...check first to see if current ring could stack at the potential event location
                cutRing=rings[layIdx][lcx-p[3]:lcx+p[4],lcy-p[5]:lcy+p[6],lcz-p[7]:lcz+p[8]]
                if np.sum(np.array([argMaxX,argMaxY,argMaxZ])>=cutRing.shape)>0:
                    continue
                # ... then check if used at potential event location.
                if cutRing[argMaxX-(p[0]-p[3]),argMaxY-(p[1]-p[5]),argMaxZ-(p[2]-p[7])]==1:
                    aUsedOrigArg.append(anArg)
            usedOrigArgs.append(np.array(aUsedOrigArg,dtype=int))
            eveProbs.append(aEveProb)
            eveLocs.append([aEveX,aEveY,aEveZ])
            eveTimes.append(aEveTime)
        # If there are no more peaks to review, break
        if len(eveProbs)==0:
            search=False
            break
        eveProbs,eveLocs,eveTimes=np.array(eveProbs),np.array(eveLocs),np.array(eveTimes)
        # Collect all maximums, with the +/- maxTT bounding them
        collectPeaks=True
        peakIdxs=[]
        copyEveProbs=np.copy(eveProbs) # Used as a padding array for this "search" iteration
        while collectPeaks:
            maxArg=np.argmax(copyEveProbs)
            # If the peak was seen, swap it and any nearby peaks to be zero
            nearbyArgs=np.where(np.abs(eveTimes-eveTimes[maxArg])<maxTT)
            copyEveProbs[nearbyArgs]=0
            if np.max(copyEveProbs)<eveProbMinSum:
                collectPeaks=False
            # This peak however still requires to be a local maximum (in the unaltered array)...
            # ... as to not ruin the chance of a larger peak close to an even larger peak (which...
            # ... was already added to peakIdxs during this "search" iteration)
            if eveProbs[maxArg]<np.max(eveProbs[nearbyArgs]):
                continue
            peakIdxs.append(maxArg)
        peakIdxs=np.array(peakIdxs)
        # For each peak, look at the surrounding origin entries to see if they should be ignored later
        for aPeakArg in peakIdxs:
            # Collect nearby (in time) origin information (no need to scan all origin entries)
            aEveTime=eveTimes[aPeakArg]
            aEveLoc=eveLocs[aPeakArg]
            oArgs=np.where(np.abs(aEveTime-originInfo['t'])<maxTT)[0]
            # Collect the diagonal and vertical value from the peak for each station...
            peakDiags=[originInfo['pIdx'][anArg]+originInfo['dIdx'][anArg] for anArg in usedOrigArgs[aPeakArg]]
            peakVerts=[originInfo['pIdx'][anArg] for anArg in usedOrigArgs[aPeakArg]]
            peakStas=[originInfo['sta'][anArg] for anArg in usedOrigArgs[aPeakArg]]
            # ... including the theoretical peakDiags, and peakVerts if the station was not helpful in the stack
            for aSta in [sta for sta in segmentStas if sta not in peakStas]:
                # Find out what the pIdx would have been
                refIdx,refOrig,refpIdx=refInfo[aSta] # Get the reference origin time and pIdx (dIdx=0)
                staXYZ=originInfo['loc'][refIdx]     # Calculate the P and S travel times
                aDist=np.sum((aEveLoc-staXYZ)**2)**0.5
                tp,ts=calcPSarrivalTimes(vdInfo,aDist)
                psDelayIdx=int(np.round((ts-tp)/timeDelta))
                pIdx=int(np.round((aEveTime+tp-refOrig)/timeDelta))+refpIdx # Get P-index relative to reference
                peakDiags.append(pIdx+psDelayIdx)
                peakVerts.append(pIdx)
                peakStas.append(aSta)
            peakDiags,peakVerts,peakStas=np.array(peakDiags),np.array(peakVerts),np.array(peakStas)
            # Collect the diagonals and vertical index values from other times near the peak...
            # ... For varying S-wave (fix-P), and S-weights being high on P-wave
            oDiags=np.array([originInfo['pIdx'][anArg]+originInfo['dIdx'][anArg] for anArg in oArgs])
            # ... For varying P-wave (fix-S), and P-weights being high on S-wave
            oVerts=np.array([originInfo['pIdx'][anArg] for anArg in oArgs])
            oStas=np.array([originInfo['sta'][anArg] for anArg in oArgs])
            # For each station... (should only be one observation per station for a given peak - as rings do not overlap)
            for i,aSta in enumerate(peakStas):
                # ...Look at the difference between the diagonals
                peakDiag,peakVert=peakDiags[i],peakVerts[i]
                ## NOTE: Two of these lines no longer apply ##
                ## FIXME ##
                staDupArgs=(np.where((oStas==aSta)&
                                     ((np.abs(oDiags-peakDiag)<=(featAvgIdxLen))|      # Varying S-wave (fix P)
                                      (np.abs(oVerts-peakDiag)<=(featAvgIdxLen))|      # P-weights being high on S-wave
                                      (np.abs(oVerts-peakVert)<=(featAvgIdxLen))|      # Varying P-wave (fix-S)
                                      (np.abs(oDiags-peakVert)<=(featAvgIdxLen)))))[0] # S-weights being high on P-wave
                # ...convert back to the main arrays indices, and set them as "seen"
                seenArgs[oArgs[staDupArgs]]=1
            # Finally add this information to the event detections
            outTimes.append(eveTimes[aPeakArg])
            outLocs.append(eveLocs[aPeakArg])
            outProbs.append(eveProbs[aPeakArg])
            outArgs.append(usedOrigArgs[aPeakArg])
            if len(outTimes)>=maxEveCount:
                search=False
                break
    eveDetections={'t':np.array(outTimes),'loc':np.array(outLocs),'prob':np.array(outProbs),'originArgs':outArgs}
    return eveDetections

# Create the ROI from the staLoc...
# ...uses min,max locations of stations for X,Y
# ...uses a given distance for the vertical spread
def defaultROI(staLoc,maxVertDist):
    mins=np.min(staLoc[:,1:].astype(float),axis=0).reshape(1,3)
    maxs=np.max(staLoc[:,1:].astype(float),axis=0).reshape(1,3)
    roi=np.vstack((mins,maxs)).T.flatten()
    roi[4]-=0.1*maxVertDist
    roi[5]+=maxVertDist
    return roi

# Locate any number of events in the given window, using a stacking method
# ...tDelta is the origin time precision
# ...tIdxSpread infers how many extra time indicies before/after pick to spread the probability
# ...eveProbMinSum is the minimum summed event probability to declare as an event
# ...maxVertDist is the maximum vertical distance (km) to be used for stacking
# ...maxEveCount, if this number of events is reached - the association will stop
# ...nullBuffer, how many extra indicies to null out trios on either side of diagonals/straight
def stackLocate(pickSet,staLoc,sourceTag,tDelta=0.5,tIdxSpread=1,
                eveProbMinSum=3.0,maxVertDist=10,
                maxEveCount=999999,nullBuffer=0):
    print('Initiating stack locate')
    # Get the basic velocity info
    vdInfo=getVelDelay(sourceTag)
    if vdInfo=='$pass':
        print('No simple velocity information supplied (see getVelDelay in Locate.py)')
        return np.empty((0,5))
    keepArgs=np.ones(len(pickSet)).astype(bool)
    # Remove any picks no station metadata
    for i,entry in enumerate(pickSet):
        if entry[0] not in staLoc[:,0]:
            keepArgs[i]=False
    pickSet=pickSet[keepArgs]
    # Use only P and S picks
    if len(pickSet)>=4:
        pickSet=pickSet[np.where((pickSet[:,1]=='P')|(pickSet[:,1]=='S'))]
    # If there are not enough picks to surpass the minimum event summed probability, return
    if len(pickSet)<eveProbMinSum:
        print('Not enough picks to pass minimum event summed probability')
        return np.empty((0,5))
    QtWidgets.qApp.processEvents()
    # Create the ROI from the staLoc
    roi=defaultROI(staLoc,maxVertDist)
    maxHypDist=((roi[0]-roi[1])**2+(roi[2]-roi[3])**2)**0.5
    # Calculate the max duration a P and S arrival can differ (over the desired distance range)
    PSdelay=np.diff(calcPSarrivalTimes(vdInfo,maxHypDist))[0]
    maxSampShift=int(np.ceil(PSdelay/tDelta))
    maxTT=1*calcPSarrivalTimes(vdInfo,maxHypDist)[0]+2*maxSampShift*tDelta
    # Probability to be spread -/+ the actual pick time
    probSpread=signal.gaussian(1+2*tIdxSpread, std=1.0)
    probSpread/=np.sum(probSpread)
    # Get all of the pIdx,dIdx,prob trios
    originInfo=getOriginInfo(vdInfo,pickSet,staLoc,probSpread,tDelta,maxSampShift)
    # Create the stacking arrays
    ringLay,ringBounds,baseX,baseY,baseZ,baseProb,distErr=getStackRings(roi,vdInfo,maxHypDist,
                                                                        maxVertDist,PSdelay,tDelta)
    # Assign information the ring index, and station grid bounds (for stacking)
    originInfo=assignLayerInfo(originInfo,ringLay,ringBounds,baseX,baseY,baseZ,baseProb)
    # Detect the events
    eveDetections=stackDetectAlgorithm(originInfo,ringLay,baseX,baseY,baseZ,baseProb,tDelta,
                                       len(probSpread)/2+nullBuffer,maxTT,eveProbMinSum,vdInfo,
                                       maxEveCount=maxEveCount)
    numDetect=len(eveDetections['t'])
    mapCurEve=[]
    for i in range(numDetect):
        x,y,z=eveDetections['loc'][i]
        mapCurEve.append([i,x,y,z,eveDetections['t'][i]])
    print(str(numDetect)+' event(s) associated')
    return np.array(mapCurEve)

# Return the max origin time error when stacking picks, in units of "tRes"
def getGridError_NumTres(roi,tRes,numXyTres,tResSplit,Vs,
                                    useOneDepth=False,testDepth=None):
    numTresErr=1 # Set by the user gives (velocity model error + pick error)
    numTresErr+=numXyTres # Addition due to the initial XY grid spacing (set to be one tRes)
    numTresErr+=0.5/tResSplit # From digitizing origin times into bins of finite length
    if useOneDepth:
        maxEpi=((roi[0]-roi[1])**2+(roi[2]-roi[3])**2)**0.5
        d0,d1,d2=0,testDepth,roi[5]
        maxDistErr=np.max(np.abs([(d1**2+maxEpi**2)**0.5-(d0**2+maxEpi**2)**0.5,
                                  (d1**2+maxEpi**2)**0.5-(d2**2+maxEpi**2)**0.5]))
        numTresErr+=maxDistErr/(Vs*tRes) # From the max travel time distance differences between depths
    return numTresErr

# Reference each pick to a station index
def getPickStaIdxs(pickSet,staNames):
    pickStas,pickIdx=np.unique(pickSet[:,0],return_inverse=True)
    pickStaIdxs=np.ones(len(pickIdx),dtype=int)*-1
    for pos in np.unique(pickIdx):
        sta=pickStas[pos]
        if sta in staNames:
            posArgs=np.where(pickIdx==pos)[0]
            pickStaIdxs[posArgs]=np.where(staNames==sta)[0][0]
    # Remove any picks which did not have station metadata
    remArgs=np.where(pickStaIdxs!=-1)[0]
    pickStaIdxs=pickStaIdxs[remArgs]
    pickSet=pickSet[remArgs]
    return pickSet,pickStaIdxs

# Associate any number of picks based off pick alignment to reference solutions
# ...histogram resolution is given by tRes/tResSplit
# ...picks will be assigned a location if they have a residual<tRes
def alignLocate(staLoc,pickSet,sourceTag,minNumPicks=5,
                maxVertDist=10,tRes=1.0,
                tResSplit=5,testDepth=5.0):
    # Sanity checks before beginning
    if maxVertDist<=0 or tRes<=0:
        print('maxVertDist and tRes must be positive numbers')
        return np.empty((0,5))
    elif type(tResSplit)!=int:
        print('tResSplit must be an integer')
        return np.empty((0,5))
    # Grab simple velocity model
    vdInfo=getVelDelay(sourceTag)
    if vdInfo=='$pass':
        print('No simple velocity information supplied (see getVelDelay in Locate.py)')
        return np.empty((0,5))
    # Collect the station information
    staLocs=staLoc[:,1:].astype(float)
    staNames=staLoc[:,0]
    # Remove any picks which are neither P or S
    pickSet=pickSet[np.where((pickSet[:,1]=='P')|(pickSet[:,1]=='S'))]
    # Remove any picks without metadata
    pickSet=getPickStaIdxs(pickSet,staNames)[0]
    # Create the default ROI from the staLoc
    mainRoi=defaultROI(staLoc,maxVertDist)
    if testDepth>mainRoi[5] or testDepth<mainRoi[4]:
        print('testDepth must be within '+str(mainRoi[4])+' and '+str(mainRoi[5])+' which is determined by'+
              'the uppermost station elevation and maxVertDist (roughly)')
        return np.empty((0,5))
    # Make a holder for the returned event locations
    eveLocs,search=[],True
    while search:
        # Do a low resolution sweep to find a maximum position
        maxLoc,maxTime,gridSpace,coarsePickArgs=iterAlignLocate(vdInfo,staLocs,staNames,mainRoi,pickSet,
                                                                   tRes,tResSplit,1,useOneDepth=True,testDepth=testDepth)
        if len(coarsePickArgs)<minNumPicks:
            search=False
            break
        # Given this rough origin, do a search about it
        buff=0.5/tResSplit
        roi=[maxLoc[0]-gridSpace*(0.5+buff),maxLoc[0]+gridSpace*(0.5+buff),
             maxLoc[1]-gridSpace*(0.5+buff),maxLoc[1]+gridSpace*(0.5+buff),
             mainRoi[4],mainRoi[5]]
        maxLoc,maxTime,gridSpace,finePickArgs=iterAlignLocate(vdInfo,staLocs,staNames,roi,pickSet[coarsePickArgs],
                                                                   tRes,tResSplit,buff,useOneDepth=False)
        # Remove any picks were were included in this iterations pickSet
        pickSet=np.array([entry for i,entry in enumerate(pickSet) if i not in coarsePickArgs[finePickArgs]])
        # Append this iterations event if the number of stacked picks >= minNumPicks
        ## May want to allow searching again, as number of picks could drop...##
        ## ...a lot from coarse->fine (allowing another position to show)##
        if len(finePickArgs)<minNumPicks:
            search=False
        else:
            eveLocs.append([len(eveLocs)]+list(maxLoc)+[maxTime])
    eveLocs=np.array(eveLocs,dtype=str)
    if len(eveLocs)==0:
        eveLocs=np.empty((0,5))
    return eveLocs

# Run a iteration of align locate with a specified grid and resolution
def iterAlignLocate(vdInfo,staLocs,staNames,roi,pickSet,
                    tRes,tResSplit,numXyTres,
                    useOneDepth=False,testDepth=None):
    # First test grid spacing (taken from distance of center of a grid cell to a corner)
    errXY=2*(((vdInfo['Vs']*tRes*numXyTres)**2)/2.0)**0.5
    # Make a grid space to test locations
    xi=np.arange(roi[0],roi[1],errXY)
    yi=np.arange(roi[2],roi[3],errXY)
    if useOneDepth:
        zi=np.array([testDepth])
    else:
        zi=np.arange(roi[4],roi[5],errXY)
    xGrid,yGrid,zGrid=np.meshgrid(xi,yi,zi)
    gridLocs=np.array([xGrid.flatten(),yGrid.flatten(),zGrid.flatten()]).T
    # Reference each pick to a station index
    pickSet,pickStaIdxs=getPickStaIdxs(pickSet,staNames)
    pickTimes=pickSet[:,2].astype(float)
    # Increase the index for S-picks (easier referencing later)
    pickStaIdxs[np.where(pickSet[:,1]=='S')]+=len(staNames)
    # Calculate the distance for all locations and stations
    dists=cdist(gridLocs,staLocs)
    # Convert distance to travel times for P and S (P/S is left/right side of array)
    tt=np.hstack((calcPSarrivalTimes(vdInfo,dists)))
    # Align the times based of their delay (so they all represent origin times "origPicks")
    origPicks=pickTimes-tt[:,pickStaIdxs]
    # numTresErr is the factor times tRes which accounts for all potential error in the aligned times (from the true time)
    numTresErr=getGridError_NumTres(roi,tRes,numXyTres,tResSplit,vdInfo['Vs'],
                                    useOneDepth=useOneDepth,testDepth=testDepth)
    # Create an array of histograms for each event (row)
    bins=np.arange(np.min(pickTimes)-np.max(tt)-(numTresErr+1)*tRes,np.max(pickTimes),float(tRes)/tResSplit)
    counts=np.apply_along_axis(lambda a: np.histogram(a, bins=bins)[0], 1, origPicks)
    # Stack the bins cumulatively, use 2x (both ways from origin) numTresErr for the index
    cumSumLen=int(np.ceil(2*numTresErr*tResSplit))
    counts=np.cumsum(counts,axis=1)
    counts=counts[:,cumSumLen:]-counts[:,:-cumSumLen]
    # Assign origin times to each of the bins
    origTimes=bins[cumSumLen:]-tRes+float(tRes)/tResSplit
    # Collect the max position, and see which picks filled this grid cell...
    maxArg=np.unravel_index(np.argmax(counts.flatten()),counts.shape)
    # ...get the origin location and time
    maxLoc,maxOrigTime=gridLocs[maxArg[0]],origTimes[maxArg[1]]
    # ...calculate the travel times for these picks
    maxDists=cdist([maxLoc],staLocs)
    maxTT=np.hstack((calcPSarrivalTimes(vdInfo,maxDists)))
    maxOrigPicks=(pickTimes-maxTT[0,pickStaIdxs])
    maxResiduals=np.abs(maxOrigPicks-maxOrigTime)
    # ...take only the picks which have an abs(residual) < numTresErr*tRes...
    # ...max 1 per station and phase type "staTypes"
    posPickArgs,seenStaTypes=[],[]
    for arg in np.argsort(maxResiduals):
        staType=pickSet[arg,0]+'-----'+pickSet[arg,1]
        if maxResiduals[arg]<float(tRes)*numTresErr and staType not in seenStaTypes:
            posPickArgs.append(arg)
            seenStaTypes.append(staType)
#    print(np.mean(maxOrigPicks-maxOrigTime),float(tRes)*numTresErr)
    return maxLoc,maxOrigTime,errXY,np.array(posPickArgs,dtype=int)


from xml.etree.ElementTree import Element
from .. import TReqz

class reqif_attribute_definition_enumeration(TReqz.reqif_attribute_definition):

    def __init__(self, content: Element = None, id_dict=None):
        self.multi_valued: str = None  # attribute, required
        super(reqif_attribute_definition_enumeration,
              self).__init__(content, id_dict)
        self.name = "ATTRIBUTE-DEFINITION-ENUMERATION"

    def getValueMap(self):
        valueDefinitions = self.type.specified_values
        valueMap = dict()
        for possibleValues in valueDefinitions:
            valueMap[possibleValues.embedded_value.key] = possibleValues.long_name
        return valueMap

    def getReversedValueMap(self):
        valueMap = self.getValueMap()
        reversedValueMap = dict(
            map(reversed, valueMap.items()))
        return reversedValueMap

    def getValueMapWithEnumObjects(self):
        valueDefinitions = self.type.specified_values
        valueMap = dict()
        for possibleValues in valueDefinitions:
            valueMap[possibleValues.embedded_value.key] = possibleValues
        return valueMap

    def decode(self, content: Element, id_dict: TReqz.reqif_id_dict = None):
        super().decode(content, id_dict)
        namespace = TReqz.xml_utils.get_tag_namespace(content.tag)

        typeList = {
            "ATTRIBUTE-VALUE-ENUMERATION": "reqif_attribute_value_enumeration"}
        self.default_value = TReqz.reqif_utils.generate_object_list_by_element_class(
            content, id_dict, "./{0}DEFAULT-VALUE".format(namespace), typeList)

        self.multi_valued = TReqz.reqif_utils.unescapeAttribute(content.get("MULTI-VALUED"))
        self.type = TReqz.reqif_utils.get_local_ref_from_element_text(
            content, id_dict, "./{0}TYPE/{0}DATATYPE-DEFINITION-ENUMERATION-REF".format(namespace))

    def encode(self):
        elem = super().encode()
        elem.tag = self.name
        TReqz.xml_utils.setElementAttribute(
            elem, "MULTI-VALUED", TReqz.reqif_utils.escapeAttribute(self.multi_valued))

        if self.default_value != None:
            defaultElement = TReqz.xml_utils.addRequiredSubElement(
                elem, "DEFAULT-VALUE")
            if defaultElement != None and self.default_value != None:
                for value in self.default_value:
                    TReqz.xml_utils.addEncodedSubElement(defaultElement, value)

        if self.type != None:
            typeElement = TReqz.xml_utils.addRequiredSubElement(elem, "TYPE")
            TReqz.xml_utils.addRequiredSubElement(
                typeElement, "DATATYPE-DEFINITION-ENUMERATION-REF", self.type.identifier)
        return elem


from database import Database
import os, json,sys, uuid

json_final = list()

db = Database(os.path.dirname(os.path.realpath(__file__))+'/database.sqlite')
db.exportTo(json_final)

# Check arguments
if len(sys.argv) > 1:
  arg = sys.argv[1]

if arg == '--anonymous':
    for someone in json_final:
        someone['name'] = someone['fbid'] = someone['username'] = str(uuid.uuid4())
        for place in someone['locations']:
            place['img_link'] = place['permalink'] = 'https://www.petfinder.com/wp-content/uploads/2012/11/140272627-grooming-needs-senior-cat-632x475.jpg'

with open('EXPORT.json', 'w') as outfile:
    json.dump(json_final, outfile)


#!/usr/bin/env python
# a -> 1 b-2 and so on to z -26
# write a function to find the number of possibility of decodes can happen for a given number
# Example 111 gives possible decodes as 'aaa', 'ka', 'ak' then number of possibilies are 3

# reccursion
def helper(data, k):
    if k == 0:#false
        return 1

    s = len(data) - k
    if data[s] == '0':#false
        return 0

    result = helper(data , k-1)#Called Helper again
    if k>=2 and int(data[s:s+2]) <=26 :#
        print("value of k is", k)
        result += helper(data , k-2)
    return result

def num_ways(data):
    #memo = [None]*(len(data)+1)
    return helper(data, len(data))

def main():
    print ("Possibilities of getting the decoded vaues are: ",num_ways("123123123"))
    #aaa or ka or ak
if __name__ == "__main__":
    main()


#!/usr/local/bin/python3.3
# -*- coding: utf8 -*-

import sys
import re

DELIMITER = "]"

# Message
ERROR_MESSAGES = {
        "101" : "지정된 이름에 문제가 발견되었습니다.",
        "102" : "파일을 읽으려는 도중 에러가 발생했습니다",
        "999" : "알 수 없는 오류가 발생했습니다"
      }

class ErrorLogParser:

    def __init__(self,inputFile):
        self.dErrorInfo = {}
        self.inputFile = inputFile
        self._makeErrorDataMsg()
        self.isCheckImage = False

    def _makeErrorDataMsg(self):
        try:
            errorFile = open(self.inputFile)

            for line in errorFile:
                if line.find("[error]") != -1:
                    #인자로 함수를 바로 넣었다
                    self._appendErrorMsg(self._findMessage(line))

            """
            sReportData = getReportData(self.dErrorInfo)
            writeReport(sReportData)
            """

        except NameError as detail:
            self._throwError(101,detail)

        except IOError as detail:
            self._throwError(102,detail)

        finally :
            errorFile.close()

    """
        getErrorData 메서드는 객체 형태의 에러데이터를 반환한다
        @ method getErrorData
        @ param {void}
        @ return {Dictionaries} , {errorMsg , [count, [detailErrorMsg1, detailErrorMsg2....] }
    """
    def getErrorData(self):
        return self.dErrorInfo

    @property
    def errdata(self):
        print("getting errordata");
        return self.dErrorInfo

    """
        execReport 메서드는 최종결과를 출력을 한다
        @ method execReport
        @ param {String} toPrint 화면에 출력할 파일명 . 없으면 시스템의 표준 출력 장치에 출력(대부분 콘솔화면)
        @ return {void}
    """
    def execReport(self, toPrint=sys.stdout):
        sReportData = self._getReportData()
        self._writeReport(sReportData,toPrint)

    """
        setExceptImage 메서드는 에러로그 중 이미지파일 확장자를 가진 것을 제외한다
        @ method setExceptImage
        @ param {Boolean} isTrue 이미지파일 확장자를 제외할 것인지. 기본 값은 True
        @ return {void}
    """
    def setExceptImage(self,isTrue=True):
        self.isCheckImage = isTrue;

    """
    ###  private method start
    """

    def _writeReport(self, sReportData, toPrint):
        try:
            with open('report.txt' , 'w') as reportFile:
                #print(sReportData, file=reportFile)
                print(sReportData, file=toPrint)   #console에 출력하기. (표준 출력 장치)

        except IOError as detail:
            self._throwError(102,detail)

    # with문을 사용해서 file flushing이 필요 없다
    def _getReportData(self):

        aReportData = []

        aReportData.append("[SUMMARY REPORT]")
        for name,detail in self.dErrorInfo.items():
           aReportData.append("\"%s\" :  %d개\n" % (name,detail[0]))

        aReportData.append("\n\n")
        aReportData.append("[Detail REPORT]\n")
        aReportData.append("[여기서 한글 테스트 한 번 합니다]\n")

        nIndex = 1
        for name,detail in self.dErrorInfo.items():
            aReportData.append("--------------------------------------------\n")
            aReportData.append("%d.%s\n" % (nIndex,name))
            for detailMsg in detail[1]:
                #check image file (regexp)
                #if not self._checkIsImageFile(detailMsg):
                if not self.isCheckImage or not self._checkIsImageFile(detailMsg):
                    aReportData.append(detailMsg+"\n")
            nIndex += 1;

        return "".join(aReportData);

    def _checkIsImageFile(self, msg):
        # ico, gif, png, jpg 를 가진 확장자인경우
        # 확장자로 끝나거나 문자가 아닌 것(0-9,A-Z,a-z,_)이 올 수 있음
        m = re.search('\.(ico|gif|png|jpg)($|[\W])',msg)

        #3항연산자
        return True if m else False

    def _appendErrorMsg(self, tMsg):
        errorMsg,errorDetailMsg = tMsg

        if not errorMsg in self.dErrorInfo:
            #initiate value
            self.dErrorInfo[errorMsg] = []
            self.dErrorInfo[errorMsg].append(1)
            self.dErrorInfo[errorMsg].append([])  #[count , []]
            self.dErrorInfo[errorMsg][1].append(errorDetailMsg) #[count , [errMsg,errMsg2.....]]

        else:
            #addition message count
            self.dErrorInfo[errorMsg][0] += 1
            #append detail message
            if not errorDetailMsg in self.dErrorInfo[errorMsg][1]:
                self.dErrorInfo[errorMsg][1].append(errorDetailMsg)

    def _findMessage(self,str):

        if str.find("[error]") != -1:
            msgPart = str.split(DELIMITER)
            errMsg  =  msgPart[3].split(":")[0].strip()
            temp    = msgPart[3].split(":")[1:]
            errorDetailMsg = "".join(temp).strip()

            return (errMsg,errorDetailMsg)

    def _throwError(self, code, detailMsg = ""):
        code = str(code)
        print("[error %s] %s" % (code, ERROR_MESSAGES[code]))

        if detailMsg:
            print("\n[자세한 오류 내용]\n",detailMsg)

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

if __name__ == "__main__":
    oElp = ErrorLogParser("/Users/nhn/src/python/pythonBasic/files/error_log")
    msg = oElp.getErrorData();
    print(msg)

    """
    #test for Property Descriptor (getter)
    print(oElp.errdata)
    rint("\n")
    """

    #oElp.setExceptImage()
    oElp.execReport();

else :
    print("called -> " , __name__)

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


import torch
import glob
import cv2
from PIL import Image
from torchvision import transforms
import numpy as np
from demos.demo1.base_resnet import resnet
import os

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

net = resnet()

model_path = os.path.abspath(os.path.join(os.getcwd(), "models/resnet/159.pth"))

net.load_state_dict(torch.load(model_path))

im_list = glob.glob("../../datasets/cifar-10/test/*/*")

np.random.shuffle(im_list)
net.to(device)

label_name = ["airplane", "automobile", "bird",
              "cat", "deer", "dog",
              "frog", "horse", "ship", "truck"]

test_transform = transforms.Compose([
    transforms.CenterCrop((28, 28)),
    transforms.ToTensor(),
    transforms.Normalize((0.4914, 0.4822, 0.4465),
                         (0.2023, 0.1994, 0.2010)),
])

for im_path in im_list:
    net.eval()
    im_data = Image.open(im_path)

    inputs = test_transform(im_data)
    inputs = torch.unsqueeze(inputs, dim=0)

    inputs = inputs.to(device)
    outputs = net.forward(inputs)

    _, pred = torch.max(outputs.data, dim=1)
    print(label_name[pred.cpu().numpy()[0]])

    img = np.asarray(im_data)
    img = img[:, :, [1, 2, 0]]

    img = cv2.resize(img, (300, 300))
    cv2.imshow("im", img)
    cv2.waitKey()


import matplotlib.pyplot as plt
from scipy.stats.mstats import mquantiles
from numpy import exp

import sys
sys.path.append('..\\..\\')
from GetParamStats import GetParamValues2 as GetParamValues
#Function to get recruit-values from .csv vile
from nGetParamStats import nGetParamValues
sys.path.append('..\\')
from hdf5file import *
nthin=2000
lnM=GetParamValues(hdf5file, 'lnM',nthin=nthin)

lnM.sort()
n=len(lnM)
lowM=[exp(lnM[i]) for i in range(int(  n/3))   ]
medM=[exp(lnM[i]) for i in range(int(  n/3),int(2*n/3))  ]
uppM=[exp(lnM[i]) for i in range(int(2*n/3),n)   ]
bins=[0.004*i  for i in range(1+int(0.16/0.004))]
plt.close()

plt.hist(lowM,bins=bins,edgecolor='r',alpha=0.75,hatch='x',facecolor='none',linewidth=3)
plt.hist(medM,bins=bins,color='k'          ,alpha=0.25,                     linewidth=3)
plt.hist(uppM,bins=bins,edgecolor='b',alpha=0.75,hatch='*',facecolor='none',linewidth=3)

plt.title('FA3 Mortality Levels')
plt.xlabel('Natural Mortality Rate (year'+r'$-1$'+')')
plt.ylabel('Frequency')

import matplotlib.patches as mpatches
upppatch=mpatches.Patch(edgecolor='b',alpha=0.75,hatch='*',facecolor='white',linewidth=3,label='High')
midpatch=mpatches.Patch(color='k'          ,alpha=0.25,                                 label='Medium')
lowpatch=mpatches.Patch(edgecolor='r',alpha=0.75,hatch='x',facecolor='white',linewidth=3,label='Low')
plt.legend(handles=[upppatch,midpatch,lowpatch],title='Mortality Level')
plt.xlim(0,0.16)

plt.savefig('FA3_TriHist.png', format="png")
plt.show()


#!/usr/bin/env python3

from cryptography.fernet import Fernet
import os
import sys
import json

_version = "0.1.0"
_creds = ".jolene/creds.json"

def _key():
    key = os.environ.get("JOLENE")
    if key is None: return None
    return key.encode()

def _set_key(key):
    return f"export JOLENE={key}"

def _fernet():
    return Fernet(_key())

def _gen_key():
    key = Fernet.generate_key().decode()
    os.environ['JOLENE'] = key
    return key

def decrypt(string):
    return _fernet().decrypt(string.encode()).decode()

def encrypt(string):
    return _fernet().encrypt(string.encode()).decode()

def encrypt_dic(dic):
    enc = {}
    for key, value in dic.items():
        enc[key] = encrypt(value)

    return enc

def write(dic):
    print('writing to file', dic)
    with open(_creds, "w") as write_file:
        json.dump(dic, write_file)

def _load_creds():
    with open(_creds) as rf:
        return json.load(rf)

def user_input_data():
    print(f"Enter data to ecode in JSON format with JOLENE. \n eg. username: jolene, password: i love jolene, date: 7/10/2017")
    entering = True

    data = {}
    inp = input(":: ")

    for row in inp.split(", "):
        key, value = row.split(": ")
        data[key] = value

    return data

def dic_diff(og, edit):
    for key, value in edit.items():
        og[key] = value
    return og

def add_data(dic):
    new = encrypt_dic(dic)
    write(dic_diff(_load_creds(), new))

def get(key):
    creds = _load_creds()
    if key in creds:
        return decrypt(creds[key])

    return None

def run_cli(args):
    print(f" \n\n\tJOLENE 💈  \n\n \tv{_version}\n")
    if _key() is not None:
        print(f"Current ENV key is {_key().decode()} \n")
        inp = input(f"Add data to JOLENE? (y/n): ")
        if inp == "y":
            add_data(user_input_data())
            return

    inp = input(f"Remove everything and start over (y/n): ")
    if (inp != "y"): return
    print(f" \n\n\tJOLENE SETUP 💈 \n\n")
    print(f"By continuing {_creds} will be obliterated and the current JOLENE key will be rendered useless. \n ")
    input("Ok?")

    input("Generate new JOLENE key [enter] \n")
    new_key = _gen_key()
    print(f"New key is {new_key} \n")

    data = user_input_data()

    enc = encrypt_dic(data)
    print(enc)
    write(enc)

    print(f"Exported encrypted data to {_creds} \n")
    print(f"Please run $\t {_set_key(new_key)}")


import json
from datetime import datetime

epoch = datetime.utcfromtimestamp(0)

depth = 9 # 10s intervals
m = {}

def unix_time_millis(datetime):
    return str((datetime - epoch).total_seconds() * 1000.0).replace(".0", "")

def convertEvents(sysmon):
    for event in sysmon:
        if "Microsoft-Windows-Sysmon" in event:
            event = json.loads(event)
            timestamp = event['@timestamp']
            parsed = datetime.strptime(timestamp,"%Y-%m-%dT%H:%M:%S.%fZ")
            millis = unix_time_millis(parsed)
            top = millis[:depth]

            if top in m:
                m[top] += 1
            else:
                m[top] = 1

with open('./caldera_attack_evals_round1_day1_2019-10-20201108.json', 'r') as sysmon:
    convertEvents(sysmon)

with open('./empire_apt3_2019-05-14223117.json', 'r') as sysmon:
    convertEvents(sysmon)

for x in m:
    print(f"({x},{m[x]})")


# url : https://leetcode.com/problems/3sum-with-multiplicity/
# tag : math,dp

# 枚举所有加起来等于 target 的组合

class Solution:
    def threeSumMulti(self, A, target):
        """
        :type A: List[int]
        :type target: int
        :rtype: int
        """
        N = 103
        Mod = 1000000007
        cnt = [0] * N
        for i in A:
            cnt[i] += 1
        ret = 0
        # print(cnt)
        for i in range(N):
            if i * 3 == target:
                ret += cnt[i] * (cnt[i]-1) * (cnt[i]-2) // 6
                ret %= Mod

            for j in range(i+1, N):
                if i*2+j == target:
                    ret += cnt[i] * (cnt[i]-1) // 2 * cnt[j]
                    ret %= Mod

                if i+j*2 == target:
                    ret += cnt[i] * cnt[j] * (cnt[j]-1) // 2
                    ret %= Mod

                for k in range(j+1, N):
                    if i+j+k == target:
                        ret += cnt[i] * cnt[j] *cnt[k]
                        ret %= Mod

        return ret

s = Solution()
print(s.threeSumMulti([1,1,2,2,3,3,4,4,5,5], 8))
print(s.threeSumMulti([1,1,2,2,2,2], 5))
print(s.threeSumMulti([3,3,1], 7))

LeetCode/923. 3Sum With Multiplicity/solution.py


#encoding: UTF-8

# Autor: Fernando Sebastian Silva Miramontes, A01746925
# Descripcion: Calcular porcentaje de hombres y mujeres de una poblacion estudiantil

numDeM = float(input("Cuantas mujeres hay?\n"))
numDeH = float(input("Cuantos hombres hay?\n"))
total = numDeH+numDeM
porcDeM = numDeM/total *100
porcDeH = numDeH/total *100
print("Hay "+str(total)+" estudiantes inscritos")
print("El "+str(porcDeM)+"% son mujeres")
print("El "+str(porcDeH)+"% son hombres")


import numpy as np
import lg
import matplotlib.pyplot as plt
import pf as pf

def sample_data(n,params,propagation_freq,return_state=False):
    '''
    samples data occasionally
    '''

    fparams = params.fparams
    gparams = params.gparams

    L = fparams['L']
    L_R = np.linalg.cholesky(gparams['R'])
    assert L.shape[0]==L.shape[1]
    D = L.shape[0]

    X = np.zeros((n,propagation_freq,D))
    X[0,-1,] = lg.musample(1,params.muparams)[0].ravel()
    Y = np.zeros((n,D))

    noise_eps= np.random.randn(*(X[0,-1,].shape))
    Y[0,] = X[0,-1,] + np.dot(noise_eps,L_R.T)
    for t in range(1,n):
        X[t,] = fsample(X[t-1,-1,].reshape((1,D)),fparams,propagation_freq)[0].reshape((propagation_freq,D))
        noise_eps= np.random.randn(*(X[t,-1,].shape))
        Y[t,] = X[t,-1,] + np.dot(noise_eps,L_R.T)

    if return_state:
        return Y,X
    else:
        return Y

def fsample(Xtm1,fparams,propagation_freq):
    '''
    samples repeated iterations of the normal lg transition
    '''

    assert Xtm1.ndim==2
    N,D = Xtm1.shape

    if 'mu' in fparams:
        mu = fparams['mu']
        assert mu.shape[0]==D
        assert mu.ndim==1
    else:
        mu = np.zeros(D)

    L = fparams['L']

    epst = np.zeros((propagation_freq,N,D))
    X = np.zeros((propagation_freq,N,D))

    epst[0,] = np.random.randn(*(Xtm1.shape))
    X[0,] = mu + np.dot( (Xtm1-mu), np.asarray(fparams['A']).T ) + np.dot(epst[0,],L.T)
    for t in range(1,propagation_freq):
        epst[t,] = np.random.randn(*(Xtm1.shape))
        X[t,] = mu + np.dot( (X[t-1,]-mu), np.asarray(fparams['A']).T ) + np.dot(epst[t,],L.T)

    return X,epst

def plot_state_and_observations(X,Y,D_max=10):
    '''
    method primarily used to plot the true state and observations
    '''
    assert X.ndim==3 and Y.ndim==2

    # get y plotting indices
    n,propagation_freq,D = X.shape
    if D>=D_max:
        D_plt = D_max
    else:
        D_plt = D

    # plot state
    fig = plt.figure('state and obs')

    y_plot_indices = np.arange(n)*propagation_freq
    # plot each dimension of y
    axes = []
    for i in range(D_plt):
        ax = fig.add_subplot(D_plt,1,i+1)
        ax.plot(X.reshape((n*propagation_freq,D))[(propagation_freq-1):,i],linewidth=0.5,label='$X$')
        ax.plot(y_plot_indices,Y[:,i],'x',label='$Y$')
        x_annotate = '$X_%i$' % (i+1)
        ax.set_ylabel(x_annotate)
        axes.append(ax)

    axes[0].set_title(r'Latent state and observations')
    axes[0].legend()
    axes[-1].set_xlabel(r'$\frac{t}{\Delta T}$')
    return axes

def plot_filtered_state(Xtrue,Xf,Y,D_max=10):
    '''
    method used to plot filtered state and true state
    '''

    n,propagation_freq,D = Xtrue.shape
    assert Xf.shape==(n,D)
    assert Y.shape==(n,D)

    if D>=D_max:
        D_plt = D_max
    else:
        D_plt = D

    # plot filtered state
    filtered_fig = plt.figure('filtered state')
    filt_axes = []
    for i in range(D_plt):
        ax_filt = filtered_fig.add_subplot(D_plt,1,i+1)
        ax_filt.plot( Xf[:,i],label='$X_f$',linewidth=0.5)
        ax_filt.plot( Xtrue[:,-1,][:,i],label='$X$',linewidth=0.5)
        ax_filt.plot( Y[:,i],label='$Y$' ,linewidth=0.5)
        filt_axes.append(ax_filt)

    filt_axes[-1].legend()
    filt_axes[-1].set_xlabel('$t$')
    return filt_axes

if __name__== '__main__':

    D = 5
    n = 20
    N = 10000

    propagation_freq = 20

    A =  0.9*np.eye(D)
    A += -np.ones(D)*0.1

    Q_sqrt = 0.5*np.random.randn(D,D)
    Q = np.dot(Q_sqrt,Q_sqrt.T)
    R = 1*np.eye(D)
    mu0 = 1*np.ones(D)
    Sigma0 = 1*np.eye(D)
    mu = 5*np.ones(D)

    # pf arguments
    muparams,fparams,gparams = lg.multivariate_params(A,Q,R,mu0,Sigma0,mu)
    params = pf.pf_params(muparams,fparams,gparams)
    #distributions = pf.pf_distributions(lg.musample,fsample,lg.log_gdensity)

    distributions = pf.pf_distributions(lg.bootstrap_sample_particles,lg.bootstrap_log_inc_weight,lg.bootstrap_sample_particles_initial,lg.bootstrap_log_inc_weight_initial)

    # sample observations
    Y,Xtrue = sample_data(n,params,propagation_freq,return_state=True)
    plt.rc('text',usetex=True)
    plt.rc('font',family='serif')
    axes = plot_state_and_observations(Xtrue,Y)

    pf_settings = pf.get_pf_settings(False,rel_var_est=False,ess_threshold=0.1)
    logZ,X,W,log_w,epst,A =  pf.pf(Y,N,params,distributions,pf_settings)

    Xf = (W[:,:,np.newaxis]*X).sum(1)
    axes_filtered =  plot_filtered_state(Xtrue,Xf,Y)

    # estimate likelihood with kf
    logZtrue = lg.kf_like_est(Y,params,propagation_freq)

    # plot likelihood
    f2 = plt.figure('likelihood')
    ax2 = f2.add_subplot(111)
    ax2.plot(logZ,linewidth=0.5,label='pf')
    ax2.plot(logZtrue,linewidth=0.5,label='kf')
    ax2.legend()
    ax2.set_title('Conditional likelihood')
    ax2.set_xlabel('$t$')
    plt.show()


from flask import Flask, Response, request
from kafka import SimpleProducer, KafkaClient
#  connect to Kafka
kafka = KafkaClient('localhost:9092')
producer = SimpleProducer(kafka)
# Assign a topic
topic = 'messages'

# Continuously listen to the connection and print messages as recieved
app = Flask(__name__)

@app.route('/analytics')
def index():
    x = request.args.get('x', type=int)
    y = request.args.get('y', type=int)
    msg = 'X: {};  Y: {}'.format(x, y)
    producer.send_messages('mouse', msg.encode())
    return Response('OK',
                    content_type='text/plain; charset=utf-8')

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)


from CoviDNetwork import *

H = nx.Graph()

H = add_cluster(H, 10, 8, 'S')
H = add_cluster(H, 10, 7, 'S')
H = add_cluster(H, 5, 3, 'I')

add_edges(H, 0, 9, 9, 19, 0.25)
add_edges(H, 0, 19, 20, 24, 0.4)

print((H.nodes(data = True)))
print("\n")
print((H.edges(data = True)))

S = []
I = []
R = []

def update(num, layout, G, ax):
    ax.clear()
    color = []
    s = 0
    i = 0
    r = 0
    for node in G.nodes():
        if G.nodes[node]['state'] == 'I':
            color.append('r')
            i += 1
        elif G.nodes[node]['state'] == 'S':
            color.append('b')
            s += 1
        elif G.nodes[node]['state'] == 'R':
            color.append('g')
            r += 1
    S.append(s)
    I.append(i)
    R.append(r)

    nx.draw(G, pos = layout, node_color = color, with_labels = True, ax = ax)

    # Set the title
    ax.set_title("Frame {}".format(num))
    remove(G)
    infect(G)

def animate(G):

    # Build plot
    fig, ax = plt.subplots(figsize=(6,4))

    layout = nx.spring_layout(G)

    ani = animation.FuncAnimation(fig, update, interval=750, fargs=(layout, G, ax))
    # ani.save('animation_1.gif', writer='imagemagick')
    plt.show(1)

animate(H)

print('\n')
print(S)
print(I)
print(R)
plt.plot(S)
plt.plot(I)
plt.plot(R)
plt.show(1)


class Physics:
    @staticmethod
    def is_collision_detection(object1, object2):
        if int(object1.x) > int(object2["width"]):
            return True
        elif int(object1.y) > int(object2["height"]):
            return True
        elif object1.x < 0:
            return True
        elif object1.y < 0:
            return True

        return False

    @staticmethod
    def is_squares_colliding(square1, square2):
        if square1.x == square2.x and square1.y == square2.y:
            return True

        return False

    @staticmethod
    def is_snakehead_colliding_with_body(head, body):
        pass


import cPickle
import os.path
import hashlib
import uuid

# TODO: load/save should not include the player_dir
# TODO: Create Saveable class, for player, pass, and map?  include version?

class Password(object):
    def __init__(self, name, player_dir):
        self.name = name
        self.hash = ""
        self.salt = ""
        self.player_dir = player_dir

    def do_hash(self, password, salt):
        return hashlib.sha512(password + salt).hexdigest()

    def load(self):
        with open(self.player_dir + self.name + '.pwd', 'rb') as in_file:
            self.name, self.hash, self.salt = cPickle.load(in_file)

    def save(self, password):
        # TODO: Atomic save, by rotating old files?
        self.salt = uuid.uuid4().hex;
        self.hash = self.do_hash(password, self.salt)
        with open(self.player_dir + self.name + '.pwd', 'wb') as out_file:
            cPickle.dump((self.name, self.hash, self.salt), out_file, cPickle.HIGHEST_PROTOCOL)

    def check(self, password):
        return self.hash == self.do_hash(password, self.salt)

class Item(object):
    def __init__(self, name, description):
        self.name = name
        self.description = description

class Equipment(Item):

    # Wear slots
    HEAD = 0
    TORSO = 1
    LEFT_HAND = 2
    RIGHT_HAND = 3
    LEGS = 4
    BACK = 5
    MAX_WEAR = 6

    # Name of wear slots
    wear_names = {
        HEAD: "Head",
        TORSO: "Torso",
        LEFT_HAND: "Left Hand",
        RIGHT_HAND: "Right Hand",
        LEGS: "Legs",
        BACK: "Back"
    }

    def __init__(self, name, description, wear=[], mods=[]):
        Item.__init__(self, name, description)
        self.wear = wear
        self.mods = mods

    def to_string(self):
        mod_strs = []
        for mod in self.mods:
            if mod.value < 0:
                if mod.is_percent:
                    mod_strs.append('{} {}%'.format(Stat.names[mod.stat], int(mod.value*100)))
                else:
                    mod_strs.append('{} {}'.format(Stat.names[mod.stat], mod.value))
            else:
                if mod.is_percent:
                    mod_strs.append('{} +{}%'.format(Stat.names[mod.stat], int(mod.value*100)))
                else:
                    mod_strs.append('{} +{}'.format(Stat.names[mod.stat], mod.value))
        return '{} ({})'.format(self.name, ', '.join(mod_strs))

class Character(object):
    def __init__(self, name, hp, mp, location):
        self.name = name
        self.hp = hp
        self.mp = mp
        self.stats = StatTable(stats={Stat.STRENGTH: 11, Stat.SPEED: 8, Stat.INTELLIGENCE: 5, Stat.MELEE: 4})
        self.location = location
        self.wear = []

    def mod_hp(self, hp):
        self.hp += hp
        if self.hp < 0:
            self.hp = 0

class NPC(Character):

    AGGRO_FRIENDLY = 0
    AGGRO_NEUTRAL = 1
    AGGRO_HOSTILE = 2

    def __init__(self, name, hp, mp, location, aggro, game_server):
        Character.__init__(self, name, hp, mp, location)
        self.aggro = aggro
        self.engaged = None  # Character this NPC is fighting
        self.game_server = game_server

    def act(self):
        # 1) If engaged, fight that character
        if self.engaged is not None:
            self.game_server.add_action(AttackAction(actor=self, target_character=self.engaged))

        # 2) If not engaged, find someone to fight if hostile
        #elif self.AGGRO_HOSTILE:

    def engage(self, character):
        """
        Engage a character as they enter the room. This could be a shop owner saying hi to a shopper, or a hostile
        monster attacking a PC
        """
        if self.aggro == NPC.AGGRO_HOSTILE:
            self.engaged = character
            self.act()
            return '{} prepares to fight {}'.format(self.name, character.name)

        if self.aggro == NPC.AGGRO_FRIENDLY:
            return '{} waves to {}'.format(self.name, character.name)

        return None

    def disengage(self):
        self.engaged = None

class Player(Character):
    def __init__(self, name, player_dir, net_handler):
        # TODO: This is all test stuff
        Character.__init__(self, name, 10, 10, (0,0,0))
        self.player_dir = player_dir
        self.net_handler = net_handler
        self.inventory = [
            Equipment(
                'Iron Longsword',
                'Sturdy sword made of iron.  Might be useful in a fight',
                wear=[Equipment.RIGHT_HAND, Equipment.LEFT_HAND],
                mods=[StatMod(Stat.MELEE, 2, False)]
            ),
            Equipment(
                'Staff of Healing',
                'Smooth maple staff that emits a dim yellow aura',
                wear=[Equipment.RIGHT_HAND, Equipment.LEFT_HAND],
                mods=[StatMod(Stat.INTELLIGENCE, 2, False), StatMod(Stat.CAST_HEAL, .25, True)]
            ),
            Equipment(
                'Berserker Helmet',
                'Helmet that enrages the wearer',
                wear=[Equipment.HEAD],
                mods=[StatMod(Stat.ARMOR, 3, False), StatMod(Stat.MELEE, 5, False), StatMod(Stat.INTELLIGENCE, -5, False)]
            ),
            Equipment(
                'Heavy Chainmail',
                'Thick coat of chainmail.  Offers excellent protection from physical damage.',
                wear=[Equipment.TORSO],
                mods=[StatMod(Stat.ARMOR, 7, False), StatMod(Stat.SPEED, -.5, True)]
            )
        ]

    def serialize(self):
        return self.name, self.hp, self.mp, self.location

    def deserialize(self, data):
        self.name, self.hp, self.mp, self.location = data

    def save(self):
        # TODO: Atomic pickle, by rotating old pickle files?
        with open(self.player_dir + self.name + '.save', 'wb') as out_file:
            cPickle.dump(self.serialize(), out_file, cPickle.HIGHEST_PROTOCOL)
        self.net_handler.update_prompt()

    def load(self):
        with open(self.player_dir + self.name + '.save', 'rb') as in_file:
            self.deserialize(cPickle.load(in_file))

def player_exists(name, player_dir):
    return os.path.isfile(player_dir + name + '.save')

class Action(object):
    def __init__(self, actor, target=None, target_character=None, kwargs={}):
        self.actor = actor
        self.target = target
        self.target_character = target_character
        self.location = actor.location
        self.kwargs = kwargs
        self.pre_msg_str = None
        self.zero_target_error_msg_str = None

    def pre_msg(self):
        if self.time > 0 and self.pre_msg_str is not None:
            return self.pre_msg_str.format(self.actor.name)
        return None

    def zero_target_error_msg(self):
        if self.zero_target_error_msg_str is not None:
            return self.zero_target_error_msg_str.format(self.target)
        return None

class SayAction(Action):
    def __init__(self, actor, target=None, target_character=None, kwargs={}):
        super(SayAction, self).__init__(actor, target, target_character, kwargs)
        self.time = 0
        self.pre_msg_str = '{} prepares to speak...'

    def execute(self):
        return '{} says \"{}\"'.format(self.actor.name, self.kwargs["msg"])

class AttackAction(Action):
    def __init__(self, actor, target=None, target_character=None, kwargs={}):
        super(AttackAction, self).__init__(actor, target, target_character, kwargs)
        self.time = 1.0  # TODO: Make the time actually dependent on the actor (e.g. actor.get_attack_speed())
        # NPCs will hit over and over.  Don't actually say this every single time
        if isinstance(actor, Player):
            self.pre_msg_str = '{} makes ready their weapon...'

    def execute(self):
        damage = 1
        self.target_character.mod_hp(-damage)

        if isinstance(self.target_character, Player):
            self.target_character.save()

        # If an NPC is attacked, set aggro to hostile and engage
        # If an NPC did the attacking, see if they need to attack again
        if isinstance(self.target_character, NPC) and self.target_character.hp > 0:
            self.target_character.aggro = NPC.AGGRO_HOSTILE
            if self.target_character.engaged is None:
                self.target_character.engage(self.actor)
        elif isinstance(self.actor, NPC) and self.target_character.hp > 0:
            self.actor.act()

        msg = '{} strikes {} for {} damage'.format(self.actor.name, self.target_character.name, damage)
        if self.target_character.hp <= 0:
            msg += '...and {} is slain.'.format(self.target_character.name)
        return msg

    def target_gone(self, target):
        if isinstance(self.actor, NPC) and self.actor.engaged == target:
            self.actor.disengage()

class AwarenessAction(Action):
    """
    NPCs use this action to detect new PCs in the room
    """
    def __init__(self, actor, target=None, target_character=None, kwargs={}):
        super(AwarenessAction, self).__init__(actor, target, target_character, kwargs)
        self.time = 1

    def execute(self):
        # TODO: possibly some check to see if the NPC can detect the PC
        if self.actor.engaged is None:
            return self.actor.engage(self.target_character)

class Stat(object):

    # TODO: Are these class variables??

    # Base stats
    STRENGTH = 0
    SPEED = 1
    INTELLIGENCE = 2

    # Secondary stats
    MELEE = 10
    ARMOR = 11
    RANGED = 20
    EVASION = 21

    WILLPOWER = 30
    CAST_FLAME = 31
    CAST_HEAL = 32
    CAST_CHILL = 33
    CAST_FLAMING_SWORD = 34

    # Name of the stat
    names = {
        STRENGTH: "Strength",
        SPEED: "Speed",
        INTELLIGENCE: "Intelligence",
        MELEE: "Melee Attack",
        ARMOR: "Armor",
        RANGED: "Ranged Attack",
        EVASION: "Evasion",
        WILLPOWER: "Willpower",
        CAST_FLAME: "Cast Flame",
        CAST_HEAL: "Cast Heal",
        CAST_CHILL: "Cast Chill",
        CAST_FLAMING_SWORD: "Cast Flaming Sword"
    }

    # Description of the stat
    # TODO: Descriptions like this should be in some sort of documentation resource file
    descriptions = {
        STRENGTH: "Physical strength and stamina.  Used to determine effectiveness of melee attacks and amount of HP.",
        SPEED: "Speed and dexterity.  Used to determine effectiveness of ranged attacks and evasion.",
        INTELLIGENCE: "Intellect and willpower.  Used to determine effectiveness of magical attacks and magical defense.",
        MELEE: "Strike an opponent with a weapon at close range.  Usage: equip a melee weapon and type 'attack <target>'",
        ARMOR: "Physical defense due to armor, physical constitution, or both.  Damage from melee and ranged attacks is reduced",
        RANGED: "Fire a ranged weapon at an opponent.  Usage: equip a ranged weapon and type 'attack <target>'",
        EVASION: "Ability to dodge melee and ranged attacks",
        WILLPOWER: "Damaged is reduced from magic attacks",
        CAST_FLAME: "Launch a fireball about as big as a fist towards an opponent.  Usage: 'cast flame <target>'",
        CAST_HEAL: "TBD",
        CAST_CHILL: "TBD",
        CAST_FLAMING_SWORD: "TBD"
    }

    def __init__(self, value):
        self._value = value
        self._mod_value = value

        self._mod_number = 0
        self._mod_percent = 1.0
        self._modifiers = []

    @property
    def base(self):
        return int(self._value)

    @property
    def modified(self):
        mod_val = int(round((self._value + self._mod_number) * self._mod_percent))
        if mod_val < 0:
            return 0
        return mod_val

    def add_modifier(self, value, is_percent):
        self._modifiers.append([value, is_percent])
        if is_percent:
            self._mod_percent += value
        else:
            self._mod_number += value

    def remove_modifier(self, value, is_percent):
        if [value, is_percent] in self._modifiers:
            self._modifiers.remove([value, is_percent])
            if is_percent:
                self._mod_percent -= value
            else:
                self._mod_number -= value

class StatTable(object):

    # Base modifiers (which base stats enhance a secondary stat)
    base_mod = {
        Stat.MELEE:                Stat.STRENGTH,
        Stat.ARMOR:                Stat.STRENGTH,
        Stat.RANGED:               Stat.SPEED,
        Stat.EVASION:              Stat.SPEED,
        Stat.WILLPOWER:            Stat.INTELLIGENCE,
        Stat.CAST_FLAME:           Stat.INTELLIGENCE,
        Stat.CAST_HEAL:            Stat.INTELLIGENCE,
        Stat.CAST_CHILL:           Stat.INTELLIGENCE,
        Stat.CAST_FLAMING_SWORD:   Stat.INTELLIGENCE
    }

    # Dependencies (which stats do you need to be able to access new stats?)
    deps = {
        Stat.STRENGTH: [],
        Stat.SPEED: [],
        Stat.INTELLIGENCE: [],
        Stat.MELEE: [],
        Stat.ARMOR: [],
        Stat.RANGED: [],
        Stat.EVASION: [],
        Stat.WILLPOWER: [],
        Stat.CAST_FLAME: [],
        Stat.CAST_HEAL: [
            {'stat': Stat.CAST_FLAME, 'level': 5}
        ],
        Stat.CAST_CHILL: [
            {'stat': Stat.CAST_HEAL, 'level': 5}
        ],
        Stat.CAST_FLAMING_SWORD: [
            {'stat': Stat.CAST_FLAME, 'level': 5},
            {'stat': Stat.MELEE, 'level': 10}
        ]
    }

    # Initialize with {stat_enum: int, stat_enum: int}
    def __init__(self, stats={}):
        self._stats = {}
        for stat in StatTable.deps:
            if stat in stats:
                self._stats[stat] = Stat(stats[stat])
            else:
                self._stats[stat] = Stat(0)

    def get_combined_modified(self, stat):
        modified = self._stats[stat].modified
        if modified <= 0:
            return 0  # Base mods should not buff stats that are 0 to begin with
        if stat in StatTable.base_mod:
            return self._stats[stat].modified + self._stats[StatTable.base_mod[stat]].modified
        else:
            return self._stats[stat].modified

    # Get a list of all stats where deps have been met
    def get_available_stats(self):
        ret = []
        for stat in StatTable.deps:
            should_add = True
            for dep in StatTable.deps[stat]:
                if self._stats[dep['stat']].base < dep['level']:
                    should_add = False
                    break
            if should_add:
                ret.append(stat)
        return ret

    def to_string(self):
        display_stats = []
        for s in self._stats:
            combined_modified = self.get_combined_modified(s)
            if combined_modified > 0:
                display_stats.append('{}:\t{} ({})'.format(Stat.names[s], combined_modified, self._stats[s].base))
        return '\n'.join(display_stats)

class StatMod(object):
    def __init__(self, stat, value, is_percent, time=0):
        self.stat = stat
        self.value = value
        self.is_percent = is_percent
        self.time = time


'''Demo agent class

The policies for v1.2 are sanity checyks only.
I rushed a simple baseline to get the new client
out faster. I am working on something better now.
If you want help getting other models working in
the meanwhile, drop in the Discord support channel.'''
from pdb import set_trace as T
import numpy as np
import torch

from torch import nn

from forge import trinity

from forge.blade.lib.enums import Neon
from forge.blade.lib import enums
from forge.ethyr import torch as torchlib

from forge.ethyr.torch import policy
from forge.blade import entity

from forge.ethyr.torch.param import setParameters, getParameters, zeroGrads
from forge.ethyr.torch import param

from forge.ethyr.torch.policy import functional

from forge.ethyr.torch.io.stimulus import Env
from forge.ethyr.torch.io.action import NetTree
from forge.ethyr.torch.policy import attention

#Hacky inner attention layer
class EmbAttn(nn.Module):
   def __init__(self, config, n):
      super().__init__()
      self.fc1 = nn.Linear(n*config.EMBED, config.HIDDEN)

   def forward(self, x):
      batch, ents, _, _ = x.shape
      x = x.view(batch, ents, -1)
      x = self.fc1(x)
      return x

#Hacky outer attention layer
class EntAttn(nn.Module):
   def __init__(self, config, n):
      super().__init__()
      self.fc1 = nn.Linear(n*config.HIDDEN, config.HIDDEN)

   def forward(self, x):
      batch, ents, _, = x.shape
      x = x.view(batch, -1)
      x = self.fc1(x)
      return x

#Simple max
class Max(nn.Module):
   def __init__(self, config):
      super().__init__()

   def forward(self, x):
      x, _ = x.max(-2)
      return x

#Variable number of entities
class Entity(nn.Module):
   def __init__(self, config):
      super().__init__()
      self.emb = EmbAttn(config, 11)
      self.ent = EntAttn(config, 10)

#Fixed number of entities
class Tile(nn.Module):
   def __init__(self, config):
      super().__init__()
      self.emb = EmbAttn(config, 4)
      self.ent = EntAttn(config, 225)

class Net(nn.Module):
   def __init__(self, config):
      super().__init__()

      h = config.HIDDEN
      #net = attention.BareAttend
      #net = attention.MaxReluBlock
      self.attns = nn.ModuleDict({
         'Tile':   Tile(config),
         'Entity': Entity(config),
         'Meta':   EntAttn(config, 2),
      })

      self.val  = torch.nn.Linear(h, 1)

class ANN(nn.Module):
   def __init__(self, config):
      '''Demo model'''
      super().__init__()
      self.config = config
      self.net = nn.ModuleList([Net(config)
            for _ in range(config.NPOP)])

      #Shared environment/action maps
      self.env    = Env(config)
      self.action = NetTree(config)

   def forward(self, pop, stim, actions):
      net           = self.net[pop]
      stim, embed   = self.env(net, stim)
      val           = net.val(stim)
      atns, atnsIdx = self.action(stim, actions, embed)

      return atns, atnsIdx, val

   def recvUpdate(self, update):
      if update is None:
         return

      setParameters(self, update)

   def grads(self):
      grads = param.getGrads(self)
      zeroGrads(self)
      return grads

   def params(self):
      return param.getParameters(self)

   #These hooks are outdated. Better policy
   #visualization for the new client is planned
   #for a future update
   def visDeps(self):
      from forge.blade.core import realm
      from forge.blade.core.tile import Tile
      colorInd = int(self.config.NPOP*np.random.rand())
      color    = Neon.color12()[colorInd]
      color    = (colorInd, color)
      ent = realm.Desciple(-1, self.config, color).server
      targ = realm.Desciple(-1, self.config, color).server

      sz = 15
      tiles = np.zeros((sz, sz), dtype=object)
      for r in range(sz):
         for c in range(sz):
            tiles[r, c] = Tile(enums.Grass, r, c, 1, None)

      targ.pos = (7, 7)
      tiles[7, 7].addEnt(0, targ)
      posList, vals = [], []
      for r in range(sz):
         for c in range(sz):
            ent.pos  = (r, c)
            tiles[r, c].addEnt(1, ent)
            #_, _, val = self.net(tiles, ent)
            val = np.random.rand()
            vals.append(float(val))
            tiles[r, c].delEnt(1)
            posList.append((r, c))
      vals = list(zip(posList, vals))
      return vals

   #These hooks are outdated. Better policy
   #visualization for the new client is planned
   #for a future update
   def visVals(self, food='max', water='max'):
      from forge.blade.core import realm
      posList, vals = [], []
      R, C = self.world.shape
      for r in range(self.config.BORDER, R-self.config.BORDER):
          for c in range(self.config.BORDER, C-self.config.BORDER):
            colorInd = int(self.config.NPOP*np.random.rand())
            color    = Neon.color12()[colorInd]
            color    = (colorInd, color)
            ent = entity.Player(-1, color, self.config)
            ent._r.update(r)
            ent._c.update(c)
            if food != 'max':
               ent._food = food
            if water != 'max':
               ent._water = water
            posList.append(ent.pos)

            self.world.env.tiles[r, c].addEnt(ent.entID, ent)
            stim = self.world.env.stim(ent.pos, self.config.STIM)
            #_, _, val = self.net(stim, ent)
            val = np.random.rand()
            self.world.env.tiles[r, c].delEnt(ent.entID)
            vals.append(float(val))

      vals = list(zip(posList, vals))
      return vals