#!/usr/bin/env pybricks-micropython

from pybricks.parameters import Port, Color, Button
# from pybricks.ev3devices import Motor, ColorSensor, InfraredSensor, GyroSensor, TouchSensor, UltrasonicSensor
from pybricks.hubs import EV3Brick
from pybricks.media.ev3dev import Font, Image, ImageFile
from os import listdir, system
import os.path
import time, _thread, typing, sys, traceback, json

# Credits: <https://github.com/pybricks/support/issues/152#issuecomment-709608118>

def rl(path) -> str:
    "Open a file, return its first line and close the file afterwards, ignoring any errors."
    try:
        f = open(path)
        txt = f.readline()
        f.close()
    except:
        txt = "0"
    return txt

class Sensor:
    def __init__(self, path) -> None:
        self.path = path
        self.driverName = rl(self.path+"/driver_name")[:-1]
        self.portLetter = rl(self.path+"/address")[12:13]
        self.numValues = int(rl(self.path+"/num_values")[:-1])
        self.mode = rl(self.path+"/mode")[:-1]
        self.values = None
        self.modes = rl(self.path+"/modes").split()
        self.commands = rl(self.path + "/commands").split()
        self.additionalAttributes = {"gyro-ang-offset": 0, "gyro-tilt-offset": 0} if self.driverName == "lego-ev3-gyro" else {}

    def setMode(self, mode):
        self.mode = mode
        system("echo %s > %s/mode" % (mode, self.path))
        self.numValues = int(rl(self.path+"/num_values")[:-1])

    def getValues(self):
        self.values = [int(rl(self.path+"/value"+str(i))[:-1]) for i in range(self.numValues)]
        return self.values

    def execCommand(self, command):
        system("echo %s > %s/command" % (command, self.path))
        self.numValues = int(rl(self.path+"/num_values")[:-1])

class Motor:
    def __init__(self, path) -> None:
        self.path = path
        self.portLetter = rl(self.path+"/address")[-2:-1]
        self.driverName = rl(self.path+"/driver_name")[:-1]
        self.speed = None
        self.position = None

    def getPosition(self):
        self.position = int(rl(self.path+"/position")[:-1])
        return self.position

    def getSpeed(self):
        self.speed = int(rl(self.path+"/speed")[:-1])
        return self.speed

    def resetPos(self):
        system("echo reset > %s/command" % self.path)

sensorNames = []
motorNames = []

portDevList = [None] * 8

# portDevList:typing.List[typing.Union[None, Motor, Sensor]] = [None] * 8

def updateDevList():
    global portDevList, sensorNames, motorNames

    dirs = listdir("/sys/class/tacho-motor")
    if dirs != motorNames: # motor added / removed
        print("updateDevList: Motor added/removed")
        portDevList[:4] = [None] * 4
        for dir in dirs:
            m = Motor("/sys/class/tacho-motor/"+dir)
            portDevList[ord(m.portLetter)-65] = m
    motorNames = dirs

    dirs = listdir("/sys/class/lego-sensor")
    if dirs != sensorNames: # sensor added / removed
        print("updateDevList: Sensor added/removed")
        portDevList[4:] = [None] * 4
        for dir in dirs:
            s = Sensor("/sys/class/lego-sensor/"+dir)
            portDevList[int(s.portLetter)+3] = s
    sensorNames = dirs

def caliEv3Reflect(sensor: Sensor):
    global portDevList, b, render, allCaliVals
    render = False
    time.sleep(0.1)

    b.screen.clear()
    b.screen.set_font(f18)
    txt = "Calibration: Port " + sensor.portLetter
    b.screen.draw_text((b.screen.width - f18.text_width(txt))/2, 0, txt)
    b.screen.set_font(f12)
    txt = "Put the sensor on a"
    b.screen.draw_text((b.screen.width - f12.text_width(txt))/2, 30, txt)
    txt = "surface and press [CENTER]"
    b.screen.draw_text((b.screen.width - f12.text_width(txt))/2, 70, txt)
    b.screen.set_font(f24)
    txt = "BLACK"
    b.screen.draw_text((b.screen.width - f24.text_width(txt))/2, 42, txt)
    sensor.setMode("COL-REFLECT")

    while not Button.CENTER in b.buttons.pressed():
        time.sleep(0.05)
    while Button.CENTER in b.buttons.pressed():
        time.sleep(0.05)
    black = sensor.getValues()[0]

    b.screen.draw_box(0, 42, b.screen.width, 70, fill=True, color=Color.WHITE)
    txt = "WHITE"
    b.screen.draw_text((b.screen.width - f24.text_width(txt))/2, 42, txt)

    while not Button.CENTER in b.buttons.pressed():
        time.sleep(0.05)
    white = sensor.getValues()[0]

    if white == black:
        b.screen.clear()
        b.screen.set_font(f12)
        b.screen.draw_text(0, 5, "Invalid values.")
        b.screen.draw_text(0, 25, "Please recalibrate.")
        while not any(b.buttons.pressed()):
            time.sleep(0.05)
    else:
        with open("/home/robot/.caliValues.json") as f:
            allCaliVals = json.load(f)
        allCaliVals[sensor.portLetter]["reflect"][0] = 100 / (white - black)
        allCaliVals[sensor.portLetter]["reflect"][1] = (-100 * black) / (white - black)
        with open("/home/robot/.caliValues.json", "w") as f:
            json.dump(allCaliVals, f, indent=4)

    render = True

def caliEv3Rgb(sensor: Sensor):
    global b, render
    render = False

    # Radios:rounded boxes, unfilled circle left (with dot inside if active), caption right, inverted colors if selected
    # Cali-Modes: (Todo: Try out -> Default)
        # Black + White
        # Black + Red+Green+Blue
    # Value range:
        # 0-255 (default)
        # 0-100
    # Start-Button (selected by default)

    render = True

def resetGyroAngles(sensor: Sensor):
    if sensor.mode in ("GYRO-ANG", "GYRO-G&A"):
        sensor.additionalAttributes["gyro-ang-offset"] = int(sensor.values[0])
    elif sensor.mode == "TILT-ANG":
        sensor.additionalAttributes["gyro-tilt-offset"] = int(sensor.values[0])

additionalFunctions = {
    "lego-ev3-color": {
        "Calib. COL-REFLECT": caliEv3Reflect,
        # "Calib. RGB-RAW": caliEv3Rgb
    },
    # "lego-ev3-gyro": {
    #     "Reset angles": resetGyroAngles
    # }
}

b = EV3Brick()
selectedDevNr = 6 # Number of the selected device (0-7)
modeSelected = [1, ""] # mode/command selected
menuOptionNrSelected = 0 # including headings
menuScrollPos = 0 # scroll pos of modeMenu
menuOptionsList = [] # All elements of the menu, including headings
portLetters = ["A", "B", "C", "D", "1", "2", "3", "4"]
menuOpen = False
allCaliVals = []
render = True # if False, stop rendering
f12 = Font(size=12)
f18 = Font(size=18)
f24 = Font(size=24)

defaultSettings = {'calibration.ev3col.reflect.enabled': True, 'calibration.ev3col.rgb.enabled': False, 'calibration.ev3col.rgb.defaultRange': 255, 'calibration.ev3col.rgb.defaultMode': 'single', 'hideModes': {'lego-ev3-color': ['COL-CAL'], 'lego-ev3-gyro': ['GYRO-CAL'], 'lego-ev3-ir': ['IR-CAL', 'IR-S-ALT']}}
try:
    settings = json.load(open("/home/robot/.portView.conf.json"))
except:
    try:
        settings = json.load(open(__file__.rsplit("/", 1)[0]+"/.portView.conf.json"))
    except:
        settings = defaultSettings
for k in defaultSettings.keys():
        if k not in settings.keys():
            settings[k] = defaultSettings[k]

#arrow_up = Image("arrow_up_s.png")
arrow_up = Image.empty(22, 22)
arrow_up.draw_box(0, 0, 21, 21, fill=True)
arrow_up.draw_line(8, 18, 16, 18, color=Color.WHITE)
arrow_up.draw_line(8, 18, 8, 10, color=Color.WHITE)
arrow_up.draw_line(16, 18, 16, 10, color=Color.WHITE)
arrow_up.draw_line(8, 10, 4, 10, color=Color.WHITE)
arrow_up.draw_line(16, 10, 20, 10, color=Color.WHITE)
arrow_up.draw_line(4, 10, 12, 2, color=Color.WHITE)
arrow_up.draw_line(20, 10, 12, 2, color=Color.WHITE)

#arrow_down = Image("arrow_down_s.png")
arrow_down = Image.empty(22, 22)
arrow_down.draw_box(0, 0, 21, 21, fill=True)
arrow_down.draw_line(8, 6, 16, 6, color=Color.WHITE)
arrow_down.draw_line(8, 6, 8, 14, color=Color.WHITE)
arrow_down.draw_line(16, 6, 16, 14, color=Color.WHITE)
arrow_down.draw_line(8, 14, 4, 14, color=Color.WHITE)
arrow_down.draw_line(16, 14, 20, 14, color=Color.WHITE)
arrow_down.draw_line(4, 14, 12, 22, color=Color.WHITE)
arrow_down.draw_line(20, 14, 12, 22, color=Color.WHITE)

if any([settings["calibration.ev3col.reflect.enabled"], settings["calibration.ev3col.rgb.enabled"]]):
    try:
        f = open("/home/robot/.caliValues.json")
        allCaliVals = json.load(f)
        f.close()
    except:
        #f = open("/home/robot/.caliValues.json", "w")
        #f.write("{\"1\": [0, 100], \"2\": [0, 100], \"3\": [0, 100], \"4\": [0, 100]}")
        #f.close()
        #allCaliVals = {str(i+1): [0, 100] for i in range(4)}
        allCaliVals = {str(i+1): {"reflect": [1, 0], "rgb": {c: [1, 0] for c in ["red", "green", "blue"]}} for i in range(4)}
        json.dump(allCaliVals, open("/home/robot/.caliValues.json", "w"))
        print("CaliLoader: Created new .caliValues file")

def keyhandler():
    global selectedDevNr, modeSelected, menuOptionNrSelected, menuScrollPos, menuOptionsList, menuOpen, b, portDevList
    while True:
        while not any(b.buttons.pressed()):
            time.sleep(0.05)

        buttons = b.buttons.pressed()
        if not menuOpen:
            if Button.LEFT in buttons:
                if selectedDevNr == 0:
                    selectedDevNr = 7
                else:
                    selectedDevNr -= 1
            elif Button.RIGHT in buttons:
                if selectedDevNr == 7:
                    selectedDevNr = 0
                else:
                    selectedDevNr += 1
            elif Button.UP in buttons or Button.DOWN in buttons:
                if selectedDevNr > 3:
                    selectedDevNr -= 4
                else:
                    selectedDevNr += 4
            elif Button.CENTER in buttons and portDevList[selectedDevNr] != None:
                if selectedDevNr > 3: # Sensor -> open menu
                    menuOpen = True
                    print("keyhandler: opening modeMenu")

                    # menuOptionsList = [(0, "Modes:")] + [(1, elem) for elem in portDevList[selectedDevNr].modes if elem not in settings["hideModes"][portDevList[selectedDevNr].driverName]]
                    menuOptionsList = [(0, "Modes:")]
                    for mode in portDevList[selectedDevNr].modes:
                        if portDevList[selectedDevNr].driverName in settings["hideModes"]:
                            if mode in settings["hideModes"][portDevList[selectedDevNr].driverName]:
                                continue
                        menuOptionsList += [(1, mode)]

                    if portDevList[selectedDevNr].commands != []:
                        menuOptionsList += [(0, "Commands:")] + [(2, elem) for elem in portDevList[selectedDevNr].commands]
                    if portDevList[selectedDevNr].driverName in additionalFunctions.keys():
                        menuOptionsList += [(0, "Additional functions")]
                        for title in additionalFunctions[portDevList[selectedDevNr].driverName].keys():
                            menuOptionsList += [(3, title, additionalFunctions[portDevList[selectedDevNr].driverName][title])]
                    #if portDevList[selectedDevNr].driverName == "lego-ev3-color" and any(settings["calibration.ev3col.reflect.enabled"], settings["calibration.ev3col.rgb.enabled"]): # Calibration
                    #    menuOptionsList += [(0, "Additional: Calibration")]
                    #    if settings["calibration.ev3col.reflect.enabled"]:
                    #        menuOptionsList += [(3, "Calib. COL-REFLECT", caliEv3Reflect)]
                    #    if settings["calibration.ev3col.rgb.enabled"]:
                    #    menuOptionsList += [(3, "Calib. RGB-RAW", caliEv3Rgb)]

                    menuOptionNrSelected = menuOptionsList.index((1, portDevList[selectedDevNr].mode))
                    menuScrollPos = 0
                    if len(menuOptionsList) > 5:
                        if menuOptionNrSelected > 4:
                            menuScrollPos = menuOptionNrSelected - 4

                    print("keyhandler: menuOptionsList:", menuOptionsList)

                else: # Motor -> reset
                    portDevList[selectedDevNr].resetPos()
                    print("keyhandler: resetting pos")

        else: # menuOpen == True
            if Button.UP in buttons:
                if menuOptionNrSelected != 1: # 0 -> Heading "Modes"
                    menuOptionNrSelected -= 1
                if menuOptionsList[menuOptionNrSelected][0] == 0:
                    menuOptionNrSelected -= 1

                if menuOptionNrSelected == 1 and menuScrollPos != 0: # Scroll up to make the heading visible
                    menuScrollPos = 0
                elif menuOptionNrSelected < menuScrollPos and menuScrollPos != 0:
                    menuScrollPos -= 1

            elif Button.DOWN in buttons:
                if menuOptionNrSelected != len(menuOptionsList)-1:
                    menuOptionNrSelected += 1
                if menuOptionsList[menuOptionNrSelected][0] == 0:
                    menuOptionNrSelected += 1

                for i in range(2):
                    if menuOptionNrSelected-4 > menuScrollPos:
                        menuScrollPos += 1

            elif Button.LEFT in buttons: # close the menu without changing anything
                menuOpen = False
                print("keyhandler: closing modeMenu")

            elif Button.CENTER in buttons:
                if menuOptionsList[menuOptionNrSelected][0] == 1: # Mode
                    portDevList[selectedDevNr].setMode(menuOptionsList[menuOptionNrSelected][1])
                elif menuOptionsList[menuOptionNrSelected][0] == 2: # Command
                    portDevList[selectedDevNr].execCommand(menuOptionsList[menuOptionNrSelected][1])
                elif menuOptionsList[menuOptionNrSelected][0] == 3: # Additional function
                    menuOptionsList[menuOptionNrSelected][2](portDevList[selectedDevNr])

                print("keyhandler: closing modeMenu")
                menuOpen = False

        while any(b.buttons.pressed()):
            time.sleep(0.05)

_thread.start_new_thread(keyhandler, [])

while True:
    if not render:
        time.sleep(0.1)
        continue

    # try:
    if not menuOpen:
        time.sleep(0.1)

        updateDevList()

        # print(portDevList)
        b.screen.clear()
        b.screen.set_font(f12)

        for i in range(4): # Motoren
            if i == selectedDevNr:
                b.screen.draw_box(44*i, 0, 44+44*i, 22, 5, True)
                b.screen.draw_image(10+44*i, -3, arrow_down)
            else:
                b.screen.draw_box(44*i, 0, 44+44*i, 22, 5, False)
                if portDevList[i] == None:
                    b.screen.draw_text(16+44*i, 5, "---")
                else:
                    txt = str(portDevList[i].getPosition())
                    b.screen.draw_text(44*i + (44-f12.text_width(txt))/2, 5, txt)

        for i in range(4): # Sensoren
            if i+4 == selectedDevNr:
                b.screen.draw_box(44*i, 105, 44+44*i, 127, 5, True)
                b.screen.draw_image(10+44*i, 105, arrow_up)
            else:
                b.screen.draw_box(44*i, 105, 44+44*i, 127, 5, False)
                if portDevList[i+4] == None:
                    b.screen.draw_text(16+44*i, 110, "---")
                elif len(portDevList[i+4].getValues()) > 1:
                    b.screen.draw_text(14+44*i, 110, "[...]")
                else:
                    if portDevList[i+4].mode == "COL-REFLECT" and settings['calibration.ev3col.reflect.enabled']:
                        vals = allCaliVals[portDevList[selectedDevNr].portLetter]["reflect"]
                        txt = str(int(vals[0] * portDevList[i+4].values[0] + vals[1]))
                    else:
                        txt = str(portDevList[i+4].values[0])
                    b.screen.draw_text(44*i + (44-f12.text_width(txt))/2, 110, txt)

        b.screen.set_font(f18)
        if portDevList[selectedDevNr] == None:
            b.screen.draw_text(0, 24, "%s: None" % portLetters[selectedDevNr])
        else:

            if selectedDevNr < 4: # Motor:
                b.screen.draw_text(0, 24, "%s: %s" % (portLetters[selectedDevNr], portDevList[selectedDevNr].driverName))
                b.screen.set_font(f12)
                b.screen.draw_text(7, 50, "Position [deg]")
                b.screen.draw_text(95, 50, "Speed [deg/s]")
                # b.screen.set_font(f18 if abs(int(portDevList[selectedDevNr].getPosition())) < 1000 and abs(int(portDevList[selectedDevNr].getSpeed())) < 1000 else f12)
                b.screen.set_font(f24)

                pos = str(portDevList[selectedDevNr].getPosition())
                b.screen.draw_text((b.screen.width/2 - f24.text_width(pos))/2, 75, pos)
                b.screen.draw_line(88, 42, 88, 90)

                speed = str(portDevList[selectedDevNr].getSpeed())
                b.screen.draw_text(b.screen.width/2 + (b.screen.width/2 - f24.text_width(speed))/2, 75, speed)

                # b.screen.set_font(f12)
                # b.screen.draw_text(0, 80, "Press [CENTER] to reset position")
            else: # Sensor
                b.screen.draw_text(0, 24, "%s: %s" % (portLetters[selectedDevNr], portDevList[selectedDevNr].mode))
                b.screen.set_font(f12)
                nv = portDevList[selectedDevNr].numValues
                b.screen.draw_text(0, 40, str(nv) + (" Value" if nv == 1 else " Values"))
                b.screen.set_font(f24)
                b.screen.draw_text(30, 55, str(portDevList[selectedDevNr].getValues())[1:-1])

                if portDevList[selectedDevNr].driverName == "lego-ev3-color" and portDevList[selectedDevNr].mode == "COL-REFLECT" and settings["calibration.ev3col.reflect.enabled"]:
                    b.screen.draw_line(88, 42, 88, 90)
                    b.screen.set_font(f12)
                    b.screen.draw_text(95, 40, "Calibrated")
                    b.screen.set_font(f24)
                    vals = allCaliVals[portDevList[selectedDevNr].portLetter]["reflect"]
                    b.screen.draw_text(110, 55, str(int(vals[0] * portDevList[selectedDevNr].values[0] + vals[1])))

                if portDevList[selectedDevNr].driverName == "lego-ev3-color" and portDevList[selectedDevNr].mode == "RGB-RAW" and settings["calibration.ev3col.rgb.enabled"]:
                    b.screen.draw_line(88, 42, 88, 90)
                    b.screen.set_font(f12)
                    b.screen.draw_text(95, 40, "Calibrated")
                    b.screen.set_font(f24)
                    vals = [allCaliVals[portDevList[selectedDevNr].portLetter]["rgb"][key] for key in ["red", "green", "blue"]]
                    input(vals)
                    b.screen.draw_text(110, 55, str([int(vals[colNr][1] * portDevList[selectedDevNr].values[colNr] + vals[colNr][2]) for colNr in range(3)])[1:-1])

    else: # menuOpen
        time.sleep(0.1)
        b.screen.clear()

        txt = portDevList[selectedDevNr].driverName
        f = f18 if f18.text_width(txt) < b.screen.width else f12
        b.screen.set_font(f)
        b.screen.draw_text((b.screen.width - f.text_width(txt))/2, 0, txt)

        rows2display = []
        if len(menuOptionsList) > 5:
            rows2display = menuOptionsList[menuScrollPos:menuScrollPos+5]
            # Scrollbar https://stackoverflow.com/questions/16366795/how-to-calculate-the-size-of-scroll-bar-thumb/22710156#22710156
            b.screen.draw_box(b.screen.width-8, 22, b.screen.width-1, 120)
            arrowHeight = 4
            viewportHeight = 100
            contentHeight = len(menuOptionsList) * 20

            # viewableRatio = viewportHeight / contentHeight
            # scrollbarArea = viewportHeight - arrowHeight * 2
            thumbHeight = (viewportHeight - arrowHeight * 2) * (viewportHeight / contentHeight)

            # scrollTrackSpace = contentHeight - scrollbarArea
            # scrollThumbSpace = viewportHeight - thumbHeight
            # scrollJump = scrollTrackSpace / scrollThumbSpace

            # contentOffset = menuScrollPos * 20
            thumbOffset = ((menuScrollPos * 20) / ((contentHeight - (viewportHeight - arrowHeight * 2)) / (viewportHeight - thumbHeight))) + 22 + arrowHeight/2
            # print(scrollbarArea, viewableRatio, thumbHeight, thumbOffset)
            # barLength = (5/len(menuOptionsList)) * 94
            # absScrollPos = (menuOptionNrSelected+1 / len(menuOptionsList)) * 94
            # topOffset = 24 + absScrollPos + barLength/2
            b.screen.draw_box(b.screen.width - 6, thumbOffset, b.screen.width - 3, thumbOffset + thumbHeight-1, r=2, fill=True)
        else:
            rows2display = menuOptionsList

        b.screen.set_font(f12)
        # Debug
        # b.screen.draw_text(0, 0, str(menuOptionNrSelected))
        # b.screen.draw_text(b.screen.width-f12.text_width(str(menuScrollPos)), 0, str(menuScrollPos))

        for i in range(len(rows2display)):
            if rows2display[i][0] == 0: # Heading
                b.screen.draw_text(0, 25+20*i, rows2display[i][1])
                b.screen.draw_line(0, 38+20*i, b.screen.width-11, 38+20*i)

            else: # Mode / command / extra
                if i+menuScrollPos == menuOptionNrSelected:
                    b.screen.draw_box(10, 22+20*i, b.screen.width-11, 40+20*i, 5, fill=True)
                    b.screen.draw_text(15, 25+20*i, rows2display[i][1], Color.WHITE)
                else:
                    b.screen.draw_box(10, 22+20*i, b.screen.width-11, 40+20*i, 5)
                    b.screen.draw_text(15, 25+20*i, rows2display[i][1])

    # except Exception as e:
    #     print("Exception occured during rendering:")
    #     print(e)
    #     print()


# 导入必要的库
import matplotlib.pyplot as plt  # 用于绘图
import numpy as np

# 提供一些示例数据
x_data = np.linspace(0, 10, 500)  # 创建一个包含5000个点的线性空间，范围从0到10
noise = 2 * np.random.rand(500) - 1
y_data = 5 * x_data + 3 + noise  # 生成带有噪声的线性数据

w = 0  # 初始化权重
b = 0  # 初始化偏差

# 定义线性回归模型的前向传播函数
def forward(x):
    return x * w + b

# 定义损失函数，衡量模型的预测与真实值之间的差距
def loss(x, y):
    y_pred = forward(x)  # 使用前向传播函数计算预测值
    return (y_pred - y) ** 2  # 返回平方损失

# 定义学习率和迭代次数
learning_rate = 0.001
num_epochs = 100
batch_size = 500  # 小批量样本的大小
losses = []  # 用于存储每个epoch的损失值

# 使用小批量随机梯度下降法更新权重和偏差
for epoch in range(num_epochs):
    # 将数据集打乱顺序
    indices = np.arange(len(x_data))
    np.random.shuffle(indices)

    for i in range(0, len(x_data), batch_size):
        # 选择小批量样本
        batch_indices = indices[i:i + batch_size]
        x_batch = x_data[batch_indices]
        y_batch = y_data[batch_indices]

        # 计算模型的预测值
        y_pred = forward(x_batch)
        # 计算平均损失值
        loss_val = np.mean(loss(x_batch, y_batch))

        # 计算梯度
        dw = np.mean(2 * x_batch * (y_pred - y_batch))  # 权重的梯度
        db = np.mean(2 * (y_pred - y_batch))  # 偏差的梯度

        # 更新权重和偏差
        w = w - learning_rate * dw  # 使用梯度下降法更新权重
        b = b - learning_rate * db  # 使用梯度下降法更新偏差
    losses.append(loss_val)  # 将最后一个小批量的损失值添加到损失列表中

# 绘制训练损失随epoch的变化图
plt.figure(figsize=(10, 6))
plt.plot(range(num_epochs), losses, label='Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss over Epochs')
plt.legend()
plt.grid(True)
plt.show()

# 打印最终的权重和偏差
print("最终的权重 w:", w)
print("最终的偏差 b:", b)


import utils
import player
import planet

import random
from math import sqrt
from collections import deque
from easymunk import pyxel as phys, Vec2d
from easymunk import Constraint, Arbiter
import pyxel

class Game:
    def __init__(self):
        self.camera = phys.Camera(flip_y=False)
        self.space = phys.space(
            camera = self.camera,
            elasticity = 1.0,
        )

        # Inicializa objetos
        self.state = utils.GameState.RUNNING

        self.player = player.Player(30, 30)
        self.player.register(self.space, self.message)

        self.planet1 = planet.Planet(100, 100)
        self.planet1.register(self.space, self.message)

        self.planet2 = planet.Planet(180, 110)
        self.planet2.register(self.space, self.message)

        self.particles = utils.Particles(self.space)

        # Cria margem
        L = 48
        phys.margin(-L, -L, pyxel.width + 2 * L, pyxel.width + 2 * L, radius = 5)

    # Administra colisões
        self.space.collision_handler(
            utils.ColType.PLAYER, utils.ColType.PLANET, post_solve=self.on_collision
        )

    def on_collision(self, arb: Arbiter):
        d1 = abs(self.player.player_body.position.x - self.planet1.planet_body.position.x) + abs(self.player.player_body.position.y - self.planet1.planet_body.position.y)
        d2 = abs(self.player.player_body.position.x - self.planet2.planet_body.position.x) + abs(self.player.player_body.position.y - self.planet2.planet_body.position.y)

        if self.player.planet_joint is not None:
            self.player.planet_joint.max_force = 0

        if d1 < d2:
            self.player.planet_joint = self.player.player_body.junction(self.planet1.planet_body).pivot()
            self.player.landed_on = self.planet1
            print("hey")
        else:
            self.player.planet_joint = self.player.player_body.junction(self.planet2.planet_body).pivot()
            self.player.landed_on = self.planet2

        self.player.planet_joint.max_force = float("inf")

        for _ in range(2):
            self.particles.emmit(
                position=self.player.player_body.local_to_world((random.uniform(-2, 2), -3)),
                velocity=(0, 0) #-random.uniform(50, 90) * self.player.player_body.rotation_vector.rotated(90),
            )

    def message(self, msg, sender):
        fn = getattr(self, f'handle_{msg}', None)
        if fn is None:
            print(f'Mensagem desconhecida: "{msg} ({sender})')
        else:
            fn(sender)

    def update(self):
        self.space.step(1 / 30, 2)

        # Atualiza lógica caso o jogo esteja rodando
        if self.state is not utils.GameState.GAME_OVER:
            self.player.update(self.camera)
            self.planet1.update()
            self.planet2.update()

        # Camera segue o Player
        self.camera.follow(self.player.player_body.position)

        # atualiza particulas
        self.particles.update()

    def draw(self):
        pyxel.cls(0)

        for body in self.space.bodies:
            if isinstance(body, (player.Player, planet.Planet)):
                body.draw(self.camera)
            else:
                self.camera.draw(body)

        self.player.draw(self.camera)
        self.planet1.draw(self.camera)
        self.planet2.draw(self.camera)
        self.particles.draw(self.camera)

pyxel.init(utils.WIDTH, utils.HEIGHT)
pyxel.mouse(True)
game = Game()
pyxel.run(game.update, game.draw)


import pydicom
# from openpyxl import load_workbook
from collections import Counter
import xlrd
from random import shuffle
import scipy.misc as sp
import matplotlib.pyplot as plt
import os
import logging
import numpy as np

def parse_xsl(path):
    """
    parse_xsl from table to list of dicts
    :param path: str - path of xslx file
    :return samples: list of dicts by rows. Each value is sorted by the column's name as the key
    """
    wb = xlrd.open_workbook(path)
    ws = wb.sheet_by_index(0)
    num_of_rows = ws.nrows
    num_of_cols = ws.ncols

    samples = []
    for i in range(1, num_of_rows):
        sample = dict()
        for j in range(num_of_cols):
            sample[str(ws.cell(0, j).value)] = str(ws.cell(i, j).value)
        samples.append(sample)

    format_accession_field(samples)
    return samples

def format_accession_field(samples):

    for sample in samples:
        try:
            float(sample['Accession'])
            sample['Accession'] = sample['Accession'].split('.')[0]
        except ValueError:
            pass

def samples_to_lables(samples):
    """
    seperate between plural efusion positive and negative and return lists of Accession field
    :param samples: list of samples generated from xl file
    :return plural_efusion_positive: list of positive samples' Accession
    :return all_the_rest: list of negative samples' Accession
    """
    plural_efusion_positive = [sample['Accession'] for sample in samples if (sample['Taflitright'] == '1.0' or
                                                                             sample['Taflitleft'] == '1.0')]
    all_the_rest = [sample['Accession'] for sample in samples if (sample['Taflitright'] == '0.0' and
                                                                  sample['Taflitleft'] == '0.0')]

    return plural_efusion_positive, all_the_rest

def unite_labels(samples_dict):

    samples = []
    for k, val in samples_dict.items():
        [samples.append({'Accession': sample, 'label': int(k)}) for sample in val]
    return samples

def load_images_to_samples(samples):
    pass

def eliminate_multiple_occurrences(dataset_descript):

    access_list = [sample['Accession'] for sample in dataset_descript]

    counter = Counter(access_list)

    multiple_occurence = [key for key, val in counter.items() if val > 1]

    for key in multiple_occurence:
        samples = [{'sample': sample, 'index': i} for i, sample in enumerate(dataset_descript)
                   if sample['Accession'] == key]

        for i, sample in enumerate(samples):
            for comp_sample in samples[i+1:]:
                if sample['sample'] == comp_sample['sample']:
                    del dataset_descript[comp_sample['index']]
                else:
                    del dataset_descript[comp_sample['index']]
                    del dataset_descript[sample['index']]

def unpack_data(src, samples, dst):
    def check_LAT_PA(fold):
        angles = {'PA' : [],
                  'LAT': []}
        if not sample['label']:  ### debug
            print('{}\n{}\n**************'.format(fold, os.listdir(fold)))
        for key in angles.keys():
            for file in os.listdir(fold):
                if file.find(key) != -1:
                    angles[key].append(file)
        return angles

    for sample in samples:
        fold = os.path.join(src, sample['Accession'])
        angle_files = check_LAT_PA(fold)
        if not all(angle_files.values()):
            if not angle_files['PA']:
                logging.warning('{} doesn\'t hold a PA file'.format(fold))
            continue

        matches = []
        matched = []
        for file_pa in angle_files['PA']:
            ending = file_pa.split('PA')[-1]

            match = [file_lat for file_lat in angle_files['LAT'] if file_lat.split('LAT')[-1] == ending]
            if match:
                logging.info('successfully matched {} to {} in {}'.format(file_pa, match[0], fold))
                matches.append({'PA': file_pa, 'LAT': match[0]})
                matched.append(match[0])
            else:
                logging.warning('{} has no match for {}'.format(fold, file_pa))

        [logging.warning('{} has no match for {}'.format(fold, file_lat))
         for file_lat in angle_files['LAT'] if file_lat not in matched]

        for i, match in enumerate(matches):
            for angle, angle_file in match.items():
                # if len(angle_file) > 1:
                #     err = '{} has {} {} files: {}'.format(fold, len(angle_file), angle, angle_file)
                #     # raise ValueError(err)
                #     logging.warning(err)

                dic_path = os.path.join(fold, angle_file)
                ds = pydicom.dcmread(dic_path)

                dst_dir = os.path.join(os.path.join(dst, str(sample['label'])), sample['Accession'] + '_{}'.format(i))
                os.makedirs(dst_dir, exist_ok=True)
                dst_path = os.path.join(dst_dir, sample['Accession'] + '_{}'.format(i) + '_' + angle + '.png')

                logging.info('saving {} to {}'.format(dic_path, dst_path))
                # sp.imsave(dst_path, ds.pixel_array)
                save_xray_scan(dst_path, ds)

def save_xray_scan(dst, ds):

    img = ds.pixel_array.astype(np.float32)
    if ds.PhotometricInterpretation == 'MONOCHROME1':
        maxl = np.amax(img)
        img = (2 ** int(np.ceil(np.log2(maxl - 1)))) - 1 - img
    sp.imsave(dst, img)

def main():

    handlers = [logging.FileHandler('/Users/Ido/Desktop/Project Data/data_log.log'), logging.StreamHandler()]
    formatter = '%(levelname)s - %(message)s'
    logging.basicConfig(handlers=handlers, level=logging.INFO, format=formatter)

    trainset_descript = parse_xsl('/Users/Ido/Desktop/Project Data/Shiba_dataset/db_description_cxr_train.xlsx')
    testset_descript = parse_xsl('/Users/Ido/Desktop/Project Data/Shiba_dataset/db_description_cxr_test.xlsx')

    eliminate_multiple_occurrences(trainset_descript)
    eliminate_multiple_occurrences(testset_descript)

    positives_trainset, negatives_trainset = samples_to_lables(trainset_descript)
    train_data = unite_labels({'0': negatives_trainset, '1': positives_trainset})
    shuffle(train_data)

    positives_testset, negatives_testset = samples_to_lables(testset_descript)
    test_data = unite_labels({'0': negatives_testset, '1': positives_testset})
    shuffle(test_data)
    # unpack_data('/Users/Ido/Desktop/Project Data/Shiba_dataset/train_dcm', train_data, '/Users/Ido/Desktop/Project Data/train_images')
    unpack_data('/Users/Ido/Desktop/Project Data/Shiba_dataset/test_dcm', test_data, '/Users/Ido/Desktop/Project Data/test_images')

if __name__ == '__main__':
    main()


import torch
import torch.nn as nn

from models.world_model import WorldModel

def expand_world_model_embedding(world_model: WorldModel) -> None:
    # we need to modify the Embedder of the world model, because
    # now the there are two more actions added.
    config = world_model.config

    embedding_tables = world_model.embedder.embedding_tables
    act_vocab_size, obs_vocab_size = (
        embedding_tables[0].weight.shape[0],
        embedding_tables[1].weight.shape[0]
    )

    assert act_vocab_size == world_model.act_vocab_size

    vocab_embedding = nn.Embedding(act_vocab_size + 2, config.embed_dim)
    vocab_embedding.weight.data[:act_vocab_size] = world_model.embedder.embedding_tables[0].weight.data

    new_embedding_tables = nn.ModuleList([
        vocab_embedding,
        embedding_tables[1]
    ]).to(next(world_model.parameters()).device)

    world_model.embedder.embedding_tables = new_embedding_tables


from PyQt5 import QtCore, QtGui, QtWidgets

class AddAuthorUI(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(656, 157)
        self.authorEdit = QtWidgets.QLineEdit(Form)
        self.authorEdit.setGeometry(QtCore.QRect(30, 40, 581, 22))
        self.authorEdit.setReadOnly(False)
        self.authorEdit.setObjectName("authorEdit")
        self.addAuthorButton = QtWidgets.QPushButton(Form)
        self.addAuthorButton.setGeometry(QtCore.QRect(30, 110, 581, 28))
        self.addAuthorButton.setObjectName("addAuthorButton")

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.authorEdit.setPlaceholderText(_translate("Form", "Автор"))
        self.addAuthorButton.setText(_translate("Form", "Добавить автора"))


# -*- coding: utf-8 -*-

from __future__ import annotations

import logging
import os
import sys
from collections.abc import Mapping
from typing import Optional, Union

def _setup_handlers(logger, handlers=None, reset=True):
    if reset:
        for handler in logger.handlers[:]:
            logger.removeHandler(handler)
            handler.close()

    if handlers:
        for handler in handlers:
            logger.addHandler(handler)

def _setup_formaters(logger, format_):
    for handler in logger.handlers:
        handler.setFormatter(logging.Formatter(format_))

def _setup_logger(logger, level, format_, handlers):
    logger.setLevel(level)
    _setup_handlers(logger, handlers, reset=True)
    _setup_formaters(logger, format_)

#  pylint: disable=redefined-builtin
def setup_logger(
    logger: Optional[logging.Logger] = None,
    level: Optional[Union[int, str]] = logging.INFO,
    format: str = "%(asctime)s %(name)s %(levelname)s: %(message)s",
    force: bool = True,
    stream: bool = True,
    filename: Optional[Union[str, bytes, os.PathLike]] = None,
    **kwargs,
) -> None:
    handlers = []
    if stream:
        handlers += [logging.StreamHandler()]

    if filename:
        handlers += [logging.FileHandler(filename)]

    if logger is not None:
        _setup_logger(logger, level, format, handlers)
        return

    kwargs = {"level": level, "format": format, "handlers": handlers}
    if force:
        if sys.version_info < (3, 8):
            _setup_handlers(logging.root, reset=True)
        else:
            kwargs["force"] = force

    logging.basicConfig(**kwargs)

def log_dict(_logger: logging.Logger, d: Mapping, sep: str = " = ") -> None:
    for key, value in d.items():
        _logger.info(f"{key}{sep}{value}")


import BoltzmannMachine as bm
import QHO as qho
import numpy as np
import datetime
# Visualization imports
from IPython.display import clear_output
from PIL import Image
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['figure.dpi']=300
def sigmoid(x):
    return .5 * (1 + np.tanh(x / 2.))

# Set the quantum gas with N particles, a limit of 10 for the
# quantum numbers and default temperature and frequency
N = 10*10
gas = qho.QHOGas(N=N)
n_max = 10

training_size = 100000
# the amount of hidden units was set by trial and error
hidden_units = 70
# the recipe suggests to set the batchsize to 10, though it can range
# from 10 to 100
batchsize = 10
# the recipe suggests a learning rate that makes the weight updates about
# 1e-3 times the weights (to within an order of magnitude)
eta = 0.005
# the amount of steps was set by trial and error
nsteps = 300000

# define the validation set to be used in training_visualization
validation_set = gas.generate(amount=20)

def training_visualization(machine, current_step, total_steps, eta, a, b, w, da, db, dw):
    # Every now and then (every 50k steps), let us know that the training
    # is still running
    if current_step%50000 == 0:
        print("{:08d} / {:08d}".format(current_step, total_steps), end="   \r")

    # After 'checkpoint_steps', show the suggested plots
    checkpoint_steps = 10000
    if current_step%checkpoint_steps == 0 or current_step == total_steps-1:
        print(f"Showing at step {current_step}.")
        # Produce a sample starting from the validation set after 100 steps
        v_prime = machine.generate(validation_set, 100, a=a, b=b, w=w)
        # print useful plots for training
        plot_training(validation_set, v_prime, eta, a, b, w, da, db, dw)

def plot_training(v, v_prime, eta, a, b, w, da, db, dw):
    clear_output(wait=True)
    # Show how the weights light up for the state v
    hMean = sigmoid(np.dot(v, w) + b)
    image = Image.fromarray(hMean * 256).show()

    # Create the grid for all the other plots we want
    plt.rcParams.update({'font.size': 2})

    # plot histogram of initial vs generated
    n = np.arange(0,10)
    generated_quantum_numbers = np.rint(v_prime*10)
    plt.hist( generated_quantum_numbers.flatten(), bins=np.arange(0,10), density=True, label="Sampled" )
    plt.plot( n, gas.p_n(n), label="Theor." )
    plt.xlabel('n')
    plt.ylabel('P(n)')
    plt.legend()

    # plot histogram of visible, hidden, weights
    fig = plt.figure(constrained_layout=True)
    gs = fig.add_gridspec(ncols=3, nrows=2)
    def plotit(axis, values, title):
        axis.hist(values)
        axis.set_title(f"{title}: mm = {np.mean(np.fabs(values))}")
    plotit(fig.add_subplot(gs[0,0]), a, 'a')
    plotit(fig.add_subplot(gs[0,1]), w.flatten(), 'w')
    plotit(fig.add_subplot(gs[0,2]), b, 'b')
    # plot histogram of d_visible, d_hidden, d_weights
    plotit(fig.add_subplot(gs[1,0]), eta*da, 'da')
    plotit(fig.add_subplot(gs[1,1]), eta*dw.flatten(), 'dw')
    plotit(fig.add_subplot(gs[1,2]), eta*db, 'db')
    # show free energies of the average of samples
    x = lambda vv : b + np.dot(vv, w)
    free_training  = -np.dot(v, a) - np.sum( np.log(1 + np.exp(x(v))), axis=1)
    free_valdation = -np.dot(v_prime, a) - np.sum( np.log(1 + np.exp(x(v_prime))), axis=1)
    print(f"\nF_training={np.average(free_training)} vs F_validation={np.average(free_valdation)}\n")
    # Show.
    # CAUTION! This will freeze the execution
    plt.show()

# Init the boltzmann machine and train it while visualizing the suggested plots
training_set = gas.generate(amount=training_size, n_max=n_max)
m = bm.BoltzmannMachine(num_hidden=hidden_units)
a,b,w = m.train(training_set, batchsize=batchsize, eta=eta, nsteps=nsteps, do_while_training=None)
# Store in a file
run_id = int(datetime.datetime.now().timestamp())
np.savetxt(f"a_{run_id}.csv", a, delimiter=',')
np.savetxt(f"b_{run_id}.csv", b, delimiter=',')
np.savetxt(f"w_{run_id}.csv", w, delimiter=',')


from pathlib import Path

import torch
import torch.nn as nn
from torch.nn import functional as F
from torch.optim import Adam
from torch.nn import CrossEntropyLoss
from torch.utils.data import DataLoader

from torchvision.transforms import ToTensor
from torchvision.datasets.mnist import MNIST
import os, sys
import cv2
import numpy as np

# An installation agnostic method to find and link to root of the package which is mlfactory
#==========================================================
import re
try: #testing the functions locally without pip install
  import __init__
  cimportpath = os.path.abspath(__init__.__file__)
  if 'extensions' in cimportpath:
    print("local testing ")
    import mlfactory
    cimportpath = os.path.abspath(mlfactory.__file__)

except: #testing while mlfactory is installed using pip
  print("Non local testing")
  import mlfactory
  cimportpath = os.path.abspath(mlfactory.__file__)

main_package_loc = cimportpath[:cimportpath.rfind('mlfactory')+len('mlfactory')]
print("got main package location ",main_package_loc)

os.environ['top'] = main_package_loc
sys.path.append(os.path.join(os.environ['top']))
#==========================================================

from models.pytorch.timesformer import TimeSformer
from datetime import datetime as dt
import cv2
from collections import deque

import mss.tools #for fast screenshots
import pyautogui
from datetime import datetime as dt
import time

pressed = []

def agent_action(pred): #maybe need to incorporate multinomial sampling
    global pressed

    if pressed!=[]:
        for p in pressed:
            pyautogui.keyUp(p)

    if pred==0:
        pyautogui.keyDown('w') # for example key = 'a'
        pressed = ['w']
    if pred==1:
        pyautogui.keyDown('a') # for example key = 'a'
        pressed = ['a']
    if pred==2:
        pyautogui.keyDown('s') # for example key = 'a'
        pressed = ['s']
    if pred==3:
        pyautogui.keyDown('d') # for example key = 'a'
        pressed = ['d']

    if pred==4:
        pyautogui.keyDown('w') # for example key = 'a'
        pyautogui.keyDown('a') # for example key = 'a'
        pressed = ['w','a']
    if pred==5:
        pyautogui.keyDown('w') # for example key = 'a'
        pyautogui.keyDown('d') # for example key = 'a'
        pressed = ['w','d']
    if pred==6:
        pyautogui.keyDown('s') # for example key = 'a'
        pyautogui.keyDown('a') # for example key = 'a'
        pressed = ['s','a']
    if pred==7:
        pyautogui.keyDown('s') # for example key = 'a'
        pyautogui.keyDown('d') # for example key = 'a'
        pressed = ['s','a']

def close_agent():
    global pressed

    if pressed!=[]:
        for p in pressed:
            pyautogui.keyUp(p)

def record_screen(rs):
    #im = pyautogui.screenshot(region=(823, 153, 997, 464)) #takes around 0.15 s
    #cv2.imwrite(folder+"/"+str(idx)+".png", np.array(im))

    with mss.mss() as sct:
        # The screen part to capture
        monitor = {"top": 191, "left": 373, "width": 1125, "height": 611}
        #output = "sct-{top}x{left}_{width}x{height}.png".format(**monitor)
        output = "temp.png"

        # Grab the data
        sct_img = sct.grab(monitor) #takes around 0.03 s

        # Save to the picture file
        mss.tools.to_png(sct_img.rgb, sct_img.size, output=output)
        #print(output)
    im = cv2.imread("temp.png", 0)
    im = cv2.resize(im, (rs,rs))
    return im

if __name__ == '__main__':
    print("loading timesformer ")
    num_prev_conditioning_frames = 10
    C,D = 1, 112 #input image properties, assuming H=W=D
    batch_size = 1
    n_class = 9
    num_batches = 500
    LR = 0.0003
    datafolder = 3 #each number stores a single run of a game

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

    model = TimeSformer(
    dim = 256,
    image_size = D,
    patch_size = 8,
    channels = C,
    num_frames = num_prev_conditioning_frames,
    num_classes = n_class,
    depth = 8,
    heads = 8,
    dim_head =  64,
    attn_dropout = 0.1,
    ff_dropout = 0.1
    )

    if os.path.exists('weights.pt'):
        print("loading weights do far ")
        model = torch.load('weights.pt')

    print(sum(p.numel() for p in model.parameters())/1e6, 'M parameters')

    model.to(device)

    d = deque(maxlen=num_prev_conditioning_frames)

    time.sleep(5) #provide time for user to position the mouse cursor
    y = 8

    for _ in range(200): #try out 100 model actions
        x = record_screen(D)
        x = np.array(x/255.0, dtype = np.float32).reshape((1,D,D))

        if len(d)==num_prev_conditioning_frames: #now the agent can act upon sufficient history
            obs = np.array(d)
            inp = torch.from_numpy(obs).view((batch_size, num_prev_conditioning_frames, C, D, D)).to(device)
            out = model(inp)
            probs = F.softmax(out, dim=-1)

            act_idx = torch.argmax(probs)
            act_idx = torch.multinomial(probs, num_samples=1) # (B, 1)
            pred = act_idx.detach().cpu().numpy()[0][0] #should be a vector of length number of possible actions

            #pred = act_idx.detach().cpu().numpy() #should be a vector of length number of possible actions
            print("model pred ",pred)

            agent_action(pred)
            y = int(pred)

        #r = np.random.randint(0,9)
        #action_conditioning = (r/9.0)*np.ones((112,112), dtype = np.float32).reshape((1,D,D))
        #action_conditioning = np.random.sample((1,D,D)).astype(np.float32) #apply a random conditioning to each frame to help the network be able to think generatively

        #x = np.stack((x,action_conditioning)).reshape((2,D,D))
        #x = np.stack((x,x)).reshape((2,D,D))
        d.append(x)

    close_agent()


import torch
import torch.nn as nn
import torch.nn.functional as F
import time

thresh = 0.5  # neuronal threshold
lens = 0.5  # hyper-parameters of approximate function

# Define approximate firing function
class ActFun(torch.autograd.Function):
    @staticmethod
    def forward(ctx, input):
        if input.requires_grad:
            ctx.save_for_backward(input)
        return input.gt(thresh).float()

    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        temp = abs(input - thresh) < lens
        return grad_input * temp.float()

act_fun = ActFun.apply

class SCNN(nn.Module):
    def __init__(self, device, decay=0.8, init_gain=2.5, input_size=(304, 240, 2)): # input_size (x, y, c)
        super(SCNN, self).__init__()
        self.device = device
        self.decay = decay
        self.layers = []

        self.x = input_size[0]
        self.y = input_size[1]
        self.c = input_size[2]
        self.init_gain = init_gain  # Ensure starting spikerate
        self.s = "Input: " + str((1, self.c, self.y, self.x)) + "\n"  # string representation

        self.model = nn.ModuleList() # For pytorch functionality

        # Define network layout
        self.layers = (
            self.Conv2d(self, 16),
            self.MaxPool(self),
            self.Conv2d(self, 32),
            self.MaxPool(self),
            self.Conv2d(self, 64),
            self.MaxPool(self),
            self.Conv2d(self, 128),
            self.MaxPool(self),
            self.Conv2d(self, 256),
            self.MaxPool(self, stride=1),
            self.Conv2d(self, 512),
            self.MaxPool(self, stride=1),
            self.Conv2d(self, 1024)
        )

        self.output_spikes = torch.zeros(1, self.c, self.y, self.x, device=self.device)

    # Feed new inputs for continuous feedforward
    def feed(self, inputs):
        # Calculate firing based on input probabilities (approximates non-binary values such as gray)
        x = (inputs > torch.rand(inputs.size(), device=self.device)).float()

        for _, layer in enumerate(self.layers):
            x = layer.forward(x)

        self.output_spikes += x

    # Collect output from network
    def collect(self):
        outputs = self.output_spikes
        self.clear()
        return outputs

    # Clear output spikes
    def clear(self):
        self.output_spikes = torch.zeros(self.output_spikes.shape, device=self.device)

    # Reset membrane potentials and output spikes
    def reset_potentials(self):
        for i, layer in enumerate(self.layers):
            layer.reset()
        self.clear()

    def __str__(self):
        return self.s

    class Conv2d():
        def __init__(self, snn, f, size=3, stride=1):
            padding=1
            if snn.x % stride != 0 or snn.y % stride != 0:
                raise ValueError("Layer input", x, y, "would be rounded with stride", stride)

            self.snn = snn
            self.conv = nn.Conv2d(snn.c, f, kernel_size=size, stride=stride, padding=padding)
            nn.init.xavier_uniform_(self.conv.weight.data, gain=snn.init_gain) # Ensures an initial spike-rate

            snn.c = f
            snn.x = snn.x // stride
            snn.y = snn.y // stride
            snn.s += "Conv: " + str((1, snn.c, snn.y, snn.x)) + "\n"

            self.mem = self.spike = torch.zeros(1, snn.c, snn.y, snn.x, device=snn.device)
            snn.model.extend([self.conv]) # Used internally by pytorch

        # Iterative LIF model
        def forward(self, x):
            # Reset potential if last iteration spiked, otherwise decay it. Add new input.
            self.mem = self.mem * (1 - self.snn.decay) * (1. - self.spike) + self.conv(x)
            self.spike = act_fun(self.mem)  # Approximation firing
            return self.spike

        # Reset membrane potentials
        def reset(self):
            self.mem = self.spike = torch.zeros(self.mem.shape, device=self.snn.device)

    class AvgPool():
        def __init__(self, snn, size=2, stride=2):
            self.size = size
            self.stride = stride

            if snn.x % stride != 0 or snn.y % stride != 0:
                raise ValueError("Layer input size", snn.x, snn.y, "would get rounded with stride", stride)

            if stride == 1:  # For 0 padding
                snn.x -= size-1
                snn.y -= size-1
            else:
                snn.x = snn.x // stride
                snn.y = snn.y // stride
            snn.s += "Avg pool: " + str((1, snn.c, snn.y, snn.x)) + "\n"

        def forward(self, x):
            return F.avg_pool2d(x, self.size, stride=self.stride)

        def reset(self):
            return

    class MaxPool():
        def __init__(self, snn, size=2, stride=2):
            self.size = size
            self.stride = stride

            if snn.x % stride != 0 or snn.y % stride != 0:
                raise ValueError("Layer input size", snn.x, snn.y, "would get rounded with stride", stride)

            if stride == 1:  # For 0 padding
                snn.x -= size-1
                snn.y -= size-1
            else:
                snn.x = snn.x // stride
                snn.y = snn.y // stride
            snn.s += "Max pool: " + str((1, snn.c, snn.y, snn.x)) + "\n"

        def forward(self, x):
            return F.max_pool2d(x, self.size, stride=self.stride)

        def reset(self):
            return


"""

Given a list of non negative integers, arrange them such that they form the largest number.

For example:

Given [3, 30, 34, 5, 9], the largest formed number is 9534330.

Note: The result may be very large, so you need to return a string instead of an integer.

QUESTIONS YOU SHOULD ASK:
+ Is the input always going to be valid? Yes.
+ Can I get zeroes in the list? Yes.
+ Should I form the largest number with them or try to ignore them from the function? Use them to form it.

"""
from fractions import Fraction

"""

Since sorting in descending order doesn't work (because 34 > 9 but 9 appears first in the finished number), we need to
sort on a custom comparison A or B.

A) Given numbers X and Y, we append to form XY and YX, and then we compare. If XY > YX, then X should come first,
   otherwise Y should come first.

B) Given a number X, we calculate it's fraction in relation to the number of digits it has. For example, if X=3, then
   the fraction is 3/10^1 = 3/10. If X=30, the fraction is 30/10^2 = 30/100. In order to make 3 come before 30 as we
   need, we subtract 1 from the denominator. That makes X=3 --> 3/9 and X=30 --> 30/99, such as 3/9 > 30/99. This
   subtraction of 1 makes sure that all the cases of 3 vs 30 or 30 vs 300 always prioritize the number with the least
   digit count.
    It also makes the cases of 3 vs 31 or 3 vs 3i, with i > 3 favor the case of 3i as it should be. For example,
    X=3 --> 3/9 and X=34 --> 34/99 such that 3/9 < 34/99. Of course this also means that 3 vs 3i will favor 3 with
    i < 3. With i == 3 it doesn't matter, since they are "the same".

"""

def largest_number(numbers):
    # Sort by the fraction method B)
    numbers = sorted(numbers, key=lambda n: Fraction(n, (10 ** len(str(n)))-1), reverse=True)
    i = 0
    # This while is in place in case the array is full of zeroes. It will make answer just a '0' which is the
    # largest number.
    while i < len(numbers) - 1:
        if numbers[i] != 0:
            break
        else:
            i += 1
    answer = ''.join(str(digit) for digit in numbers[i:])
    return answer


from django.urls import path
from . import views
from django.conf.urls import url
from users.views import DependantListAPIView

urlpatterns = [
    path('dependants/', views.DependantListAPIView.as_view(),
         name=views.DependantListAPIView.name),
    path('dependants/<uuid:pk>/', views.DependantDetailAPIView.as_view(),
         name=views.DependantDetailAPIView.name),
    path('dependants/<uuid:pk>/prescription', views.PrescriptionCreate.as_view(),
         name=views.PrescriptionCreate.name),
    path('prescriptions/', views.PrescriptionList.as_view(),
         name=views.PrescriptionList.name),
    path('prescriptions/<uuid:pk>', views.PrescriptionDetail.as_view(),
         name=views.PrescriptionDetail.name),
    path('prescriptions/<uuid:pk>/', views.PrescriptionUpdate.as_view(),
         name=views.PrescriptionUpdate.name),

    path('prescriptions/<uuid:pk>/item', views.PrescriptionItemCreate.as_view(),
         name=views.PrescriptionItemCreate.name),
    path('prescription-items/', views.PrescriptionItemList.as_view(),
         name=views.PrescriptionItemList.name),
    path('prescription-items/<uuid:pk>', views.PrescriptionItemDetail.as_view(),
         name=views.PrescriptionItemDetail.name),
    # path('prescription-items/<uuid:pk>/', views.PrescriptionItemUpdate.as_view(),
    #      name=views.PrescriptionItemUpdate.name),

]


from typing import Optional, List

from fastapi import APIRouter, Depends

from helpers.auth import oauth2_scheme, is_admin
from helpers.users import UserHelper
from models import RoleTypes
from schemas.responses.users import UserOut

router = APIRouter(tags=["Users"])

@router.get(
    "/users/users/",
    dependencies=[Depends(oauth2_scheme), Depends(is_admin)],
    response_model=List[UserOut],
)
async def get_users(username: Optional[str] = None):
    if username:
        return await UserHelper.get_user_by_username(username=username)
    return await UserHelper.get_all_users()

@router.put(
    "/users/{user_id}/make-admin",
    dependencies=[Depends(oauth2_scheme), Depends(is_admin)],
    status_code=204,
)
async def make_admin(user_id: int):
    await UserHelper.change_role(RoleTypes.admin, user_id)
    return {"success": True}

@router.put(
    "/users/{user_id}/make-scraper",
    dependencies=[Depends(oauth2_scheme), Depends(is_admin)],
    status_code=204,
)
async def make_scraper(user_id: int):
    await UserHelper.change_role(RoleTypes.scraper, user_id)
    return {"success": True}


from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path

from .views import (
    custom_bad_request_view,
    custom_permission_denied_view,
    custom_page_not_found_view,
    custom_server_error_view,
)

urlpatterns = [
    path("admin/", admin.site.urls),
    path("accounts/", include("allauth.urls")),
    path("", include("home.urls")),
    path("products/", include("inventorize.urls")),
    path("products/", include("products.urls")),
    path('bag/', include('bag.urls')),
    path('checkout/', include('checkout.urls')),
    path('profile/', include('profiles.urls')),
    path('wishlist/', include('wishlist.urls')),
    #  allows to serve media files in development
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

handler400 = custom_bad_request_view
handler403 = custom_permission_denied_view
handler404 = custom_page_not_found_view
handler500 = custom_server_error_view


import json;
import vilo; app = vilo.buildApp(); wsgi = app.wsgi;

pasteMap = {};  # In-memory paste storage.

@app.route("GET", "/")
def get_homepage (req, res):
    return vilo.escfmt("""

TestBin: In-Memory Pastebin

            Visit /compose to compose a new paste.

            Go to /paste/{Paste-ID-here} to view a paste.

Paste IDs: %s

    """, json.dumps(list(pasteMap.keys()), indent=4));

@app.route("GET", "/compose")
def get_compose (req, res):
    return """

Compose Paste:

        <form method="POST" style="max-width: 500px;">
            <input type="text" name="title" placeholder="Title" style="width: 100%;">

            <textarea name="body" placeholder="Body ..." rows="10" style="width: 100%;"></textarea>

            <button>Submit</button>
        </form>

< Home

    """;

@app.route("POST", "/compose")
def post_compose (req, res):
    title = req.fdata.get("title") or "(Blank Title)";
    body = req.fdata.get("body") or "(Blank Body)";
    pasteId = len(pasteMap) + 1;
    pasteMap[pasteId] = vilo.dotsi.fy({
        "id": pasteId, "title": title, "body": body,
    });
    assert len(pasteMap) == pasteId;
    return vilo.escfmt("""

Paste Created!

            Visit /paste/%s to view your paste,
            or /compose to compose another.

< Home

    """, [pasteId, pasteId]);

@app.route("GET", "/paste/*")
def get_paste (req, res):
    pid = req.wildcards[0];
    if not (pid.isdigit() and int(pid) in pasteMap):
        raise vilo.error("
No such paste.
", 404);
    paste = pasteMap[int(pid)];
    return vilo.escfmt("""

Paste ID: %(id)s

%(title)s

%(body)s

< Home

    """, paste);

# Error related: :::::::::::::::::::::::::::::::::::::::::::

@app.frameworkError("route_not_found")
def error_routeNotFound (req, res, err):
    return "That route couldn't be found.";

@app.route("GET", "/static/**")
def get_static (req, res):
    relpath = req.wildcards[0];
    return res.staticFile("./static/no.such.path/" + relpath);

@app.frameworkError("file_not_found")
def error_fileNotFound (req, res, err):
    return "No such file.";

@app.route("GET", "/500")
def get_500 (req, res):
    1/0;
    assert False;

@app.frameworkError("unexpected_error")
def error_unexpected (req, res, err):
    return "Something broke.";

@app.route("GET", "/foo/*")
def get_foo (req, res):
    return vilo.escfmt("%s", repr(req.wildcards[0]));

@app.route("GET", "/redirect-home")
def get_redirHome (req, res):
    return res.redirect("/");


import pywt
import numpy as np

from chapter3_分析实验.mel import *

def greyList(n):
    """
    生成格雷编码序列
    参考：https://www.jb51.net/article/133575.htm
    :param n: 长度
    :return: 范围 2 ** n的格雷序列
    """

    def get_grace(list_grace, n):
        if n == 1:
            return list_grace
        list_before, list_after = [], []
        for i in range(len(list_grace)):
            list_before.append('0' + list_grace[i])
            list_after.append('1' + list_grace[-(i + 1)])
        return get_grace(list_before + list_after, n - 1)

    # get_grace生成的序列是二进制字符串，转化为10进制数
    return [int(i, 2) for i in get_grace(['0', '1'], n)]

def wavePacketRec(s, wname):
    """
    小波包重构
    :param s: 小波包分解系数，(a,d)间隔
    :param wname: 小波名
    :return: 各个分量的重构,注意这里重构的长度可能稍微大于原来的长度，如果要保证一样长，去除尾段的即可
    """
    out = []
    for i, l in enumerate(s):
        # 利用该数字去判断是a,d分量类型
        magic = i
        for j in range(int(np.log2(len(s)))):
            if magic % 2 == 0:
                # 为a分量时
                l = pywt.waverec([l, None], wname)
            else:
                # 为d分量时
                l = pywt.waverec([None, l], wname)
            magic = magic // 2
        out.append(l)

    grey_order = greyList(int(np.log2(len(s))))
    return np.array(out)[grey_order]

def wavePacketDec(s, jN, wname):
    """
    小波包分解，分解得到的是最后一层的结果，并非小波系数
    :param s: 源信号
    :param jN: 分解层数
    :param wname: 小波名
    :return:
        out: 小波包系数,是len为2**jN的list,以a,d相互间隔的输出
    """
    assert jN > 0 and isinstance(jN, int), 'Please take a positive integer as jN'
    out = []
    # 第一层分解
    a, d = pywt.dwt(s, wname)
    out.append(a)
    out.append(d)

    # 执行第2-jN次分解
    for level in range(1, jN):
        tmp = []
        for i in range(2 ** level):
            a, d = pywt.dwt(out[i], wname)
            tmp.append(a)
            tmp.append(d)
        out = tmp
    return out

def WPMFCC(x, fs, p, frameSize, inc, jN, wname, nFFT=512, n_dct=12):
    """
    利用小波包-MFCC的方式提取特征，处理流程参考：
    陈静，基于小波包变换和MFCC的说话人识别特征参数，语音技术，2009，DOI:10.16311/j.audioe.2009.02.017
    :param x: 输入信号
    :param fs: 采样率
    :param p: Mel滤波器组的个数
    :param frameSize: 分帧的每帧长度
    :param inc: 帧移
    :param jN: 小波包分解尺度
    :param wname: 小波包名
    :param nFFT: FFT点数
    :param n_dct: DCT阶数
    :return:
    """
    # 预处理-预加重
    xx = lfilter([1, -0.9375], [1], x)
    # 预处理-分幀
    xx = enframe(xx, frameSize, inc)
    # 预处理-加窗
    xx = np.multiply(xx, np.hanning(frameSize))
    # 分层计算小波包能量谱线并合并
    enp = np.zeros((xx.shape[0], nFFT // 2 + 1))
    for i, l in enumerate(xx):
        # 进行小波包分解
        wpcoeff = wavePacketDec(l, jN, wname)
        wp_l = wavePacketRec(wpcoeff, wname)[:, :frameSize]
        # 计算谱线能量
        en = np.abs(np.fft.rfft(wp_l, nFFT)) ** 2 / nFFT
        # 谱线合并
        enp[i] = np.sum(en, axis=0)
    # 计算通过Mel滤波器的能量
    bank = melbankm(p, nFFT, fs, 0, 0.5 * fs, 0)
    ss = np.matmul(enp, bank.T)
    # 计算DCT倒谱
    M = bank.shape[0]
    m = np.array([i for i in range(M)])
    mfcc = np.zeros((ss.shape[0], n_dct))
    for n in range(n_dct):
        mfcc[:, n] = np.sqrt(2 / M) * np.sum(np.multiply(np.log(ss), np.cos((2 * m - 1) * n * np.pi / 2 / M)), axis=1)
    return mfcc

def wavedec(s, jN, wname):
    ca, cd = [], []
    a = s
    for i in range(jN):
        a, d = pywt.dwt(a, wname)
        ca.append(a)
        cd.append(d)
    return ca, cd

def Wavelet_Hard(s, jN, wname):
    """
    小波硬阈值滤波
    :param s:
    :param jN:
    :param wname:
    :return:
    """
    ca, cd = wavedec(s, jN, wname)
    for i in range(len(ca)):
        thr = np.median(cd[i] * np.sqrt(2 * np.log((i + 2) / (i + 1)))) / 0.6745
        di = np.array(cd[i])
        cd[i] = np.where(np.abs(di) > thr, di, 0)
    calast = np.array(ca[-1])
    thr = np.median(calast * np.sqrt(2 * np.log((jN + 1) / jN))) / 0.6745
    calast = np.where(np.abs(calast) > thr, di, 0)
    cd.append(calast)
    coef = cd[::-1]
    res = pywt.waverec(coef, wname)
    return res

def Wavelet_Soft(s, jN, wname):
    """
    小波软阈值滤波
    :param s:
    :param jN:
    :param wname:
    :return:
    """
    ca, cd = wavedec(s, jN, wname)
    for i in range(len(ca)):
        thr = np.median(cd[i] * np.sqrt(2 * np.log((i + 2) / (i + 1)))) / 0.6745
        di = np.array(cd[i])
        cd[i] = np.where(np.abs(di) > thr, np.sign(di) * (np.abs(di) - thr), 0)
    calast = np.array(ca[-1])
    thr = np.median(calast * np.sqrt(2 * np.log((jN + 1) / jN))) / 0.6745
    calast = np.where(np.abs(calast) > thr, np.sign(calast) * (np.abs(calast) - thr), 0)
    cd.append(calast)
    coef = cd[::-1]
    res = pywt.waverec(coef, wname)
    return res

def Wavelet_hardSoft(s, jN, wname, alpha=0.5):
    """
    小波折中阈值滤波
    :param s:
    :param jN:
    :param wname:
    :param alpha:
    :return:
    """
    ca, cd = wavedec(s, jN, wname)
    for i in range(len(ca)):
        thr = np.median(cd[i] * np.sqrt(2 * np.log((i + 2) / (i + 1)))) / 0.6745
        di = np.array(cd[i])
        cd[i] = np.where(np.abs(di) > thr, np.sign(di) * (np.abs(di) - alpha * thr), 0)
    calast = np.array(ca[-1])
    thr = np.median(calast * np.sqrt(2 * np.log((jN + 1) / jN))) / 0.6745
    calast = np.where(np.abs(calast) > thr, np.sign(calast) * (np.abs(calast) - alpha * thr), 0)
    cd.append(calast)
    coef = cd[::-1]
    res = pywt.waverec(coef, wname)
    return res

def Wavelet_average(s, jN, wname, mu=0.1):
    """
    小波加权平均滤波
    :param s:
    :param jN:
    :param wname:
    :param alpha:
    :return:
    """
    ca, cd = wavedec(s, jN, wname)
    for i in range(len(ca)):
        thr = np.median(cd[i] * np.sqrt(2 * np.log((i + 2) / (i + 1)))) / 0.6745
        di = np.array(cd[i])
        cd[i] = np.where(np.abs(di) > thr, (1 - mu) * di + np.sign(di) * mu * (np.abs(di) - thr), 0)
    calast = np.array(ca[-1])
    thr = np.median(calast * np.sqrt(2 * np.log((jN + 1) / jN))) / 0.6745
    calast = np.where(np.abs(calast) > thr, (1 - mu) * calast + np.sign(calast) * mu * (np.abs(calast) - thr), 0)
    cd.append(calast)
    coef = cd[::-1]
    res = pywt.waverec(coef, wname)
    return res


import sys
import pygame
from game.Button import Button
import utils.solver as solver
import copy

from pygame.locals import (
    K_0,
    K_1,
    K_2,
    K_3,
    K_4,
    K_5,
    K_6,
    K_7,
    K_8,
    K_9,
    K_ESCAPE,
    K_RETURN,
    K_BACKSPACE,
    K_SPACE,
    QUIT,
    K_s
)

SCREEN_WIDTH = 500
SCREEN_HEIGHT = 600

def create_buttons(screen, grid):

    buttons = []
    fake_buttons = []
    x = 21
    for i in range(9):
        y = 18
        for j in range(9):
            if (j % 3 == 0):
                y += 3
            if (grid[j][i] == '0'):
                buttons.append( Button((x, y), coordinates=(j, i)) )
            else:
                fake_buttons.append( Button((x, y), grid[j][i], pygame.Color("Grey")) )
            y += 50
        if ((i+1) % 3 == 0):
            x += 3
        x += 50

    for fake_button in fake_buttons:
        fake_button.show(screen, update=False)
    for button in buttons:
        button.show(screen, update=False)
    pygame.display.update()
    return (buttons)

def init_screen():

    pygame.init()
    # for performance, pygame doesn't register mouse motion as an event
    pygame.event.set_blocked(pygame.MOUSEMOTION)
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    screen.fill(pygame.Color("White"))
    pygame.draw.rect(screen, pygame.Color("Black"), pygame.Rect(16, 16, 464, 464), border_radius=3)
    print_message(screen, 'Let the game begin')
    pygame.display.flip()
    return (screen)

def print_message(screen, message):

    font = pygame.font.SysFont("Arial", 35)
    text = font.render(message, 1, pygame.Color("Black"))
    size = text.get_size()
    surface = pygame.Surface((400, 100))
    surface.fill(pygame.Color("White"))
    screen.blit(surface, (50, 500))
    pygame.draw.rect(surface, pygame.Color("Orange"), pygame.Rect(0, 0, size[0], size[1]))
    pygame.draw.rect(surface, pygame.Color("White"), pygame.Rect(5, 5, size[0] - 10, size[1] - 10))

    screen.blit(surface, (SCREEN_WIDTH/2 - size[0]/2, 510))
    screen.blit(text, (SCREEN_WIDTH/2 - size[0]/2, 510))
    pygame.display.update()

def main(grid_file):

    screen = init_screen()
    keys_possible = {K_BACKSPACE: '0', K_0: '0', K_1: '1', K_2: '2', K_3: '3',
                K_4: '4', K_5: '5', K_6: '6', K_7: '7', K_8: '8', K_9: '9'}
    original_grid = solver.make_grid(grid_file)
    if (solver.check_grid_format(original_grid) is False):
        print('\nPlease use a well formatted map')
        print('(9x9 grid, separated by spaces with only numbers between 0 and 9)')
        exit()
    solver.print_instructions_cli()
    grid = copy.deepcopy(original_grid)
    buttons = create_buttons(screen, grid)
    running = True
    current_button = None

    while running:

        event = pygame.event.wait()
        if event.type == QUIT:
            running = False

        # event to select a tile with the mouse
        if event.type == pygame.MOUSEBUTTONDOWN:
            for button in buttons:
                if button.check_click(screen) == True:
                    if (current_button is not None and current_button != button):
                        current_button.draw_borders(screen, pygame.Color("White"))
                    current_button = button

        if event.type == pygame.KEYDOWN:
            # ESC quits the game
            if event.key == K_ESCAPE:
                running = False

            # enter (return) checks if the player's solution is valid
            if event.key == K_RETURN:
                if (solver.grid_is_valid(grid) == True):
                    print_message(screen, "Success")
                else:
                    print_message(screen, "Not quite right!")

            # space solves the sudoku automatically
            if event.key == K_SPACE or event.key == K_s:
                if (not solver.precheck_grid(original_grid)):
                    print_message(screen, 'No solution exists')
                else:
                    if (event.key == K_SPACE):
                        ret = solver.solver(original_grid, 0, 0, buttons, screen, visu=True)
                    elif (event.key == K_s):
                        ret = solver.solver(original_grid, 0, 0, buttons, screen, visu=False)
                    if (ret == False):
                        print_message(screen, 'No solution exists')
                    elif (ret == 2):
                        running = False
                    else:
                        print_message(screen, 'Solved')
                    pygame.display.update()

            # number keys to fill the tiles
            if event.key in keys_possible.keys() and current_button is not None:
                number_pressed = keys_possible[event.key]
                current_button.change_text(number_pressed)
                current_button.show(screen)
                grid[current_button.coord[0]][current_button.coord[1]] = number_pressed

        # pygame.display.flip()

    pygame.quit()

if __name__ == '__main__':
    if (len(sys.argv) != 2):
        print('\nOne argument required: location to grid file')
    else:
        main(sys.argv[1])


from discord.ext import commands
from lavalink import DefaultPlayer

from cogs.music import Music
from utilities import checks

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

    @commands.command(name='loopqueue', aliases=['lq', 'lqueue', 'loopq'])
    @checks.premium_only(1)
    async def loopqueue(self, ctx):
        player = self.bot.lavalink.players.get(ctx.guild.id)

        check = await Music.check_connect(ctx, player)
        if check is not None:
            return
        enabled = await player.toggle_loop_queue
        if enabled:
            return await ctx.send('🔁 | Successfully enabled loopqueue')
        else:
            await ctx.send('🔁 | Successfully disabled loopqueue')

@property
async def toggle_loop_queue(player):
    loop_queue_status = await player.loop_queue
    player.delete('loopqueue')
    player.store('loopqueue', not loop_queue_status)
    return await player.loop_queue

@property
async def get_loop_queue(player):
    loop_queue_status = player.fetch('loopqueue')
    if loop_queue_status is None:
        return False
    return loop_queue_status

def setup(bot):
    setattr(DefaultPlayer, 'toggle_loop_queue', toggle_loop_queue)
    setattr(DefaultPlayer, 'loop_queue', get_loop_queue)
    bot.add_cog(Loopqueue(bot))


import pytest
import nvme as d

import time
import logging

rand = True
seq = False
TEST_SCALE = 100    #100, 10

def do_fill_drive(rand, nvme0n1, region_end, nvme0):
    io_size = 8 if rand else 128
    ns_size = nvme0n1.id_data(7, 0)
    io_count = ns_size//io_size
    io_per_second = []

    w = nvme0n1.ioworker(io_size=io_size, lba_align=io_size,
                     lba_random=rand, qdepth=512,
                     region_end=region_end,
                     io_count=io_count, read_percentage=0,
                     output_io_per_second=io_per_second).start()
    while w.running:
        nvme0.getfeatures(7).waitdone()
        time.sleep(1)
    r = w.close()
    logging.info(io_per_second)

# full drive seq write
def test_clean_and_fill_drive(nvme0, nvme0n1):
    nvme0n1.format(512)
    do_fill_drive(seq, nvme0n1, nvme0n1.id_data(7, 0), nvme0)

# random write 20% region to steady
def test_random_write_small_region(nvme0, nvme0n1):
    do_fill_drive(rand, nvme0n1, nvme0n1.id_data(7, 0)//5, nvme0)

# iops: read/write/mixed
# latency: read/write/mixed
@pytest.mark.parametrize("readp", [100, 90, 67, 50, 33, 10, 0])
def test_steady_iops_latency(nvme0, nvme0n1, readp):
    percentile_latency = dict.fromkeys([50, 90, 99, 99.9, 99.999])
    io_per_second = []

    w = nvme0n1.ioworker(io_size=8, lba_align=8,
                         lba_random=rand, qdepth=64,
                         region_end=nvme0n1.id_data(7, 0)//5,
                         time=TEST_SCALE, read_percentage=readp,
                         output_percentile_latency=percentile_latency,
                         output_io_per_second=io_per_second).start()
    while w.running:
        nvme0.getfeatures(7).waitdone()
        time.sleep(1)
    r = w.close()
    logging.info(io_per_second)
    consistency = w.iops_consistency(90)
    logging.info(percentile_latency)
    max_iops = (r.io_count_read+r.io_count_nonread)*1000//r.mseconds
    logging.info(max_iops)

    with open("report.csv", "a") as f:
        f.write('%d\n' % readp)
        f.write('%.2f\n' % (consistency/100))
        f.write('%d\n' % percentile_latency[99.9])
        f.write('%d\n' % max_iops)

    # latency against iops
    for iops_percentage in [20, 40, 60, 80, 100]:
        iops = max_iops*iops_percentage//100
        w = nvme0n1.ioworker(io_size=8, lba_align=8,
                             lba_random=rand, qdepth=64,
                             iops=iops,
                             region_end=nvme0n1.id_data(7, 0)//5,
                             time=TEST_SCALE, read_percentage=readp,
                             output_percentile_latency=percentile_latency).start()
        while w.running:
            nvme0.getfeatures(7).waitdone()
            time.sleep(1)
        r = w.close()
        logging.info("iops %d, latency %dus" % (iops, r.latency_average_us))

        # write to report
        with open("report.csv", "a") as f:
            f.write('%d\n' % iops)
            f.write('%d\n' % r.latency_average_us)


import random
import math

import numpy as np
import pandas as pd
import pdb
from pysc2.agents import base_agent
from pysc2.lib import actions
from pysc2.lib import features

from models import RelationalNN as relnn

'''
Actions and Variables
'''

_NO_OP = actions.FUNCTIONS.no_op.id
_SELECT_POINT = actions.FUNCTIONS.select_point.id
_BUILD_SUPPLY_DEPOT = actions.FUNCTIONS.Build_SupplyDepot_screen.id
_BUILD_BARRACKS = actions.FUNCTIONS.Build_Barracks_screen.id
_TRAIN_MARINE = actions.FUNCTIONS.Train_Marine_quick.id
_SELECT_ARMY = actions.FUNCTIONS.select_army.id
_ATTACK_MINIMAP = actions.FUNCTIONS.Attack_minimap.id

_PLAYER_RELATIVE = features.SCREEN_FEATURES.player_relative.index
_UNIT_TYPE = features.SCREEN_FEATURES.unit_type.index
_PLAYER_ID = features.SCREEN_FEATURES.player_id.index

_PLAYER_SELF = 1

_TERRAN_COMMANDCENTER = 18
_TERRAN_SCV = 45
_TERRAN_SUPPLY_DEPOT = 19
_TERRAN_BARRACKS = 21

_NOT_QUEUED = [0]
_QUEUED = [1]

'''
Agents
'''
class SmartAgent(base_agent.BaseAgent):
    def step(self, obs):
        super(SmartAgent, self).step(obs)

        pdb.set_trace()

        return actions.FUNCTIONS.no_op()


import numpy as np

from numpy import pi, exp, sqrt
from math import ceil, floor
from scipy.special import gamma
from scipy.stats import t as t_distribution
from scipy.stats import norm as normal_distribution

class estimators:
    def point_normal_estimation( x):
        """
        Estimate the Parameters of the normal distribution based off the
        maximum likelihood estimator (MLE)
        Parameters:
        -----------
        x:      numpy nd-array
                If x is not a 1d-array, samples have to be arranged row wise
                (each row one sample)
        Returns:
        --------
        expectation:    float or numpy 1d-array
                        estimated expectation of the normal
                        distribution based off the samples
        variance:       float or numpy 1d-array
                        estimated variance of the normal
                        distribution based off the samples
        """
        n_samples = x.shape[0]
        return x.mean(0), x.std(0)

    def interval_expectation_estimation( x, confidence_level=0.95, theory=None):
        """
        Estimate the Parameters of the normal distribution based off the
        maximum likelihood estimator (MLE)
        Parameters:
        -----------
        x:      numpy nd-array
                If x is not a 1d-array, samples have to be arranged row wise
                (each row one sample)
        confidence_level:   float, default 0.95
                            confidence level of the estimated interval
        theory:             string, default None
                            based on which theory the interval should be estimated
                            choose between:
                            - MLE/maximum_likelihood estimator
                            - t-distribution
                            If no value is given it is chosen by the number of samples
        Returns:
        --------
        expectation_interval:   list of floats
                                Interval of the estimated expectation
        """
        n_samples = x.shape[0]
        if theory is None:
            if n_samples > 30:
                theory = 'MLE'
            else:
                theory = 't-distribution'

        q = (1+ confidence_level)/2
        expectation, variance = MLE_normal_distribution( x)
        if theory.lower() == 'mle' or 'maximum likelihood' in theory.lower():
            delta = normal_distribution.ppf( q) * variance/n_samples**0.5
        else:
            nu = n_samples -1
            delta = t_distribution( nu).ppf( q) * variance/n_samples**0.5
        return [ expectation-delta, expectation+delta ]

class sample_distribution:
    def normal_distribution( n, mu=0, variance=1):
        """
        samples n values out of the normal distribution
        with the given parameters
        Parameters:
        -----------
        n:          int
                    number of samples
        mu:         float, default 0
                    mu parameter (mean)
        variance:   float, default 1
                    variance parameter (sigma^2)
        Returns:
        --------
        x:          numpy 1d-array
                    discrete samples of the normal distribution
        """
        sigma = variance**0.5
        x = sigma* np.random.randn( n) + mu
        return x

    def uniform_distribution( n, a=0, b=1):
        """
        sample n values out of the uniform distribution
        shadows the numpy function
        Parameters:
        -----------
        a:          float, default 0
                    lower bound of the distribution
        b:          float, default 1
                    upper bound of the distribution
                    expected value of the normal distribution
        interval:   list of 2 floats, default None
                    interval on which to plot the distribution
        Returns:
        --------
        x:          numpy 1d-array
                    samples of the uniform distribution
        """
        x = np.random.uniform( a, b, n)
        return x

    def lognormal_distribution( n, mu=0, sigma=1):
        """
        samples n values out of the normal distribution
        shadows the numpy function
        Parameters:
        -----------
        n:          int
                    number of samples
        mu:         float, default 0
                    mu parameter
        sigma:      float, default 1
                    sigma parameter
        Returns:
        --------
        x:          numpy 1d-array
                    discrete samples of the normal distribution
        """
        return np.random.lognormal( mu, sigma, size=n)

class distribution_plots:
    def underlying(x, bins=200):
        """
        Redefine the samples into discrete bins to be able
        to plot the distribution given with discrete samples
        Parameters:
        -----------
        x:      numpy 1d-array
                given samples of the distribution
        bins:   int, default 100
                number of discretized bins to interpolate the plot with
        Returns:
        --------
        x:      numpy 1d-array
                interval spanned by x (input)
        y:      numpy 1d-array
                underlying distribution function of x
        """
        x = np.squeeze(x) #x is required to be of shape (n,)
        x = np.sort(x)
        n = len(x)
        bins = min(n/3, bins) #ensures that there are not too rough jumps
        f = np.zeros( (bins) )
        delta_x = (x[-1]-(x[0]) )/bins
        scalefactor = ((x[-1]-x[0])/(bins)) *n
        for i in range(bins):
            x_bin = x[0]+i*delta_x
            f[i] = (np.searchsorted(x, max(x_bin+delta_x,x[0]) ) - np.searchsorted(x, min(x_bin,x[-1]) ) )/scalefactor

        x_dist = np.arange(x[0], x[-1], (x[-1]-x[0])/(bins))[:bins]
        x_dist = x_dist + (x_dist[1]- x_dist[0])/2 #moves each value in the center of the 'bin', not the beginning

        return x_dist, f

    def normal( mu=0, variance=1, interval=None):
        """
        Plot the normal distribution based on its parameters
        on the given interval
        Parameters:
        -----------
        mu:         float, default 0
                    expected value of the normal distribution variance:   float, default 0
                    variance of the normal distribution (sigma^2)
        interval:   list three floats, default None
                    interval of sample defined as [start,stop,step]
        Returns:
        --------
        x:          numpy 1d-array
                    interval spanned by x (input)
        y:          numpy 1d-array
                    underlying distribution function of x
        """
        sigma = variance**0.5
        if interval is not None:
            x = np.arange( *interval )
        else:
            x = np.arange( min(-5*sigma, -0.5), max(5*sigma,0.5), (10*sigma)/100 ) +mu
        phi = 1/ (np.sqrt( 2*pi * variance)) * np.exp( -(x-mu)**2 / (2*variance) )
        return x, phi

    def uniform( a=0, b=1, interval=None):
        """
        sample arrays to plot the normal distribution
        based on its parameters on the given interval
        Parameters:
        -----------
        a:          float, default 0
                    lower bound of the distribution
        b:          float, default 1
                    upper bound of the distribution
                    expected value of the normal distribution
        interval:   list of 2 floats, default None
                    interval on which to plot the distribution
        Returns:
        --------
        x:          numpy 1d-array
                    interval spanned by x (input)
        y:          numpy 1d-array
                    underlying distribution function of x
        """
        if interval is not None:
            x = np.arange( *interval )
        else:
            x = np.arange( a, b+(b-a)/100, (b-a)/100)
        n = abs(b-a)

        phi = np.zeros( x.shape)
        for i in range( phi.shape[0]):
            if x[i] >= a and x[i] <= b:
                phi[i] = 1/n
        return x, phi

    def lognormal( mu=0, sigma=1, interval=None):
        """
        sample the lognormal distribution based
        on its parameters on the given interval
        Parameters:
        -----------
        mu:         float, default 0
                    mu parameter of the distribution
        sigma:      float, default 1
                    sigma paramter of the distribution
                    expected value of the normal distribution
        interval:   list of 2 floats, default None
                    interval on which to plot the distribution
        Returns:
        --------
        x:          numpy 1d-array
                    interval spanned by x (input)
        y:          numpy 1d-array
                    underlying distribution function of x
        """
        if interval is not None:
            x = np.arange( *interval )
        else:
            increment = 4*sigma/200
            #x = np.arange( 0+increment, 5*mu + 10*sigma+increment, increment )
            x = np.arange( 0.01, 5*(1+mu**3), 1/100 ) #should prolly be something with e^
        y = 1/(sqrt( 2*pi)*(sigma*x)) * exp( - (np.log( x) -  mu)**2/(2*sigma**2) )
        return x, y

    def levy( mu=0, gamma=1, interval=None):
        """
        samples n values out of the levy distribution
        with the given parameters
        Parameters:
        -----------
        mu:         float, default 0
                    mu parameter (start position)
        gamma:      float, default 1
                    variance parameter (sigma^2)
        interval:   list three floats, default None
                    interval of sample defined as [start,stop,step]
        Returns:
        --------
        x:          numpy 1d-array
                    interval spanned by x (input)
        y:          numpy 1d-array
                    underlying distribution function of x
        """
        if interval is not None:
            x = np.arange( *interval )
        else:
            x = np.arange( mu+0.01, 8*gamma+0.01 +(8*gamma-mu)/100, (8*gamma-mu)/200 )
        y = np.sqrt( gamma/(2*pi) ) * 1/( x- mu)**(3/2) * exp( - gamma/(2*(x-mu) ) )
        return x, y

    def t_distribution( nu, interval ):
        """
        Return two arrays to plot the t-distirbution based on its parameter
        Parameters:
        -----------
        nu          float
                    degrees of freedom for the distributino
        interval:   list three floats, default None
                    interval of sample defined as [start,stop,step]
        Returns:
        --------
        x:          numpy nd-array
                    sample array of the interval in 100 equidistant steps
        y:          numpy nd-array
                    f(x) of the interval above
        """
        if interval is not None:
            x = np.arange( *interval )
        else:
            x = np.arange( *interval )
        if nu > 320:
            print( "can't compute the gamma function for large nu (>320)\nReturning approximation of t-distribution")
            y = np.exp( -x**2/2)/ np.sqrt( 2*pi)
        else:
            y = (gamma( ( nu+1 )/2 ) / (np.sqrt( pi*nu)* gamma( nu/2) )
                    * (1+ x**2/nu)**(-(nu+1)/2) )
        return x, y

    def chi( k, interval=None):
        """
        Return two arrays to plot the t-distirbution based on its parameter
        Parameters:
        -----------
        k:          int
                    parameter of the chi distribution
        interval:   list three floats, default None
                    interval of sample defined as [start,stop,step]
        Returns:
        --------
        x:          numpy nd-array
                    sample array of the interval in 100 equidistant steps
        y:          numpy nd-array
                    f(x) of the interval above
        """
        if interval is not None:
            x = np.arange( *interval )
        else:
            x = np.arange( -3.5, 3.57, 7/100 ) #TODO find a fitting interval
        y = x**(k-1) * np.exp( -(x**2)/2) / (2**(k/2-1) *gamma(k/2) )
        return x, y

class data_binning:
    """
    compute the bins for data which is sampled in a 1d-array.
    This merely counts the number of samples within the bounds of the bin,
    defined by the interval sampled in the array
    """
    def bin_data( data, n_bins=None ):
        """
        Count the data and return the number of samples per bin
        If the number if n_bins is not specified it will automatically be computed
        Parameters:
        -----------
        data:       numpy 1d-array
                    given data
        n_bins:     int, default None
                    How many bins the data should be put into
                    Computes a sensitive default value if not specified

        Returns:
        --------
        count:      numpy nd-array
                    number of samples in the given bin
        center:     numpy nd-array
                    center value of the given bin
        width:      float
                    width of the bin
                    bin start/end is center -/+ width/2
        """
        n_samples = data.shape[0]

        # automatical choice for the number of bins
        if n_bins is None:
            if n_samples < 100:
                print( 'too few data samples present, returning un-binned data' )
                return [data.copy()]
            else:
                # Square-root choice
                n_bins = int(np.ceil(np.sqrt(n_sample)))

        inspected_values = data[:].flatten()
        sorting          = np.argsort( inspected_values)
        data             = data[ sorting ]
        data_bins   = []
        lower_bound = inspected_values.min()
        upper_bound = inspected_values.max() - lower_bound
        stepsize    = upper_bound/ n_bins

        center      = (0.5 + np.arange(n_bins))*stepsize+lower_bound
        width       = stepsize
        count       = np.zeros(n_bins)
        previous_sample = 0
        for i_bin in range( 0,n_bins-1):
            for j in range( previous_sample, n_samples):
                if data[ j ] > center[i_bin]+0.5*stepsize:
                    count[i_bin] = j-previous_sample
                    previous_sample = j
                    break
        count[-1] = n_samples-count[0:-1].sum()
        return count, center, width

    def rebin_data( count, center, n_bins, width=None):
        """
        Put the binned data into n_bins other bins.
        n_bins should be smaller than the number of previous bins, i.e. len(count)
        Parameters:
        -----------
        count:      numpy 1d-array
                    binned data, consisting of the number of samples per bin
        n_bins:     int
                    number of bins after re-binning. n_bins < len( count) !
        center:     numpy 1d-array
                    center of the bin, is used to accurately compute bins
                    assumes a uniform distribution of samples in each bin
        width:      float or numpy 1d-array, default None
                    width of each bin, if None is given then each is calculated
                    The median width is assumed for the first and last bin
                    NOT YET IMPLEMENTED, assumes equidistant bins
        Returns:
        --------
        count:      numpy 1d-array
                    new binned count
        center:     numpy 1d-array
                    new center of the bins
        width:      float or numpy 1d-array
                    new width of the bins
        """
        bin_length = len( count)/n_bins
        bin_overlap = bin_length % 1
        new_count = np.zeros( n_bins)
        remainder = 0.
        for i in range( n_bins -1):
            new_count[i] = count[ floor(i* bin_length)] * (1-remainder )
            new_count[i] += sum( count[ floor(i* bin_length)+1 : floor( (i+1) * bin_length) ] )
            remainder = round(remainder + bin_overlap, 15) % 1
            new_count[i] +=  count[floor( (i+1) * bin_length)] * remainder
        i +=1
        new_count[ -1] =  count[ floor(i* bin_length) ]  * (1-remainder )
        new_count[ -1] += sum( count[ floor(i* bin_length)+1 :] )

        #assume equidistant bins
        center_incr = center[1]-center[0]
        center = np.arange( center[0], center[-1]+ center_incr, center_incr * bin_length )[:n_bins] + (bin_length/2 - center_incr/2)
        if width is not None:
            if isinstance( width, np.ndarray ):
                width = (bin_length * width)[:n_bins]
            else:
                width = bin_length * width
        else:
            width = None
        return new_count, center, width

    def percentile( alpha, count, center=None, width=None ):
        """
        Compute the percentile of the binned data

        Parameters:
        -----------
        alpha:      float
                    searched percentile value
        count:      numpy 1d-array
                    number of samples in the bins
        center:     numpy 1d-array, default None
                    center value of the bins
        width:      numpy 1d-array, default None
                    width of the bins

        """
        n = count.shape[0]
        if( center is None ):
            center = np.arange(n)

        # step 1: find k such that count.cumsum()[k] >= alpha*n
        # find the k-th bin in which the percentile lies
        cum = np.cumsum(count)
        k   = 0
        N   = count.sum()*alpha
        while(cum[k] < N and k<n ):
            k += 1
        # step 2: if the data is discrete, then we are good
        if( width is None ):
            ## Debug only
            #print('k=%d, N=%d, left sum: %d, at k: %d, right sum: %d' % (k, N, n_left, count[k], n_right ))
            return center[k]

        # more computations needed: estimate the position within the bin:
        n_left  = count[0:k].sum()
        n_right = count[(k+1):].sum()
        theta = (N-n_left)/count[k]
        #print('k=%d, N=%d, left sum: %d, at k: %d, right sum: %d' % (k, N, n_left, count[k], n_right ))
        #print('theta=%f' % theta)
        return center[k] + (theta - 0.5) * width[k]

    def compute_relative_frequency( count ):
        """
        Compute the relative frequency of the data in their bins
        It is assumed that the data in each bin contains n-row samples
        The mean value of each bin will be
        Parameters:
        -----------
        count:      numpy 1d-array
                    containing the count for each bin
        Returns:
        --------
        frequency:  numpy-1d array
                    relative frequency for each bin
        """
        frequency = count/count.sum()
        return frequency

    def compute_cumulative_frequency( count ):
        """
        Compute the cumulative frequency of binned data
        It is assumed that the data in each bin contains n-row samples
        The mean value of each bin will be
        Parameters:
        -----------
        count:      numpy 1d-array
                    containing the count for each bin
        Returns:
        --------
        cumulative_frequency:   numpy-1d array
                                relative frequency for each bin
        """
        frequency = data_binning.compute_relative_frequency( count )
        cumulative_frequency = np.cumsum(frequency)
        return cumulative_frequency


import spacy
nlp = spacy.load("en_core_web_sm")
third_person_prons = ["he", "she", "it", "they", "him", "her", "them", "his", "her", "its", "their"]

def create_doc(utt):
    return nlp(utt)

def _rewrite_utt(doc, first_topic="", previous_topic="",
                 context_list=None, trailing=False):
        new_utt = ""
        for token in doc:
            if (token.tag_ == "PRP" or token.tag_ == "PRP$") \
                    and token.text in third_person_prons:
                if previous_topic != "":
                    new_utt += previous_topic + " "
                if first_topic != "":
                    new_utt += first_topic + " "
            else:
                new_utt += token.text + " "

        # TRAILING THE TOPIC
        if trailing:
            if previous_topic.lower() not in new_utt.lower():
                new_utt += previous_topic + " "
            if first_topic.lower() not in new_utt.lower():
                new_utt += first_topic

        # TRAILING THE CONTEXT
        if context_list is not None:
            new_utt += " ".join(context_list)

        return new_utt

def _find_cue_topic(doc):
    cue_phrases = ["tell me more about", "tell me about"]
    third_person_prons = ["he", "she", "it", "they", "him", "her", "them",
                          "his", "her", "its", "their"]

    current_topic = ""
    pron = False
    for cue in cue_phrases:
        if cue in str(doc).lower():
            # check if pron:
            for token in doc:
                if (token.tag_ == "PRP" or token.tag_ == "PRP$") and \
                        token.text in third_person_prons:
                    pron = True
            if not pron:
                current_topic = str(doc).lower().replace(cue, "").replace(".","")
    return current_topic

def _find_topic(doc):
    """
    We remove if "chunk.root.dep_ in pos_list" for GT and winning method to avoid problems from spacy, when it cannot
    analyse the phrase.
    """
    pos_list = ["nsubj", "dobj", "pobj"]
    topic = ""

    # NEW FEATURE
    # check if it's a cue topic first
    cue_topic = _find_cue_topic(doc)
    if cue_topic != "":
        return cue_topic

    # GET the first topic
    for chunk in doc.noun_chunks:
        # if chunk.root.dep_ in pos_list:
            if chunk.root.pos_ != "PRON":
                topic += " " + chunk.text

    # NO FIRST TOPIC - trick for "Describe Uranus."
    if topic == "":
        for token in doc:
            if token.pos_ not in ["VERB", "PUNCT"]:
                topic += token.text + " "

    return topic

def _find_topic_all(doc):
    pos_list = ["nsubj", "dobj", "pobj"]
    topic = ""

    # NEW FEATURE
    # check if it's a cue topic first
    cue_topic = _find_cue_topic(doc)
    if cue_topic != "":
        return cue_topic

    # GET the first topic
    for chunk in doc.noun_chunks:
        if chunk.root.pos_ != "PRON":
            topic += " " + chunk.text

    # NO FIRST TOPIC - trick for "Describe Uranus."
    if topic == "":
        for token in doc:
            if token.pos_ not in ["VERB", "PUNCT"]:
                topic += token.text + " "

    return topic