# -*-- coding:utf-8 -*--
import re

class ChangBaDecrypt(object):
    encrypt_key = [-50, -45, 110, 105, 64, 90, 97, 119, 94, 50, 116, 71, 81, 54, -91, -68, ]

    def __init__(self):
        self.pattern = re.compile(r',0>')

    def decrypt(self, content):
        decrypt_content = bytearray()
        for i in range(0, len(content)):
            var = content[i] ^ self.encrypt_key[i % 16]
            decrypt_content.append(var & 0xff)
        return decrypt_content.decode('utf-8')

    def decrypt_by_file(self, filename):
        with open(filename, 'rb') as f:
            content = f.read()
            f.close()
            decrypt = self.decrypt(content)
            return decrypt

    def convert_to_new(self, content):
        content = self.pattern.sub('>', content)
        content = re.sub(r'\[[^\]]+\]\r?\n', '', content)
        lines = re.split(r'\r?\n', content)

        offset_item = re.findall(r'\[(\d+).(\d+)\]', content)
        start, _ = offset_item[0]
        replace_pattern = re.compile(r'(\[\d+,\d+\])')
        main_content = []
        for line in lines:
            if not line.endswith(']') and len(line) > 0:
                line = replace_pattern.sub(r'\1{red}', line)
                main_content.append(line + ';')
        content = '[offset:%d]\n[refrain:%d]\n[halfSong:%d]%s%s' % (
            int(start), 0, 0, "\n" * 6, "\n".join(main_content)
        )
        content.strip('\n')
        return content


import string

from bs4 import BeautifulSoup
from classObj import *
import requests

class SubjectObj:

    #Holds to top level URL
    mainURL = 'https://catalog.unc.edu/courses/'

    def __init__(self, name, abb, href):
        self.name = name
        self.abbreviation = abb
        self.href = href
        self.classes = []

    def __str__(self):
        return f'{self.name}({self.abbreviation}) {self.href}'

    def getClasses(self,csv_writer):

        #Opens the subject page and gets the soup from it
        newUrl = self.mainURL + self.href
        page = requests.get(newUrl).text
        subjectHTML = BeautifulSoup(page,'lxml')

        #List holding the course blocks
        courseblocks = []
        for cb in subjectHTML.find_all('div', class_='courseblock'):
            courseblocks.append(cb)

        #Loop that creates the classObj's
        for cb in courseblocks:
            courseBlockTitle = cb.find('p',class_='courseblocktitle').strong.text.strip()
            courseBlockTitleParts = courseBlockTitle.split('.')

            self.prepTitleBlock(courseBlockTitleParts)

            #Holds the abbreviation for the class
            abb = self.getGetAbb(courseBlockTitleParts)

            #Holds the number for the class
            num = self.getNum(courseBlockTitleParts)

            #Holds the full title for the class
            fullTitle = self.getFullTitle(courseBlockTitleParts)

            #Holds the raw credits for the class
            rawCreds = self.getRawCreds(courseBlockTitleParts)

            #Gets all of the course descriptions
            courseBlockDesc = cb.find('p',class_='courseblockdesc')

            #Holds the description of the class
            rawDesc = self.getRawClassDesc(courseBlockDesc)

            #Creates the classObj for each class
            a = ClassObj()
            a.setRawCredits(rawCreds)
            a.setAbbreviation(abb)
            a.setNumber(num)
            a.setFullTitle(fullTitle)
            a.setDesc(rawDesc)
            self.csvWrite(a,csv_writer)
            #print(a)

    #Preps the courseblockTitleParts array and handles the various formats in the titles of the classes
    def prepTitleBlock(self,courseBlockTitleParts):

        # Handling the M.F.A Classes
        if (len(courseBlockTitleParts) == 7):
            courseBlockTitleParts[
                1] = f'{courseBlockTitleParts[1]}.{courseBlockTitleParts[2]}.{courseBlockTitleParts[3]}.{courseBlockTitleParts[4]}'
            courseBlockTitleParts.remove(courseBlockTitleParts[2])
            courseBlockTitleParts.remove(courseBlockTitleParts[2])
            courseBlockTitleParts.remove(courseBlockTitleParts[2])

        # Handling of the classes with M.A. and Ph.D in their titles
        if (len(courseBlockTitleParts) == 6 and (courseBlockTitleParts[1] == '  M' or courseBlockTitleParts[2] == 'D')):
            courseBlockTitleParts[
                1] = f'{courseBlockTitleParts[1]}.{courseBlockTitleParts[2]}. {courseBlockTitleParts[3]}'
            courseBlockTitleParts.remove(courseBlockTitleParts[2])
            courseBlockTitleParts.remove(courseBlockTitleParts[2])

        subjectsWithPeriodInTitle = ['ENGL', 'ARTH', 'GERM', 'HPM', 'HIST', 'INLS']

        #Handling of the classes with a period in the class title
        if (len(courseBlockTitleParts) == 5 and (courseBlockTitleParts[0][:4] in subjectsWithPeriodInTitle or courseBlockTitleParts[0][:3] in subjectsWithPeriodInTitle) and not('  0' in courseBlockTitleParts or '  1' in courseBlockTitleParts)):
            courseBlockTitleParts[1] = f'{courseBlockTitleParts[1]}.{courseBlockTitleParts[2]}'
            courseBlockTitleParts.remove(courseBlockTitleParts[2])

        # Handling of the classes with half credit-hour classes
        if (len(courseBlockTitleParts) == 5 and (courseBlockTitleParts[0][:4] != 'ARCH')):
            courseBlockTitleParts[-2] = f'{courseBlockTitleParts[-3]}.{courseBlockTitleParts[-2]}'
            courseBlockTitleParts.remove(courseBlockTitleParts[-3])

        # Handling of the first weird ARCH class
        if (len(courseBlockTitleParts) == 5):
            courseBlockTitleParts[0] = courseBlockTitleParts[0] + courseBlockTitleParts[1]
            courseBlockTitleParts.remove(courseBlockTitleParts[1])

    #Returns the abbreviation of the subject
    def getGetAbb(self,courseBlockTitleParts):
        return courseBlockTitleParts[0].split('\xa0')[0]

    #Returns the number of the class
    def getNum(self,courseBlockTitleParts):
        num = courseBlockTitleParts[0].split('\xa0')[1]

        # try:
        #     num = num.replace("H", "")
        #     num = num.replace("L", "")
        #     num = num.replace("B", "")
        #     num = num.replace("A", "")
        #     num = num.replace("C", "")
        #     num = num.replace("I", "")
        #     num = num.replace("P", "")
        #
        # except:
        #     print('Missing H/L Argument')
        #
        # try:
        #     num = int(num)
        # except:
        #     num = 0
        #     print('Problem with num assignment')

        return num

    #Returns the full title of the class
    def getFullTitle(self,courseBlockTitleParts):
        return courseBlockTitleParts[1].strip()

    #Returns the raw credits string of the class
    def getRawCreds(self,courseBlockTitleParts):
        return float(courseBlockTitleParts[-2].strip(string.ascii_letters).replace('-',''))

    #Returns the class description
    def getRawClassDesc(self,courseBlockDesc):
        return courseBlockDesc.text.strip()

    def csvWrite(self,a,csv_writer):
        csv_writer.writerow([a.abb,a.number,a.fullTitle,a.rawCredits,a.minCredits,a.maxCredits,a.hasCreditRange,a.genEds,a.desc])


cook_book = {
  'Омлет': [
    {'ingredient_name': 'Яйцо', 'quantity': 2, 'measure': 'шт.'},
    {'ingredient_name': 'Молоко', 'quantity': 100, 'measure': 'мл'},
    {'ingredient_name': 'Помидор', 'quantity': 2, 'measure': 'шт'}
    ],
  'Утка по-пекински': [
    {'ingredient_name': 'Утка', 'quantity': 1, 'measure': 'шт'},
    {'ingredient_name': 'Вода', 'quantity': 2, 'measure': 'л'},
    {'ingredient_name': 'Мед', 'quantity': 3, 'measure': 'ст.л'},
    {'ingredient_name': 'Соевый соус', 'quantity': 60, 'measure': 'мл'}
    ],
  'Запеченный картофель': [
    {'ingredient_name': 'Мед', 'quantity': 2, 'measure': 'ст.л'},
    {'ingredient_name': 'Картофель', 'quantity': 1, 'measure': 'кг'},
    {'ingredient_name': 'Чеснок', 'quantity': 3, 'measure': 'зубч'},
    {'ingredient_name': 'Сыр гауда', 'quantity': 100, 'measure': 'г'},
    ]
  }

shop_list = {}

def get_ingredients(dish, person_count):
  d = {}
  for i in cook_book[dish]:
    key = i.pop('ingredient_name')
    i['quantity'] *= person_count
    d.update({key:i})
  return d

def get_shop_list_by_dishes(dishes, person_count):
  att = 'quantity'
  for dish in dishes:
    res = get_ingredients(dish, person_count)
    for key in res:
      test = shop_list.get(key)
      if test == None:
        shop_list.update({key:res[key]})
      else:
        shop_list[key][att] += res[key][att]

def check_user(dishes, person_count):
  user = dishes
  for dish in user:
    print(dish)
    if cook_book.get(dish) == None:
      print(f'{dish} does not exist, so has been deleted from input information.')
      dishes.pop(dishes.index(dish))

dishes = input("Enter dishes through /.").split('/')
person_count = int(input("Enter the number of guests."))

check_user(dishes, person_count)
get_shop_list_by_dishes(dishes, person_count)
print(shop_list)


import numpy as np

def read_file(path):
    matrix = []
    with open(path, 'r') as f:
        for line in f.readlines():
            row_string = line.split()
            row = list(map(int, row_string))
            matrix.append(row)
    return matrix

def multiplication_matrix(first_matrix, second_matrix):
    count_row = len(first_matrix)
    count_columns = len(second_matrix[0])
    matrix = []
    for i in range(count_row):
        matrix.append([0]*count_columns)

    for i in range(count_row):
        for j in range(count_columns):
            for k in range(len(first_matrix[i])):
                matrix[i][j] += first_matrix[i][k] * second_matrix[k][j]
    return matrix

def using_numpy():
    a = np.array(read_file("/Users/gwymlas/Desktop/1.txt"))
    b = np.array(read_file("/Users/gwymlas/Desktop/2.txt"))

    result = a.dot(b)
    print("Multiplication matrix using numpy")
    print(result)

    c_matrix = np.array(read_file("/Users/gwymlas/Desktop/result.txt"))
    print("Multiplication matrix using c++")
    print(c_matrix)

    print("Checking multiplication")
    print(np.array_equal(c_matrix, result))

def main():
    using_numpy()
    # a = read_file("/Users/gwymlas/Desktop/1.txt")
    # b = read_file("/Users/gwymlas/Desktop/2.txt")

    # c = multiplication_matrix(a, b)

if __name__ == "__main__":
    main()


# encoding: utf-8
# (C) 2001 - 2020 Rajkumar Palani <rajkumarpalani07@gmail.com>

import pytest

from tamil import utf8

def test_get_tamil_utf_count():
    expected = 364
    assert(utf8.get_tamil_utf_count() == expected)

def test_get_tamil_utf():
    expected = 364
    assert(len(utf8.get_tamil_utf()) == expected)

    temp1 = list(filter(lambda x: (x == 'அ'), utf8.get_tamil_utf()))
    assert(('அ' in temp1[0]) == True)

def test_validate_char():
    input1 = "ட"
    assert(utf8.validate_char(input1) == True)
    input2 = 'அ'
    assert(utf8.validate_char(input2) == True)

def test_validate_char_failing():
    input1 = 1000
    with pytest.raises(TypeError, match="Input value must be unicode string"):
        utf8.validate_char(input1)

    input2 = ''
    with pytest.raises(ValueError, match="Input value is empty"):
        utf8.validate_char(input2)

    input3 = 'Ж'
    with pytest.raises(ValueError, match="Input value is not a valid utf8 tamil character"):
        utf8.validate_char(input3)


import App

# types for initializing objects create from C.
UNKNOWN = 0
SABRE = 1
MIRANDA = 2
NOVA = 3
NORWAY = 4
STEAMRUNNER = 5
CONSTELLATION = 6
CENTAUR = 7
DEFIANT = 8
INTREPID = 9
EXCELSIOR = 10
EXCELSIORREFIT = 11
AMBASSADOR = 12
LUNA = 13
AKIRA = 14
NEBULA = 15
GALAXY = 16
SOVEREIGN = 17
PROMETHEUS = 18
SOVEREIGNREFIT = 19
BIRDOFPREY = 20
KVORT = 21
KTINGA = 22
VORCHA = 23
NEGHVAR = 24
TALON = 25
FALCON = 26
VALDORE = 27
WARBIRD = 28
SCIMITAR = 29
HIDEKI = 30
GALOR = 31
KELDON = 32
OBSIDIANKELDON = 33
CARDHYBRID = 34
BUG = 35
BREEN = 36
VORTA = 37
FOUNDER = 38
RECTANGLE = 39
DIAMOND = 40
SPHERE = 41
CUBE = 42
TACTICALCUBE = 43
ASSIMILATIONCUBE = 44
MARAUDER = 45
KESSOKLIGHT = 46
KESSOKHEAVY = 47
SONACRUISER = 48
SONABATTLESHIP = 49
BIOSHIP = 50
HIROGENHUNTER = 51
HIROGENHOLOSHIP = 52
HIROGENVENATIC = 53
KAZONRAIDER = 54
KAZONPREDATOR = 55
KRENIMPATROL = 56
KRENIMWARSHIP = 57
KRENIMTIMESHIP = 58
MALONFREIGHTER = 59
VIDIIANCRUISER = 60
SHUTTLE = 61
FIGHTER = 62
DANUBE = 63
DANUBEREFIT = 64
DELTAFLYER = 65
ARMOREDINTREPID = 66
NX = 67
CONSTITUTION = 68
CONSTITUTIONREFIT = 69
FREIGHTER = 70
TRANSPORT = 71
ESCAPEPOD = 72
CARDFREIGHTER = 73
SUNBUSTER = 74
WEAPONSPLATFORM = 75
KESSOKMINE = 76
FEDOUTPOST = 77
SPACEFACILITY = 78
FEDCOMPLEX = 79
FEDSTARBASE = 80
DS9 = 81
CARDOUTPOST = 82
CARDSTATION = 83
CARDSTARBASE = 84
COMMARRAY = 85
COMMLIGHT = 86
DRYDOCK = 87
PROBE = 88
DECOY = 89
ASTEROID = 90
ASTEROID1 = 91
ASTEROID2 = 92
ASTEROID3 = 93
AMAGON = 94
BIRANUSTATION = 95
ENTERPRISE = 96
GERONIMO = 97
PEREGRINE = 98
ASTEROIDH1 = 99
ASTEROIDH2 = 100
ASTEROIDH3 = 101
BUGRAMMER = 102
WALL = 103
KRENIMORBSHIP = 104
MAX_SHIPS = 105
MAX_FLYABLE_SHIPS = MAX_SHIPS

# Setup tuples
kSpeciesTuple = (
	(None, 0, "Neutral", 0),
	("Sabre", 109, "Federation", 1),
	("Miranda", 110, "Federation", 1),
	("Nova", 111, "Federation", 1),
	("Norway", 112, "Federation", 1),
	("Steamrunner", 113, "Federation", 1),
	("Constellation", 114, "Federation", 1),
	("Centaur", 115, "Federation", 1),
	("Defiant", 116, "Federation", 1),
	("Intrepid", 117, "Federation", 1),
	("Excelsior", 118, "Federation", 1),
	("ExcelsiorR", 119, "Federation", 1),
	("Ambassador", App.SPECIES_AMBASSADOR, "Federation", 1),
	("Luna", 120, "Federation", 1),
	("Akira", App.SPECIES_AKIRA, "Federation", 1),
	("Nebula" , App.SPECIES_NEBULA, "Federation", 1),
	("Galaxy", App.SPECIES_GALAXY, "Federation", 1),
	("Sovereign" , App.SPECIES_SOVEREIGN, "Federation", 1),
	("Prometheus" , 121, "Federation", 1),
	("SovereignR" , 122, "Federation", 1),
	("BirdOfPrey", App.SPECIES_BIRD_OF_PREY, "Klingon", 1),
	("Kvort", 403, "Klingon", 1),
	("KTinga", 404, "Klingon", 1),
	("Vorcha" , App.SPECIES_VORCHA, "Klingon", 1),
	("Neghvar", 405, "Klingon", 1),
	("Talon" , 302, "Romulan", 1),
	("Falcon" , 303, "Romulan", 1),
	("Valdore" , 304, "Romulan", 1),
	("Warbird" , App.SPECIES_WARBIRD, "Romulan", 1),
	("Scimitar" , 305, "Romulan", 1),
	("Hideki" , 205, "Cardassian", 1),
	("Galor" , App.SPECIES_GALOR, "Cardassian", 1),
	("Keldon" , App.SPECIES_KELDON, "Cardassian", 1),
	("ObsidianKeldon" , 206, "Cardassian", 1),
	("CardHybrid", App.SPECIES_CARDHYBRID, "Cardassian", 1),
	("Bug" , 801, "Dominion", 1),
	("Breen" , 802, "Dominion", 1),
	("Vorta" , 803, "Dominion", 1),
	("Founder" , 804, "Dominion", 1),
	("Rectangle" , 901, "Borg", 1),
	("Diamond" , 902, "Borg", 1),
	("Sphere" , 903, "Borg", 1),
	("Cube" , 904, "Borg", 1),
	("CubeT" , 905, "Borg", 1),
	("CubeA" , 906, "Borg", 1),
	("Marauder" , App.SPECIES_MARAUDER, "Ferengi", 1),
	("KessokLight" , App.SPECIES_KESSOK_LIGHT, "Kessok", 1),
	("KessokHeavy" , App.SPECIES_KESSOK_HEAVY, "Kessok", 1),
	("SonaCruiser" , 805, "Sona", 1),
	("SonaBattleship" , 806, "Sona", 1),
	("Bioship" , 750, "Delta Quadrant", 1),
	("HirogenHunter" , 751, "Delta Quadrant", 1),
	("HirogenHoloship" , 752, "Delta Quadrant", 1),
	("HirogenVenatic" , 753, "Delta Quadrant", 1),
	("KazonRaider" , 754, "Delta Quadrant", 1),
	("KazonPredator" , 755, "Delta Quadrant", 1),
	("KrenimPatrol" , 756, "Delta Quadrant", 1),
	("KrenimWarship" , 757, "Delta Quadrant", 1),
	("KrenimTimeship" , 758, "Delta Quadrant", 1),
	("MalonFreighter" , 759, "Delta Quadrant", 1),
	("VidiianCruiser" , 760, "Delta Quadrant", 1),
	("Shuttle" , App.SPECIES_SHUTTLE, "Shuttles", 1),
	("Fighter" , 123, "Shuttles", 1),
	("Danube" , 124, "Shuttles", 1),
	("DanubeR" , 125, "Shuttles", 1),
	("DeltaFlyer" , 126, "Shuttles", 1),
	("ArmoredIntrepid" , 127, "Other", 1),
	("NX" , 128, "Other", 1),
	("Constitution" , 129, "Other", 1),
	("ConstitutionR" , 130, "Other", 1),
	("Freighter" , App.SPECIES_FREIGHTER, "Other", 1),
	("Transport" , App.SPECIES_TRANSPORT, "Other", 1),
	("EscapePod" , App.SPECIES_ESCAPEPOD, "Other", 1),
	("CardFreighter" ,  App.SPECIES_CARDFREIGHTER, "Other", 1),
	("Sunbuster" , App.SPECIES_SUNBUSTER, "Other", 1),
	("OrbitalWeaponsPlatform" , 207, "Other", 1),
	("KessokMine" , App.SPECIES_KESSOKMINE, "Other", 1),
	("FedOutpost" , App.SPECIES_FED_OUTPOST, "Bases", 1),
	("SpaceFacility" , App.SPECIES_SPACE_FACILITY, "Bases", 1),
	("FedComplex" , 761, "Bases", 1),
	("FedStarbase" , App.SPECIES_FED_STARBASE, "Bases", 1),
	("DS9" , 762, "Bases", 1),
	("CardOutpost" , App.SPECIES_CARD_OUTPOST, "Bases", 1),
	("CardStation" , App.SPECIES_CARD_STATION, "Bases", 1),
	("CardStarbase" , App.SPECIES_CARD_STARBASE, "Bases", 1),
	("CommArray" , App.SPECIES_COMMARRAY, "Other", 1),
	("CommLight", App.SPECIES_COMMLIGHT, "Other", 1),
	("DryDock" , App.SPECIES_DRYDOCK, "Bases", 1),
	("Probe" , App.SPECIES_PROBE, "Other", 1),
	("Decoy" , App.SPECIES_PROBETYPE2, "Other", 1),
	("Asteroid" , App.SPECIES_ASTEROID, "Neutral", 1),
	("Asteroid1" , App.SPECIES_ASTEROID, "Neutral", 1),
	("Asteroid2" , App.SPECIES_ASTEROID, "Neutral", 1),
	("Asteroid3" , App.SPECIES_ASTEROID, "Neutral", 1),
	("Amagon", App.SPECIES_ASTEROID, "Neutral", 1),
	("BiranuStation", App.SPECIES_SPACE_FACILITY, "Neutral", 1),
	("Enterprise", App.SPECIES_SOVEREIGN, "Neutral", 1),
	("Geronimo", App.SPECIES_AKIRA, "Neutral", 1),
	("Peregrine", App.SPECIES_NEBULA, "Neutral", 1),
	("Asteroidh1" , App.SPECIES_ASTEROID, "Neutral", 1),
	("Asteroidh2" , App.SPECIES_ASTEROID, "Neutral", 1),
	("Asteroidh3" , App.SPECIES_ASTEROID, "Neutral", 1),
	("BugRammer" , 1001, "Dominion", 1),
	("Wall" , 1002, "Neutral", 1),
	("KrenimOrbship" , 1003, "Delta Quadrant", 1),
	(None, 1, "Neutral", 1))

def GetShipFromSpecies (iSpecies):
	if (iSpecies <= 0 or iSpecies >= MAX_SHIPS):
#		print ("Species out of range")
		return None

	pSpecTuple = kSpeciesTuple [iSpecies]
	pcScript = pSpecTuple [0]

	ShipScript = __import__("ships." + pcScript)
	ShipScript.LoadModel ()
	return ShipScript.GetShipStats ()

def GetScriptFromSpecies (iSpecies):
	if (iSpecies <= 0 or iSpecies >= MAX_SHIPS):
		return None

	pSpecTuple = kSpeciesTuple [iSpecies]
	return pSpecTuple [0]

# This function is called from code to fill in the spec of
# an object that has been serialized over the net.
def InitObject (self, iType):
	kStats = GetShipFromSpecies (iType)
	if (kStats == None):
		# Failed.  Unknown type. Bail.
		return 0

	# Now that we have the stats, initialize the objects.
	# Initialize the ship's model.
	self.SetupModel (kStats['Name'])

	# Load hardpoints.
	pPropertySet = self.GetPropertySet()
	mod = __import__("ships.Hardpoints." + kStats['HardpointFile'])

	App.g_kModelPropertyManager.ClearLocalTemplates ()
	reload (mod)

	mod.LoadPropertySet(pPropertySet)

	self.SetupProperties()

	self.UpdateNodeOnly()

	return 1

def CreateShip (iType):
	# Get ship stats
	kStats = GetShipFromSpecies (iType)

	if (kStats == None):
		# Failed.  Unknown type. Bail.
		return None

#	print ("Creating " + kStats['Name'] + "\n")
	pShip = App.ShipClass_Create (kStats['Name'])

	sModule = "ships." + kSpeciesTuple [iType][0]
#	print ("*** Setting script module " + sModule)
	pShip.SetScript(sModule)

	# Load hardpoints.
	pPropertySet = pShip.GetPropertySet()
	mod = __import__("ships.Hardpoints." + kStats['HardpointFile'])

	App.g_kModelPropertyManager.ClearLocalTemplates ()
	reload(mod)

	mod.LoadPropertySet(pPropertySet)

	pShip.SetupProperties()

	pShip.UpdateNodeOnly()

	pShip.SetNetType (iType)

	return pShip

def GetNameFromSpecies (iSpecies):
	pSpecTuple = kSpeciesTuple [iSpecies]
	pcName = pSpecTuple [0]

	return pcName

def GetIconNum (iSpecies):
	pSpecTuple = kSpeciesTuple [iSpecies]
	iNum = pSpecTuple [1]

	return iNum

def GetSideFromSpecies (iSpecies):
	pSpecTuple = kSpeciesTuple [iSpecies]
	pcSide = pSpecTuple [2]

	return pcSide

def GetClassFromSpecies (iSpecies):
	pSpecTuple = kSpeciesTuple [iSpecies]
	iClass = pSpecTuple [3]

	return iClass


import matplotlib.pyplot as plt
import itertools
from sklearn.cluster import KMeans
from sklearn import metrics
from time import time
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.datasets import load_digits
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit
from collections import defaultdict
import numpy as np

import modulation as modulate
import channel as chan

def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
                        n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5), scoreFunction="accuracy"):
    """
    Generate a simple plot of the test and training learning curve.

    Parameters
    ----------
    estimator : object type that implements the "fit" and "predict" methods
        An object of that type which is cloned for each validation.

    title : string
        Title for the chart.

    X : array-like, shape (n_samples, n_features)
        Training vector, where n_samples is the number of samples and
        n_features is the number of features.

    y : array-like, shape (n_samples) or (n_samples, n_features), optional
        Target relative to X for classification or regression;
        None for unsupervised learning.

    ylim : tuple, shape (ymin, ymax), optional
        Defines minimum and maximum yvalues plotted.

    cv : int, cross-validation generator or an iterable, optional
        Determines the cross-validation splitting strategy.
        Possible inputs for cv are:
          - None, to use the default 3-fold cross-validation,
          - integer, to specify the number of folds.
          - An object to be used as a cross-validation generator.
          - An iterable yielding train/test splits.

        For integer/None inputs, if ``y`` is binary or multiclass,
        :class:`StratifiedKFold` used. If the estimator is not a classifier
        or if ``y`` is neither binary nor multiclass, :class:`KFold` is used.

        Refer :ref:`User Guide <cross_validation>` for the various
        cross-validators that can be used here.

    n_jobs : integer, optional
        Number of jobs to run in parallel (default 1).
    """
    plt.figure()
    plt.title(title)
    if ylim is not None:
        plt.ylim(*ylim)
    plt.xlabel("Training examples")
    plt.ylabel("Score")
    train_sizes, train_scores, test_scores = learning_curve(
        estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes, scoring=scoreFunction)
    train_scores_mean = np.mean(train_scores, axis=1)
    train_scores_std = np.std(train_scores, axis=1)
    test_scores_mean = np.mean(test_scores, axis=1)
    test_scores_std = np.std(test_scores, axis=1)
    plt.grid()

    print(train_scores_mean)

    print(test_scores_mean)

    plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
                     train_scores_mean + train_scores_std, alpha=0.1,
                     color="r")
    plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
                     test_scores_mean + test_scores_std, alpha=0.1, color="g")
    plt.plot(train_sizes, train_scores_mean, 'o-', color="r",
             label="Training score")
    plt.plot(train_sizes, test_scores_mean, 'o-', color="g",
             label="Cross-validation score")

    plt.legend(loc="best")
    return plt

def genKSignals(K, mode, size, snr):
    X = np.zeros((K, size),dtype=np.complex)
    for i in range(0, K):
        V = modulate.genmodsig(mode, size)
        X[i] = chan.awgn(V, snr)
    return X

def genYTargets(sampleSize, nSignals):
    Y = np.concatenate([np.zeros(sampleSize, dtype=np.int)+k for k in range(0,nSignals)])
    return Y

def createDataset(modulationMethodList, burstSize, sampleSize, snrRange):
    snrToDatasetList = defaultdict(list)
    snrToMatrixDataset = {}

    for snr in snrRange:
        for mode in modulationMethodList:
            snrToDatasetList[snr].append(genKSignals(sampleSize, mode, burstSize, snr))
        snrToMatrixDataset[snr] = np.concatenate(snrToDatasetList[snr])

    del snrToDatasetList
    return snrToMatrixDataset


#!/usr/bin/env python
"""
Global Average Plot
===================

Using cartopy_ we can easily visualize gridded model output on maps with
different cartographic projections, and then configure them with any
aesthetics or features such as continents, geopolitical borders, gridlines,
and more.

.. _cartopy: http://scitools.org.uk/cartopy/docs/latest/
"""
# Author: Daniel Rothenberg
# Version: June 2, 2017

import matplotlib.pyplot as plt
plt.style.use(['seaborn-talk', 'seaborn-ticks'])

import xbpch

# First we read in a sample dataset containing GEOS-Chem output
ds = xbpch.open_bpchdataset(
    "/Users/daniel/workspace/bpch/test_data/ND49_20060101_ref_e2006_m2010.bpch",
    diaginfo_file="/Users/daniel/Desktop/sample_nd49/diaginfo.dat",
    tracerinfo_file="/Users/daniel/Desktop/sample_nd49/tracerinfo.dat",
    dask=False, memmap=True
)

# This dataset contains multiple timesteps of multiple tracers, but we can
# extract just ozone, and average over the timesteps to get a slice to plot.
avg_o3 = ds['IJ_AVG_S_O3'].mean('time')

################################################
# To create a cartopy plot, we have to pass a **projection** to our matplotlib
# axis. This contains the utility/logic to transform data from the coordinate
# system we give to matplotlib to the actual cartographic/geographic system.
#
# Now, when we plot on this axis, we just need to tell matplotlib how to transform
# our data to its coordinate system. We do this by passing a **transform** object.
# No matter what projection you use, if you're plotting rectangular lat/lon data
# from GEOS-Chem or another model, you should use the ``PlateCarree()`` projection
# for this transform.
#
# For convenience, we'll use the wrapper that each ``DataArray`` has for
# matplotlib functions. This is just for convenience; we could always get the
# vector of lon/lat values and plot the data in **avg_o3** directly.

import cartopy.crs as ccrs
fig = plt.figure(figsize=(8, 4))
ax = fig.add_subplot(111, projection=ccrs.PlateCarree())

avg_o3.plot.imshow(x='lon', y='lat', vmin=0, vmax=60., cmap='gist_stern',
                   ax=ax, transform=ccrs.PlateCarree())
ax.coastlines()
plt.show()


import logging

import pytorch_lightning as pl
import torch
import torchmetrics
from torch.nn import functional as F

from nevermore.data import NYUv2Dataset
from nevermore.metric import Abs_CosineSimilarity
from nevermore.model.network import SingleSegNet

__all__ = ['SingleSegnetNYUv2Model']

logger = logging.getLogger(__name__)

class SingleSegnetNYUv2Model(pl.LightningModule):

    def __init__(self, learning_rate, task):
        super().__init__()
        self.save_hyperparameters()

        allowed_task = ("segmentation", "depth", "normal")
        if task not in allowed_task:
            raise ValueError(
                f"Expected argument `tsak` to be one of "
                f"{allowed_task} but got {task}"
            )
        task_output_channels = dict(
            segmentation=len(NYUv2Dataset.CLASSES),
            depth=1,
            normal=3,
        )

        self.task = task
        self.segnet = SingleSegNet(
            input_channels=3,
            output_channels=task_output_channels[task],
        )

        self.miou = torchmetrics.IoU(
            num_classes=len(NYUv2Dataset.CLASSES), ignore_index=0
        )
        self.rmse = torchmetrics.MeanSquaredError(squared=False)
        self.cos = Abs_CosineSimilarity(reduction='abs')

    def forward(self, x):
        return self.segnet.forward(x)

    def training_step(self, batch, batch_idx):
        x = batch['image']
        y_hat, y_hat_softmax = self(x)

        if self.task == 'segmentation':
            y_seg = batch['mask']
            loss_seg = F.cross_entropy(y_hat_softmax, y_seg)
            loss = loss_seg
            self.log('train_loss_seg', loss, prog_bar=True)

        if self.task == 'depth':
            y_dep = batch['depth']
            y_hat = y_hat.squeeze()
            loss_dep = F.mse_loss(y_hat, y_dep)
            loss = loss_dep
            self.log('train_loss_dep', loss, prog_bar=True)

        if self.task == 'normal':
            y_nor = batch['normal'].flatten(start_dim=1)
            y_hat = y_hat.flatten(start_dim=1)
            loss_nor = torch.mean(F.cosine_similarity(y_hat, y_nor))
            loss = loss_nor
            self.log('train_loss_nor', loss, prog_bar=True)

        return loss

    def training_epoch_end(self, training_step_outputs):
        print(self.trainer.lr_schedulers[0]['scheduler'].get_lr())
        for out in training_step_outputs:
            pass

    def validation_step(self, batch, batch_idx):
        x = batch['image']
        y_hat, y_hat_softmax = self(x)

        if self.task == 'segmentation':
            y_seg = batch['mask']
            loss_seg = F.cross_entropy(y_hat_softmax, y_seg)
            self.log('val_seg_loss', loss_seg)
            self.log('val_seg_iou_step', self.miou(y_hat_softmax, y_seg))

        if self.task == 'depth':
            y_dep = batch['depth']
            y_hat = y_hat.squeeze()
            loss_dep = F.mse_loss(y_hat, y_dep)
            self.log('val_dep_loss', loss_dep)
            self.log('val_dep_rmse_step', self.rmse(y_hat, y_dep))

        if self.task == 'normal':
            y_nor = batch['normal'].flatten(start_dim=1)
            y_hat = y_hat.flatten(start_dim=1)
            loss_nor = torch.mean(F.cosine_similarity(y_hat, y_nor))
            self.log('val_nor_loss', loss_nor)
            self.log('val_dep_cos_step', self.cos(y_hat, y_nor))

    def validation_epoch_end(self, validation_step_outputs):

        if self.task == 'segmentation':
            val_miou = self.miou.compute()
            self.log('val_seg_iou', val_miou)
            logger.info("val_seg_iou:" + str(val_miou.item()))
            self.miou.reset()

        if self.task == 'depth':
            val_rmse = self.rmse.compute()
            self.log('val_dep_mse', val_rmse)
            logger.info("val_dep_mse:" + str(val_rmse.item()))
            self.rmse.reset()

        if self.task == 'normal':
            val_cos = self.cos.compute()
            self.log('val_nor_cos', val_cos)
            logger.info("val_nor_cos:" + str(val_cos.item()))
            self.cos.reset()

    def configure_optimizers(self):
        optimizer = torch.optim.Adam(
            self.segnet.parameters(), lr=self.hparams.learning_rate
        )
        lr_schedule = torch.optim.lr_scheduler.StepLR(
            optimizer, step_size=462, gamma=0.2
        )
        optim_dict = {'optimizer': optimizer, 'lr_scheduler': lr_schedule}
        return optim_dict


class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        a = self.convert_to_integer(num1)
        #print(a)
        b = self.convert_to_integer(num2)
        #print(b)
        c = a * b
        #print(c)
        #return self.convert_to_str(c)
        return str(c)

    def convert_to_integer(self, a: str):
        b = 0
        n = len(a)
        for i in a :
            n -= 1
            b += int(i) * (10**(n))
        return b

    def convert_to_str(self, a):
        b = ''
        count = [i for i in range(0,6)]
        count.reverse()
        start = 0
        for i in count :
            if a > 10**(i):
                index = a // 10**(i)
                print(index)
                a -= index *(10 ** (i))
                print(a)
                b += str(index)
            else:
                if len(b)>1:
                    b += str(0)

        return b


class Solution(object):
    def compress(self, chars):
        result = []
        counter = 0
        last_char = chars[0]
        for c in chars:
            if c == last_char:
                counter += 1
            else:
                result.append(last_char)
                if counter > 1:
                    result.append(counter)
                counter = 0
                last_char = c
        result.append(last_char)
        if counter > 1:
            result.append(counter)
        return result

print(Solution().compress(['a', 'a', 'b', 'c', 'c', 'c']))
# ['a', '2', 'b', 'c', '3']


# Sada Mehari, Dylan Leh

from math import pi
from cisc106_34 import *

def calculate_grade(lab_score,test_score):
    '''
    Calculate a student's grade.
    Parameters:
      lab_score (number)
      test_score (number)
    Returns:
      grade (number)
    '''
    # calculate and return the grade
    grade = (lab_score + test_score) / 200
    return grade

assertEqual(calculate_grade(47.3, 122.5), 0.849)
assertEqual(calculate_grade(42.5, 142.7), 0.926)
assertEqual(round(calculate_grade(46.6, 131.5), 2), 0.89)

print()
print('Problem 1')
assertEqual(calculate_grade(48.6, 138.2), .934)
assertEqual(calculate_grade(44.2, 133.2), .887)
assertEqual(calculate_grade(49.2, 150), .996)

print ()
print('Problem 2')
# Calculate the area of a trapezoid
def calculate_trapezoid_area(base_1, base_2, height):
    '''
    Calculate the area of a trapezoid.
    Parameters:
        base_1(number)
        base_2 (number)
        height (number)
    Returns:
        area (number)
    '''
    area = (base_1 + base_2) * height / 2
    return area

assertEqual(calculate_trapezoid_area(1, 3, 2), 4)
assertEqual(calculate_trapezoid_area(32.0, 0.0, 4.0), 64.0)
assertEqual(round(calculate_trapezoid_area(0.14, 15.07,21.20), 2), 161.23)
assertEqual(calculate_trapezoid_area(6, 10, 19), 152)
assertEqual(calculate_trapezoid_area(22.0, 38.0, 4.0), 120.0)
assertEqual(round(calculate_trapezoid_area(0.18, 14.07,20.20), 2), 143.92)

print()
print('Problem 3')

def calculate_cylinder_volume(radius, height):
    '''
    Calculate the volume of a cylinder.
    Parameters:
      radius (number)
      height (number)
    Returns:
      volume (number)
    '''
    volume = pi * radius ** 2 * height
    return volume

assertEqual(round(calculate_cylinder_volume(5, 5), 3), 392.699)
assertEqual(round(calculate_cylinder_volume(13, 22), 2), 11680.44)
assertEqual(round(calculate_cylinder_volume(4.5, 9.2), 2), 585.28)

print()
print('Problem 4')

def make_string_strata(string1, string2):
    '''
    Creates a new string out of two given strings.
    Parameters:
      string1 (string)
      string2 (string)
    Returns:
      new_string (string)
    '''
    new_string = '{}|{}|{}'.format(string1,string2,string1)
    return new_string

assertEqual(make_string_strata("bbb","a"), "bbb|a|bbb")

print()
print('Problem 5')

def bill_amount(MB, base):
    '''
    Calculate a monthly data bill.
    Parameters:
      base (number)
      MB (number)
    Returns:
      bill (number)
    '''
    if MB <= 500 :
        bill = base
    elif MB > 500 and MB <= 2500 :
        bill = base * 1.25 + (MB-500) * .01
    elif MB > 2500 and MB <= 12500 :
        bill = (base * 3.75) + ((MB-2500) * .025)
    elif MB > 12500 :
        bill = 30 * base
    return bill

assertEqual(bill_amount(145, 10), 10)
assertEqual(round(bill_amount(920.8, 10.0), 2), 16.71)
assertEqual(round(bill_amount(8607, 10.0), 2), 190.18)
assertEqual(bill_amount(15025, 10), 300)

print()
print('Problem 6')

'''
    Convert seconds to days.
    Parameters:
        seconds (number)
'''

def time_calculator(seconds):
#86400 is the number of seconds in a day. if days (see below) comes out to 2.465
#then there are a total of 2 days with a remainder of 0.465. We will use the 465
#in calculating the number of hours, and the remainder in that to minutes,
#and so on.

    days = int(seconds / 86400)
    time_remaining_after_days = seconds % 86400

    hours = int(time_remaining_after_days / 3600) #3600 seconds in an hour
    time_remaining_after_hours = time_remaining_after_days % 3600

    minutes = int(time_remaining_after_hours/60) #60 seconds in a minute
    time_remaining_after_minutes = time_remaining_after_hours % 60

    print('{} seconds equals {} day(s), {} hour(s), {} minute(s), and {} \
second(s).'.format(seconds, days, hours, minutes, time_remaining_after_minutes))

number_of_seconds = int(input('How many seconds would you like to convert: '))
time_calculator(number_of_seconds)

print()
print('Problem 7')

'''
    Swaps the values of the 10th and hundredth placeholders of a number.
    Parameters:
        number (number)
'''

def swap_2_of_3(number):
    number_string = str(number) # converts the given number to string
    length = len(number_string) # determines the length of the string

    if length >= 3: # if there is a hundredth num., find it. else, make it
        hundred = number_string[-3]# [-3] finds the 3rd item from the end in a
    else:                           # list, or in this case, string/number
        hundred = '0'

    if length >= 2: # if there is a 10th num., find it. else, make it
        tens = number_string[-2]
    else:
        tens = '0'

    if length > 2:  #Saves all parts of number before the hundredth place value
        pre_swap = number_string[:(length - 3)]
    else:
        pre_swap = ''

    return int((pre_swap + tens + hundred + number_string[-1:]))
                                            # ^ the ones digit of the number
assertEqual(swap_2_of_3(326), 236)
assertEqual(swap_2_of_3(930), 390)
assertEqual(swap_2_of_3(20), 200)
assertEqual(swap_2_of_3(7), 7)
assertEqual(swap_2_of_3(500), 50)
assertEqual(swap_2_of_3(439), 349)
assertEqual(swap_2_of_3(218), 128)
assertEqual(swap_2_of_3(-326), -236)
assertEqual(swap_2_of_3(-543), -453)
print ()
print('Problem 8')

'''
    Determines if a loan should be approved for a person.
    Parameters:
      loan_amount (number)
      current_salary (number)
      current_cash (number)
      non_cash_assets (number)
      credit_score (number)
      last_name (string)
    Returns:
      mortgage_decision (string)
'''

def mortgage_approval(loan_amount, current_salary, current_cash, \
non_cash_assets, credit_score, last_name):
    mortgage_decision = ''

    if current_cash < loan_amount *.15:
        mortgage_decision = 'No'
    elif credit_score < 590:
        mortgage_decision = 'No'
    elif credit_score >= 590 and credit_score < 700:
            if current_cash <= loan_amount * .25:
                mortgage_decision = 'No'
            else:
                mortgage_decision = 'Maybe'
    elif current_salary < .333 * (loan_amount - current_cash):
         mortgage_decison = 'No'
    elif last_name == 'Doe' and non_cash_asset < 750000:
        mortgage_decision = 'No'
    elif current_cash < loan_amount:
        mortgage_decision = 'Maybe'
    else:
        mortgage_decision = 'Yes'

    return mortgage_decision

assertEqual(mortgage_approval(100, 200, 10, 500000, 500, 'Pie'), 'No')
assertEqual(mortgage_approval(2000, 1400, 2500, 900000, 580, 'Des'), 'No')
assertEqual(mortgage_approval(7000, 900, 5500, 450000, 650, 'Eye'), 'Maybe')
assertEqual(mortgage_approval(2000, 130, 1500, 30000, 600, 'Est'), 'Maybe')
assertEqual(mortgage_approval(9000, 8880, 1900, 740000, 630, 'Doe'), 'No')
assertEqual(mortgage_approval(1800, 3000, 1500, 500000, 720, 'Kan'), 'Maybe')
assertEqual(mortgage_approval(1100, 4400, 1900, 500000, 760, 'Elv'), 'Yes')
assertEqual(mortgage_approval(1000, 270, 250, 500000, 600, 'Lin'), 'No')
assertEqual(mortgage_approval(5000, 7000, 12000, 800000, 670, 'Ash'), 'Maybe')


from django.contrib import admin

from .models import Category, Question

class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
    ]

admin.site.register(Question, QuestionAdmin)
admin.site.register(Category)

# Register your models here.


"""
Tic Tac Toe Player
"""

import math
import copy

X = "X"
O = "O"
EMPTY = None

def initial_state():
    """
    Returns starting state of the board.
    """
    return [[EMPTY, EMPTY, EMPTY],
            [EMPTY, EMPTY, EMPTY],
            [EMPTY, EMPTY, EMPTY]]

def player(board):
    """
    Returns player who has the next turn on a board.
    """
    # Vairble to count how many Xs or Os are on the board
    counter = 0
    # Looping the board to see if each position is an X/O/Empty
    for column in board:
        for row in column:
            # Adding one to the counter if it is X/O
            if (row == "O") or (row == "X"):
                counter += 1
    # Checking whether counter is even or odd
    mod = counter % 2
    # If mod is even it is X's turn, or if it is odd then it is O's turn
    if mod == 0:
        return "X"
    else:
        return "O"

def actions(board):
    """
    Returns set of all possible actions (i, j) available on the board.
    """
    # An empty set to hold the actions
    actions = set()
    # Looping the board to see if each position is an X/O/Empty
    for column in range(3):
        for row in range(3):
            # Adding to actions if postion is empty
            if board[column][row] == None:
                actions.add((column, row))
    return actions

def result(board, action):
    """
    Returns the board that results from making move (i, j) on the board.
    """
    # Copying the board
    board_copy = copy.deepcopy(board)
    # Splitting the action into row and column
    i = int(action[1])
    j = int(action[0])
    # Applying the move to the board
    board_copy[j][i] = player(board_copy)
    return board_copy

def winner(board):
    """
    Returns the winner of the game, if there is one.
    """
    # A list of all the possible winning positions
    possible_wins = [[board[0][0], board[0][1], board[0][2]], [board[1][0], board[1][1], board[1][2]], [board[2][0], board[2][1], board[2][2]], [board[0][0], board[1][0], board[2][0]], [board[0][1], board[1][1], board[2][1]], [board[0][2], board[1][2], board[2][2]], [board[0][0], board[1][1], board[2][2]], [board[0][2], board[1][1], board[2][0]]]
    # Looping over each position to see if X or O won
    for win in possible_wins:
        if win[0] == "X" and win[1] == "X" and win[2] == "X":
            return "X"
        elif win[0] == "O" and win[1] == "O" and win[2] == "O":
            return "O"
    return None

def terminal(board):
    """
    Returns True if game is over, False otherwise.
    """
    # Checking for any winners
    if winner(board) != None:
        return True

    # Looping the board to see if each position is an X/O/Empty
    for column in board:
        for row in column:
            # If there are any empty spaces we know the game isn't terminal
            if (row == None):
                return False
    # If there are no empty spaces the game is terminal
    return True

def utility(board):
    """
    Returns 1 if X has won the game, -1 if O has won, 0 otherwise.
    """
    # Using winner to determine what utility should output
    if winner(board) == "X":
        return 1
    elif winner(board) == "O":
        return -1
    else:
        return 0

def minimax(board):
    """
    Returns the optimal action for the current player on the board.
    """
    # Making sure the game is't over
    if terminal(board) == True:
        return None
    # Vairble to hold the best action, and "neg/pos" iffinty vairbles
    optimal_action = ()

    min_v = 5
    max_v = -5
    # Looping over each action
    for action in actions(board):
        # If the player is O, then find the minimum value action
        if player(board) == "O":
            min_s = (max_value(result(board, action)))
            if min_s < min_v:
                min_v = min_s
                optimal_action = action
        # If the player is X, then find the maximum value action
        elif player(board) == "X":
            max_s = (min_value(result(board, action)))
            if max_s > max_v:
                max_v = max_s
                optimal_action = action

    return optimal_action

def max_value(board):
    # Checking if the state is terminal
    if terminal(board) == True:
        return utility(board)
    # Neg iffinity vairble
    v = -5
    # Recursivly calling min value to get the maximum choice
    for action in actions(board):
        v = max(v, min_value(result(board, action)))

    return v

def min_value(board):
    # Checking if the state is terminal
    if terminal(board) == True:
        return utility(board)
    # Pos iffinity
    v = 5
    # Recursivley calling the max value to get the minimum value
    for action in actions(board):
        v = min(v, max_value(result(board, action)))
    return v


'''
class Wizard:
  def __init__(self, health, mana, armor):
    self.health = health
    self.mana = mana
    self.armor = armor
  def attack(self):
    print('파이어볼')

x = Wizard(health = 545, mana = 210, armor = 10)
print(x.health, x.mana, x.armor)
x.attack()
'''

class Dragon:
  def __init__(self, health, fire, attack):
    self.health = health
    self.fire = fire
    self.attack = attack
  def finale(self):
    print("All dead")

x = Dragon(health = 2100, fire = 2300, attack = 210)
print(x.health, x.fire, x.attack)
x.finale()


from pony.orm import db_session

from model.Student import Student
from repository.repository_imp import CRUDRepositoryImp

class StudentRepository(CRUDRepositoryImp):
    def __init__(self):
        super().__init__(Student)

    @staticmethod
    @db_session
    def from_dict(student):
        args = student
        args['result'] = Student.get(id=args['result']['id'])
        return args

    @staticmethod
    def to_dict(student):
        d = student.to_dict()
        d['result'] = {'id': d['result']}
        del d['classtype']
        return d


#!/usr/bin/env python3
import argparse
from mlagents_envs.environment import UnityEnvironment
from mlagents_envs.side_channel.engine_configuration_channel import EngineConfigurationChannel
from gym_unity.envs import UnityToGymWrapper

from lib import model
from PIL import Image

import numpy as np
import torch

ENV_ID = "../../Robotic-RL-Env/Build/Robotic-RL-Env"

if __name__ == "__main__":
    """
    Dies ist das Programm mit dem das trainierte Modell gezeigt werden kann. Dem Skript muss über das Argument das Modell
    übergeben werden.
    """
    parser = argparse.ArgumentParser()
    parser.add_argument("-m", "--model", required=True, help="Model file to load")
    parser.add_argument("-e", "--env", default=ENV_ID, help="Environment name to use, default=" + ENV_ID)
    args = parser.parse_args()

    # Wrappen der Unity-Umgebung in eine Gym-Umgebung
    channel = EngineConfigurationChannel()
    unity_env = UnityEnvironment(ENV_ID, seed=1, side_channels=[channel])
    channel.set_configuration_parameters(time_scale=1.0)
    env = UnityToGymWrapper(unity_env)

    # Erstellen des Policy-Netzes mit den optimierten Gewichtungen aus dem Training
    net = model.ModelActor(env.observation_space.shape[0], env.action_space.shape[0])
    net.load_state_dict(torch.load(args.model))

    total_reward = 0.0
    total_steps = 0
    while True:
        # Für jeden Durchgang wird die Umgebung in einen definierten Startzustand gefahren.
        obs = env.reset()
        obs_v = torch.FloatTensor(obs)
        mu_v = net(obs_v)
        action = mu_v.squeeze(dim=0).data.numpy()
        action = np.clip(action, -1, 1)
        # Die berechnete Aktion wird folgend mit der .Step() Methode ausgeführt
        obs, reward, done, _ = env.step(action)
        total_reward += reward
        total_steps += 1

        # Zur Besseren Darstellung werden die Aktionen in Geschwindigkeit und Winkel umgerechnet
        action.data[0] = action.data[0] / 2 + 0.5
        action.data[1] = action.data[1] / 2 + 0.5
        action.data[0] = 10 + action.data[0] * 360
        action.data[1] = (action.data[1]*-150)
        print("The Target ist %.3f away and with the Actions %.3f and %.3f, we get %.3f Points" % (obs_v, action.data[0], action.data[1], reward))


from planes import *

def abled(b: bool):
    return "Enabled" if b else "Disabled"

def sorted_assignable_roles(guild: discord.Guild, filter_selfroles: bool = False, filter_autoroles: bool = False):
    if filter_selfroles:
        return [g for g in sorted_assignable_roles(guild) if g.id in zeph.server_settings[guild.id].selfroles]
    if filter_autoroles:
        return [g for g in sorted_assignable_roles(guild) if g.id in zeph.server_settings[guild.id].autoroles]
    return sorted([g for g in guild.roles[1:] if (not g.managed) and g < guild.me.top_role], reverse=True)

class PrefixNavigator(Navigator):
    def __init__(self, ctx: commands.Context):
        self.ctx = ctx
        super().__init__(config, self.prefixes, 8, "Custom Prefixes [{page}/{pgs}]", close_on_timeout=True)
        self.funcs[zeph.emojis["no"]] = self.close
        for g in range(self.per):
            self.funcs[f"remove {g+1}"] = partial(self.remove_prefix, g)

    @property
    def prefixes(self):
        return zeph.server_settings[self.ctx.guild.id].command_prefixes

    @property
    def pgs(self):
        return ceil(len(self.prefixes) / self.per)

    async def remove_prefix(self, n: int):
        try:
            prefix = page_list(self.prefixes, self.per, self.page)[n]
        except IndexError:  # if there aren't that many listed on the page, do nothing
            return

        if len(self.prefixes) == 1:
            await config.edit(self.message, "You can't remove the last prefix.")
            return await asyncio.sleep(2)
        else:
            self.prefixes.remove(prefix)
            await succ.edit(self.message, "Prefix removed.")
            if self.page > self.pgs:
                self.page -= 1
            return await asyncio.sleep(2)

    @property
    def con(self):
        return self.emol.con(
            self.title.format(page=self.page, pgs=self.pgs),
            d="\n".join(
                f"**`[{g + 1}]`** [`{j}`] - e.g. `{j}help`"
                for g, j in enumerate(page_list(self.prefixes, self.per, self.page))
            ) + "\n\nTo remove a prefix, say `remove <#>` in chat, e.g. `remove 1`. "
                "To completely reset all custom prefixes, exit this menu and use `z!sconfig prefixes reset`."
        )

    async def get_emoji(self, ctx: commands.Context):
        def pred(mr: MR, u: User):
            if isinstance(mr, discord.Message):
                return u == ctx.author and mr.channel == ctx.channel and mr.content in self.funcs.keys()
            else:
                return u == ctx.author and mr.message == self.message and mr.emoji in self.legal

        mess = (await zeph.wait_for('reaction_or_message', timeout=300, check=pred))[0]

        if isinstance(mess, discord.Message):
            try:
                await mess.delete()
            except discord.HTTPException:
                pass
            return mess.content
        else:
            return mess.emoji

    async def close(self):
        await self.emol.edit(self.message, "This menu has closed.")
        await self.remove_buttons()

class SelfRoleNavigator(Navigator):
    def __init__(self, roles: list, ctx: commands.Context, mode: str = "view"):
        """mode can be "self", "auto", "assign", or "view". the mode edits the list of selfroles, edits the list of
        autoroles, assigns selfroles to the user, or just browses the list, respectively."""
        super().__init__(config, roles, 8, "Selfroles List", close_on_timeout=True)
        self.mode = mode
        self.ctx = ctx
        self.user = ctx.author

        if self.mode == "self":
            self.title = "Editing Selfroles"
            self.prefix = f"To set or remove a role as self-assignable, just say the number. " \
                f"{zeph.emojis['checked']} indicates which roles are currently set as self-assignable. " \
                f"Hit {zeph.emojis['no']} when you're done.\n\n"
            if self.settings.autoroles:
                self.prefix += f"{zeph.emojis['checked_autorole']} indicates a role that's already set as " \
                    f"automatically assigned. You can still make this self-assignable if you want.\n\n"
        elif self.mode == "auto":
            self.title = "Editing Autoroles"
            self.prefix = f"To set or remove a role as automatically assigned, just say the number. " \
                f"{zeph.emojis['checked']} indicates which roles are currently set as automatically assigned. " \
                f"Hit {zeph.emojis['no']} when you're done.\n\n"
            if self.settings.selfroles:
                self.prefix += f"{zeph.emojis['checked_selfrole']} indicates a role that's already set as " \
                    f"self-assignable. You can still make this automatically assigned if you want.\n\n"
        elif self.mode == "assign":
            self.title = "Self-Assignable Roles"
            self.prefix = f"To assign or remove a role from yourself, just say the number. " \
                f"{zeph.emojis['checked']} indicates which roles you currently have. " \
                f"Hit {zeph.emojis['no']} when you're done.\n\n"
        else:
            self.title = "Self-Assignable Roles"

        self.nativity = Nativity(ctx, block_all=True)
        if self.mode != "view":
            zeph.nativities.append(self.nativity)
        self.funcs[zeph.emojis["no"]] = self.close
        for g in range(8):
            self.funcs[str(g + 1)] = partial(self.toggle_role, g)

    @property
    def settings(self):
        return zeph.server_settings[self.ctx.guild.id]

    @property
    def legal(self):  # overwriting this so it doesn't try to add all the numbers as reactions, delaying startup
        return [self.prev, self.next, zeph.emojis["no"]]

    async def toggle_role(self, n: int):
        role = self.roles_table[n]

        if self.mode == "self":
            if role.id in self.settings.selfroles:
                self.settings.selfroles.remove(role.id)
            else:
                self.settings.selfroles.append(role.id)

        elif self.mode == "auto":
            if role.id in self.settings.autoroles:
                self.settings.autoroles.remove(role.id)
            else:
                self.settings.autoroles.append(role.id)

        elif self.mode == "assign":
            try:
                if role in self.user.roles:
                    await self.user.remove_roles(role, reason="removed via z!selfrole")
                else:
                    await self.user.add_roles(role, reason="added via z!selfrole")
                await asyncio.sleep(0.25)  # to make sure the roles get updated before the menu does
            except discord.Forbidden:  # this shouldn't happen ideally
                raise commands.CommandError("Something went wrong. I can't edit that role.")

    def check(self, role: discord.Role):
        if self.mode == "self":
            if role.id in self.settings.autoroles and role.id not in self.settings.selfroles:
                return zeph.emojis["checked_autorole"]
            else:
                return checked(role.id in self.settings.selfroles)
        elif self.mode == "auto":
            if role.id in self.settings.selfroles and role.id not in self.settings.autoroles:
                return zeph.emojis["checked_selfrole"]
            else:
                return checked(role.id in self.settings.autoroles)
        elif self.mode == "assign":
            return checked(role in self.user.roles)

    @property
    def roles_table(self):
        return page_list(self.table, self.per, self.page)

    @property
    def mentions_table(self):
        return "\n".join(
            f"**`{n}.`** {self.check(g)} {g.mention}" if self.mode != "view" else f"- {g.mention}"
            for n, g in enumerate(self.roles_table, 1)
        )

    @property
    def con(self):
        return self.emol.con(f"{self.title} [{self.page}/{self.pgs}]", d=self.prefix + self.mentions_table)

    async def close(self):
        zeph.nativities.remove(self.nativity)
        await self.emol.edit(self.message, "This menu has closed.")
        await self.remove_buttons()

    async def get_emoji(self, ctx: commands.Context):
        def pred(mr: MR, u: User):
            if isinstance(mr, discord.Message):
                return can_int(mr.content) and int(mr.content) - 1 in range(len(self.roles_table)) and \
                    u == ctx.author and mr.channel == ctx.channel
            else:
                return u == ctx.author and mr.emoji in self.legal and mr.message.id == self.message.id

        mess = (await zeph.wait_for(
            'reaction_or_message', timeout=300, check=pred
        ))[0]

        if isinstance(mess, discord.Message):
            try:
                await mess.delete()
            except discord.HTTPException:
                pass
            return mess.content
        else:
            return mess.emoji

class AitchNavigator(Navigator):
    def __init__(self, ctx: commands.Context):
        super().__init__(config, ctx.guild.text_channels, 8, "Aitch Settings", close_on_timeout=True)
        self.ctx = ctx
        self.prefix = f"To toggle {zeph.emojis['aitch']} on or off in a channel, just say the number. " \
            f"{zeph.emojis['checked']} indicates which channels it is currently enabled in. " \
            f"Hit {zeph.emojis['no']} when you're done.\n\n"

        self.nativity = Nativity(ctx, block_all=True)
        zeph.nativities.append(self.nativity)
        self.funcs[zeph.emojis["no"]] = self.close
        for g in range(8):
            self.funcs[str(g + 1)] = partial(self.toggle_channel, g)

    async def toggle_channel(self, n: int):
        channel = self.channels_table[n]

        if channel.id in self.settings.h_exceptions:
            self.settings.h_exceptions.remove(channel.id)
        else:
            self.settings.h_exceptions.append(channel.id)

    @property
    def settings(self):
        return zeph.server_settings[self.ctx.guild.id]

    def check(self, channel: discord.TextChannel):
        # should show whether or not it's enabled in that channel
        return checked(self.settings.h_default ^ (channel.id in self.settings.h_exceptions))

    @property
    def channels_table(self):
        return page_list(self.table, self.per, self.page)

    @property
    def mentions_table(self):
        return "\n".join(f"**`{n}.`** {self.check(g)} {g.mention}" for n, g in enumerate(self.channels_table, 1))

    @property
    def con(self):
        return self.emol.con(f"{self.title} [{self.page}/{self.pgs}]", d=self.prefix + self.mentions_table)

    async def close(self):
        zeph.nativities.remove(self.nativity)
        await self.emol.edit(self.message, "This menu has closed.")
        await self.remove_buttons()

    async def get_emoji(self, ctx: commands.Context):
        def pred(mr: MR, u: User):
            if isinstance(mr, discord.Message):
                return can_int(mr.content) and int(mr.content) - 1 in range(len(self.channels_table)) and \
                    u == ctx.author and mr.channel == ctx.channel
            else:
                return u == ctx.author and mr.emoji in self.legal and mr.message.id == self.message.id

        mess = (await zeph.wait_for(
            'reaction_or_message', timeout=300, check=pred
        ))[0]

        if isinstance(mess, discord.Message):
            try:
                await mess.delete()
            except discord.HTTPException:
                pass
            return mess.content
        else:
            return mess.emoji

def load_server_settings():
    zeph.server_settings.clear()
    with open("storage/server_settings.json", "r") as f:
        json_dict = json.load(f)

    for g, j in json_dict.items():
        if zeph.get_guild(int(g)):
            zeph.server_settings[int(g)] = ServerSettings(**j)

    for server in zeph.guilds:
        if server.id not in zeph.server_settings:
            zeph.server_settings[server.id] = ServerSettings.null()

class ServerSettings:
    def __init__(self, **kwargs):
        self.welcome_channel = zeph.get_channel(kwargs.pop("welcome_channel", None))
        self.notify_join_enabled = kwargs.pop("notify_join", False)
        self.notify_leave_enabled = kwargs.pop("notify_leave", False)
        self.notify_ban_enabled = kwargs.pop("notify_ban", False)
        self.notify_unban_enabled = kwargs.pop("notify_unban", False)
        self.welcome_message = kwargs.pop("welcome_message", None)

        self.command_prefixes = kwargs.pop("command_prefixes", ["z!"])

        self.autoroles = kwargs.pop("autoroles", [])
        self.autorole_bots = kwargs.pop("autorole_bots", False)
        self.selfroles = kwargs.pop("selfroles", [])

        self.h_default = kwargs.pop("h_default", True)
        self.h_exceptions = kwargs.pop("h_exceptions", [])

        self.sampa = kwargs.pop("sampa", True)

        self.public_emotes = kwargs.pop("public_emotes", True)

    @property
    def notify_join(self):
        return (self.welcome_channel is not None) and self.notify_join_enabled

    @property
    def notify_leave(self):
        return (self.welcome_channel is not None) and self.notify_leave_enabled

    @property
    def notify_ban(self):
        return (self.welcome_channel is not None) and self.notify_ban_enabled

    @property
    def notify_unban(self):
        return (self.welcome_channel is not None) and self.notify_unban_enabled

    @staticmethod
    def null():
        return ServerSettings()

    @property
    def full_json(self):
        return {
            "welcome_channel": self.welcome_channel.id if self.welcome_channel else None,
            "notify_join": self.notify_join_enabled,
            "notify_leave": self.notify_leave_enabled,
            "notify_ban": self.notify_ban_enabled,
            "notify_unban": self.notify_unban_enabled,
            "welcome_message": self.welcome_message,
            "command_prefixes": self.command_prefixes,
            "selfroles": self.selfroles,
            "autoroles": self.autoroles,
            "autorole_bots": self.autorole_bots,
            "h_default": self.h_default,
            "h_exceptions": self.h_exceptions,
            "sampa": self.sampa,
            "public_emotes": self.public_emotes,
        }

    @property
    def minimal_json(self):  # only values which are not the default option
        return {g: j for g, j in self.full_json.items() if self.full_json[g] != ServerSettings.null().full_json[g]}

    @property
    def welcome_fields(self):
        return {
            "Channel": self.welcome_channel.mention if self.welcome_channel else "None",
            "Join Notifications": abled(self.notify_join_enabled),
            "Leave Notifications": abled(self.notify_leave_enabled),
            "Ban Notifications": abled(self.notify_ban_enabled),
            "Unban Notifications": abled(self.notify_unban_enabled)
        }

    def formatted_welcome(self, user: User):
        if not self.welcome_message:
            return None
        else:
            return self.welcome_message.format(name=user.name)

    def welcome_con(self, user: User, message: str, col: discord.Color, include_welcome: bool = False):
        return construct_embed(
            title=f"**{user.name}** {message}", d=self.formatted_welcome(user) if include_welcome else None,
            author=author_from_user(user), footer=f"ID: {user.id}", col=col
        )

    async def send_join(self, member: discord.Member):  # if this is changed, update the example in sc welcome message
        await self.welcome_channel.send(embed=self.welcome_con(member, "joined the server!", hexcol("22dd22"), True))

    async def send_leave(self, member: discord.Member):
        await self.welcome_channel.send(embed=self.welcome_con(member, "left the server.", hexcol("ff8800")))

    async def send_ban(self, member: discord.Member):
        await self.welcome_channel.send(embed=self.welcome_con(member, "has been banned.", hexcol("ff0000")))

    async def send_unban(self, member: discord.Member):
        await self.welcome_channel.send(embed=self.welcome_con(member, "has been unbanned.", hexcol("2244dd")))

    def can_h_in(self, channel: discord.TextChannel):
        return self.h_default ^ (channel.id in self.h_exceptions)

config = Emol(":gear:", hexcol("66757F"))

class SConfigInterpreter(Interpreter):
    redirects = {
        "prefix": "prefixes", "w": "welcome", "p": "prefixes", "selfrole": "selfroles", "sr": "selfroles",
        "autorole": "autoroles", "ar": "autoroles", "h": "help", "x": "sampa", "connie": "sampa", "e": "emotes",
        "emoji": "emotes", "emote": "emotes", "emojis": "emotes"
    }

    @property
    def settings(self):
        return zeph.server_settings[self.ctx.guild.id]

    async def _help(self, *args: str):
        help_dict = {
            "welcome": "Controls for welcome messages - notifications for when a user joins, leaves, is banned, or "
                       "is unbanned.\n\n"
                       "**`z!sc welcome`** shows the current settings, as well as all controls.\n\n"
                       "`z!sc welcome channel <channel>` sets the channel for welcome messages.\n\n"
                       "`z!sc welcome enable <join | leave | ban | unban | all>` enables types of notifications.\n\n"
                       "`z!sc welcome disable <join | leave | ban | unban | all>` disables types of notifications.\n\n"
                       "**`z!sc welcome message`** shows the current custom welcome message, and all controls.\n\n"
                       "`z!sc welcome message set <message...>` sets a new custom welcome message.\n\n"
                       "`z!sc welcome message remove` removes the current welcome message.",
            "prefixes": "Controls for custom command prefixes.\n\n"
                        "**`z!sc prefixes`** shows the currently enabled prefixes, and all controls.\n\n"
                        "`z!sc prefix add <prefix>` adds a new prefix.\n\n"
                        "`z!sc prefix remove <prefix>` removes a prefix.\n\n"
                        "`z!sc prefixes reset` removes all custom prefixes and resets to the default, `z!`.",
            "selfroles": "Controls for self-assignable roles.\n\n"
                         "**`z!sc selfroles`** shows detailed selfrole information, as well as all controls.\n\n"
                         "`z!sc selfroles edit` allows you to set and un-set specific roles as self-assignable.\n\n"
                         "`z!sc selfroles clear` turns off all selfroles.",
            "autoroles": "Controls for automatically-assigned roles.\n\n"
                         "**`z!sc autoroles`** shows detailed autorole information, as well as all controls.\n\n"
                         "`z!sc autoroles edit` allows you to set and un-set specific roles as auto-assigned.\n\n"
                         "`z!sc autoroles reassign` automatically reassigns all autoroles to all current members.\n\n"
                         "`z!sc autoroles clear` turns off all autoroles.\n\n"
                         "`z!sc autoroles bots <enable | disable>` sets whether to give autoroles to bots.",
            "aitch": f"Controls for Zeph's {zeph.emojis['aitch']} feature.\n\n"
                     f"**`z!sc aitch`** shows the current settings, as well as all controls.\n\n"
                     f"`z!sc aitch enable/disable all` enables or disables the function in all channels at once.\n\n"
                     f"`z!sc aitch channels` allows you to enable or disable the function in specific channels.",
            "sampa": "Controls for Zeph's X- and Z-SAMPA interpretation feature.\n\n"
                     "**`z!sc sampa`** shows the current settings, as well as all controls.\n\n"
                     "`z!sc sampa enable/disable` enables or disables the feature.",
            "emotes": "Privacy settings for the global use of emotes via `z!emote`.\n\n"
                      "**`z!sc emotes`** shows the current settings, as well as all controls.\n\n"
                      "`z!sc emotes public/private` sets the server's emotes to publicly available or private."
        }
        desc_dict = {
            "welcome": "Controls for welcome messages.",
            "prefixes": "Controls for custom command prefixes.",
            "selfroles": "Controls for self-assignable roles.",
            "autoroles": "Controls for automatically-assigned roles.",
            "aitch": f"Controls for Zeph's {zeph.emojis['aitch']} feature.",
            "sampa": "Controls for Zeph's X- and Z-SAMPA interpretation feature.",
            "emotes": "Controls for Zeph's global emote use feature."
        }

        if len(args) == 0 or (args[0].lower() not in help_dict and args[0].lower() not in self.redirects):
            return await config.send(
                self.ctx, "z!sconfig help",
                d="Available functions:\n\n" + "\n".join(f"`{g}` - {j}" for g, j in desc_dict.items()) +
                "\n\nFor information on how to use these, use `z!sconfig help <function>`.\n"
                "(more functions will be added in the future!)"
            )

        ret = self.redirects.get(args[0].lower(), args[0].lower())

        return await config.send(self.ctx, f"z!sconfig {ret}", d=help_dict[ret])

    async def _welcome(self, *args: str):
        if not args:
            return await config.send(
                self.ctx, "Welcome Message Options",
                d="To enable/disable specific types of notifications:\n`z!sconfig welcome enable/disable <x>`\n"
                  "To enable/disable all notifications at once:\n`z!sconfig welcome enable/disable all`\n"
                  "To change the channel in which notifications are sent:\n`z!sconfig welcome channel <channel>`\n"
                  "To view custom welcome message settings:\n`z!sconfig welcome message`" +
                  ("\n\n**No notifications will be sent until the welcome channel is set.**"
                   if not self.settings.welcome_channel else ""),
                fs=self.settings.welcome_fields,
                same_line=True
            )

        if args[0].lower() == "channel":
            if len(args) == 1:
                return await config.send(
                    self.ctx,
                    f"The welcome channel is currently set to #{self.settings.welcome_channel.name}."
                    if self.settings.welcome_channel else "The welcome channel is not set yet.",
                    d="To change this, use `z!sconfig welcome channel <new channel>`."
                )

            try:
                channel = await commands.TextChannelConverter().convert(self.ctx, args[1])
            except commands.BadArgument:
                raise commands.CommandError(f"Channel `{args[1]}` not found.")
            else:
                self.settings.welcome_channel = channel
                return await succ.send(
                    self.ctx, "Welcome channel set.", d=f"Traffic notifications will now be sent to {channel.mention}."
                )

        elif args[0].lower() == "enable":
            if len(args) == 1:
                raise commands.CommandError("Format: `z!sconfig welcome enable <type>`")

            warning = None if self.settings.welcome_channel else \
                "Notifications will not be sent until the welcome channel is set. " \
                "Use `z!sconfig welcome channel <channel>` to set one."

            if args[1].lower() == "all":
                self.settings.notify_join_enabled = True
                self.settings.notify_leave_enabled = True
                self.settings.notify_ban_enabled = True
                self.settings.notify_unban_enabled = True
                return await succ.send(self.ctx, "All traffic notifications **enabled**.", d=warning)

            elif args[1].lower() in ["join", "leave", "ban", "unban"]:
                if args[1].lower() == "join":
                    if self.settings.notify_join_enabled:
                        return await config.send(self.ctx, "Join notifications are already enabled.", d=warning)
                    self.settings.notify_join_enabled = True
                if args[1].lower() == "leave":
                    if self.settings.notify_leave_enabled:
                        return await config.send(self.ctx, "Leave notifications are already enabled.", d=warning)
                    self.settings.notify_leave_enabled = True
                if args[1].lower() == "ban":
                    if self.settings.notify_ban_enabled:
                        return await config.send(self.ctx, "Ban notifications are already enabled.", d=warning)
                    self.settings.notify_ban_enabled = True
                if args[1].lower() == "unban":
                    if self.settings.notify_unban_enabled:
                        return await config.send(self.ctx, "Unban notifications are already enabled.", d=warning)
                    self.settings.notify_unban_enabled = True

                return await succ.send(self.ctx, f"{args[1].title()} notifications **enabled**.", d=warning)

            else:
                raise commands.CommandError(f"Invalid argument `{args[1]}`.")

        elif args[0].lower() == "disable":
            if len(args) == 1:
                raise commands.CommandError("Format: `z!sconfig welcome disable <type>`")

            if args[1].lower() == "all":
                self.settings.notify_join_enabled = False
                self.settings.notify_leave_enabled = False
                self.settings.notify_ban_enabled = False
                self.settings.notify_unban_enabled = False
                return await succ.send(self.ctx, "All traffic notifications **disabled**.")

            elif args[1].lower() in ["join", "leave", "ban", "unban"]:
                if args[1].lower() == "join":
                    if not self.settings.notify_join_enabled:
                        return await config.send(self.ctx, "Join notifications are already disabled.")
                    self.settings.notify_join_enabled = False
                if args[1].lower() == "leave":
                    if not self.settings.notify_leave_enabled:
                        return await config.send(self.ctx, "Leave notifications are already disabled.")
                    self.settings.notify_leave_enabled = False
                if args[1].lower() == "ban":
                    if not self.settings.notify_ban_enabled:
                        return await config.send(self.ctx, "Ban notifications are already disabled.")
                    self.settings.notify_ban_enabled = False
                if args[1].lower() == "unban":
                    if not self.settings.notify_unban_enabled:
                        return await config.send(self.ctx, "Unban notifications are already disabled.")
                    self.settings.notify_unban_enabled = False

                return await succ.send(self.ctx, f"{args[1].title()} notifications **disabled**.")

            else:
                raise commands.CommandError(f"Invalid argument `{args[1]}`.")

        elif args[0].lower() == "message":
            if len(args) == 1:
                return await config.send(
                    self.ctx, "Welcome Message Settings",
                    d="The welcome message is an optional custom text that will be displayed alongside the "
                    "notification for a new member joining the server.\n\n"
                    "To set a new welcome message:\n`z!sconfig welcome message set <message...>`\n"
                    "To remove the welcome message:\n`z!sconfig welcome message remove`\n\n"
                    "When writing a welcome message, you can include the string **`{name}`**, and it will be replaced "
                    "with the user's name - `z!sc welcome message set Welcome, {name}!` will become \"Welcome, Fort!\"",
                    fs={"Current Welcome Message": self.settings.welcome_message}
                )

            if args[1].lower() == "set":
                message = " ".join(args[2:])

                if not message:
                    raise commands.CommandError("Cannot have an empty welcome message.")

                if len(message) > 1024:
                    raise commands.CommandError("The welcome message can't be more than 1024 characters long.")

                if await confirm(
                    "Set the welcome message?", self.ctx, self.au,
                    add_info="This will look like:\n\n" + message.format(name=self.au.name) + "\n\n"
                ):
                    self.settings.welcome_message = message
                    return await succ.send(self.ctx, "Welcome message set.")
                else:
                    return await config.send(self.ctx, "Welcome message settings were not changed.")

            elif args[1].lower() == "remove":
                if await confirm(
                    "Remove the welcome message?", self.ctx, self.au,
                    add_info="This will **not** disable the join notification; it will just reset it to the default."
                ):
                    self.settings.welcome_message = None
                    return await succ.send(self.ctx, "Welcome message removed.")
                else:
                    return await config.send(self.ctx, "Welcome message settings were not changed.")

            else:
                raise commands.CommandError(f"Invalid argument `{args[1]}`.")

        else:
            raise commands.CommandError(f"Invalid argument `{args[0]}`.")

    async def _prefixes(self, *args: str):
        if not args:
            prefix_preview = "\n".join(f"[`{g}`] - e.g. `{g}help`" for g in self.settings.command_prefixes[:5]) + \
                (f"\n...plus **{len(self.settings.command_prefixes) - 5}** more; see `z!sc prefixes list`"
                 if len(self.settings.command_prefixes) > 5 else "")
            return await config.send(
                self.ctx, "Custom Command Prefixes",
                d="To add a prefix:\n`z!sconfig prefix add \"<prefix>\"`\n"
                  "To view and disable prefixes:\n`z!sconfig prefixes list`\n"
                  "To reset to only the default prefix, `z!`:\n`z!sconfig prefixes reset`\n\n"
                  "Please enclose the **entire prefix** in \"double quotes\" when adding or removing, especially if "
                  "the prefix includes a trailing space.",
                fs={"Current Prefixes": prefix_preview}
            )

        if args[0].lower() == "add":
            if len(args) == 1:
                raise commands.CommandError("Format: `z!sconfig prefix add \"<prefix>\"`")

            if len(args) > 2:
                raise commands.CommandError("Please enclose the entire prefix in \"double quotes\".")

            prefix = args[1]

            if len(prefix) > 100:
                raise commands.CommandError("Prefixes cannot be more than 100 characters long.")

            warning = f"This will look like: **`{prefix}help`** or **`{prefix}sconfig prefixes reset`**.\n"

            if not prefix:
                raise commands.CommandError("Cannot have an empty prefix.")

            if re.match(r"\s", prefix):
                # discord removes leading whitespaces, so a prefix that starts with a space would be unusable
                raise commands.CommandError("Prefixes cannot begin with a whitespace.")

            if prefix in self.settings.command_prefixes:
                raise commands.CommandError("That prefix is already enabled.")

            for existing_prefix in self.settings.command_prefixes:
                if prefix in existing_prefix and existing_prefix.index(prefix) == 0:
                    warning += f"\n:rotating_light: **WARNING:** This will **overwrite and remove** the existing " \
                        f"prefix `{existing_prefix}`, which contains `{prefix}`.\n"

            if await confirm(f"Add the prefix `{prefix}`?", self.ctx, self.au, add_info=warning+"\n"):
                extra_info = ""
                for existing_prefix in self.settings.command_prefixes:
                    if prefix in existing_prefix and existing_prefix.index(prefix) == 0:
                        self.settings.command_prefixes.remove(existing_prefix)
                        extra_info += f"Prefix `{existing_prefix}` removed.\n"

                self.settings.command_prefixes.append(prefix)
                return await succ.send(self.ctx, f"Prefix `{prefix}` added.", d=extra_info)

            else:
                return await config.send(self.ctx, "Prefixes were not changed.")

        elif args[0].lower() in ["edit", "list"]:
            return await PrefixNavigator(self.ctx).run(self.ctx)

        elif args[0].lower() == "remove":
            if len(args) == 1:
                raise commands.CommandError("Format: `z!sconfig prefix remove \"<prefix>\"`")

            if len(args) > 2:
                raise commands.CommandError("Please enclose the entire prefix in \"double quotes\".")

            prefix = args[1]

            if prefix not in self.settings.command_prefixes:
                raise commands.CommandError(f"Prefix `{prefix}` not found.")

            if len(self.settings.command_prefixes) == 1:
                raise commands.CommandError("You can't remove the only enabled command prefix.")

            if await confirm(f"Remove the prefix `{prefix}`?", self.ctx, self.au):
                self.settings.command_prefixes.remove(prefix)
                return await succ.send(self.ctx, "Prefix removed.")

            else:
                return await config.send(self.ctx, "Prefixes were not changed.")

        elif args[0].lower() == "reset":
            warning = f"This will remove all custom prefixes" \
                f"{', leaving only' if 'z!' in self.settings.command_prefixes else ' and re-enable'} " \
                f"the default prefix `z!`."

            if await confirm("Reset custom prefixes?", self.ctx, self.au, add_info=warning):
                self.settings.command_prefixes.clear()
                self.settings.command_prefixes.append("z!")
                return await succ.send(self.ctx, "Prefixes reset.")

            else:
                return await config.send(self.ctx, "Prefixes were not changed.")

        else:
            raise commands.command(f"Invalid argument `{args[0]}`.")

    async def _selfroles(self, *args: str):
        if not args:
            return await config.send(
                self.ctx, "Selfrole Settings",
                d="Selfroles are roles that any server member can assign themselves using the `z!selfroles` menu.\n\n"
                  "To view and edit enabled selfroles:\n`z!sc selfroles edit`\n"
                  "To remove all selfroles:\n`z!sc selfroles clear`\n\n"
                  "Some roles may not show up in the list. In order for a role to be self-assignable, it must be "
                  "**lower than Zephyrus's top role** - otherwise, Zephyrus won't be able to assign it." +
                  (" :rotating_light: Currently, there are **no roles which fit this criteria**, so "
                   "**no roles can be set**." if not sorted_assignable_roles(self.ctx.guild) else "") +
                  ("\n\n:rotating_light: **NOTE:** Zephyrus needs the **Manage Roles** permission for selfrole "
                   "functionality." if not self.ctx.guild.me.guild_permissions.manage_roles else "")
            )

        if args[0].lower() == "edit":
            if not self.ctx.guild.me.guild_permissions.manage_roles:
                raise commands.CommandError("I need the **Manage Roles** permission to assign selfroles.")

            if not sorted_assignable_roles(self.ctx.guild):
                return await config.send(
                    self.ctx, "There are no roles that I can assign. See `z!sc selfroles` for more info."
                )

            return await SelfRoleNavigator(sorted_assignable_roles(self.ctx.guild), self.ctx, "self").run(self.ctx)

        elif args[0].lower() == "clear":
            if not zeph.server_settings[self.ctx.guild.id].selfroles:
                return await config.send(self.ctx, "There are no selfroles to clear.")

            if await confirm(
                f"Disable all {len(self.settings.selfroles)} selfroles?", self.ctx, self.au,
                add_info="This will *not* remove any roles from members who already have them, but will prevent "
                         "members from self-assigning these roles unless re-enabled.\n\n"
            ):
                self.settings.selfroles.clear()
                return await succ.send(self.ctx, "Selfroles cleared.")

            else:
                return await config.send(self.ctx, "Selfroles were not changed.")

        else:
            raise commands.CommandError(f"Invalid argument `{args[0]}`.")

    async def _autoroles(self, *args: str):
        if not args:
            return await config.send(
                self.ctx, "Autorole Settings",
                d="Autoroles are roles that are automatically assigned to all new members of the server.\n\n"
                  "To view and edit the list of autoroles:\n`z!sc autoroles edit`\n"
                  "To automatically re-assign all autoroles to each server member (use this if you add a new "
                  "autorole):\n`z!sc autoroles reassign`\n"
                  "To toggle whether to give autoroles to bots (disabled by default):\n"
                  "`z!sc autoroles bots enable/disable`\n"
                  "To clear all autoroles:\n`z!sc autoroles clear`\n\n"
                  "Some roles may not show up in the list. In order for a role to be automatically assigned, it must "
                  "be **lower than Zephyrus's top role** - otherwise, Zephyrus won't be able to assign it." +
                  (" :rotating_light: Currently, there are **no roles which fit this criteria**, so "
                   "**no roles can be set**." if not sorted_assignable_roles(self.ctx.guild) else "") +
                  ("\n\n:rotating_light: **NOTE:** Zephyrus needs the **Manage Roles** permission for autorole "
                   "functionality." if not self.ctx.guild.me.guild_permissions.manage_roles else ""),
                fs={"Give autoroles to bots?": abled(zeph.server_settings[self.ctx.guild.id].autorole_bots)}
            )

        elif args[0].lower() == "edit":
            if not self.ctx.guild.me.guild_permissions.manage_roles:
                raise commands.CommandError("I need the **Manage Roles** permission to assign autoroles.")

            if not sorted_assignable_roles(self.ctx.guild):
                return await config.send(
                    self.ctx, "There are no roles that I can assign. See `z!sc autoroles` for more info."
                )

            return await SelfRoleNavigator(sorted_assignable_roles(self.ctx.guild), self.ctx, "auto").run(self.ctx)

        elif args[0].lower() == "reassign":
            if not self.ctx.guild.me.guild_permissions.manage_roles:
                raise commands.CommandError("I need the **Manage Roles** permission to assign roles.")

            if not self.settings.autoroles:
                return await config.send(self.ctx, "There are no autoroles to assign.")

            def check(u: discord.Member):
                if not (set(self.settings.autoroles) <= {g.id for g in u.roles}):
                    if u.bot and not self.settings.autorole_bots:
                        return False
                    else:
                        return True
                return False

            users = list(filter(check, self.ctx.guild.members))

            if not users:
                return await succ.send("All members are currently up-to-date on autoroles.")

            mess = await Emol(zeph.emojis["loading"], hexcol("66757F"))\
                .send(self.ctx, f"Updating roles for {len(users)} members...")
            roles = [self.ctx.guild.get_role(g) for g in self.settings.autoroles]
            for user in users:
                await user.add_roles(*roles, reason="re-assigning autoroles")

            return await succ.edit(mess, "Autoroles re-assigned.")

        elif args[0].lower() == "bots":
            if len(args) == 1:
                return await config.send(
                    self.ctx,
                    f"Currently, bots {'**are**' if self.settings.autorole_bots else 'are **not**'} given autoroles.",
                    d=f"To change this, use `z!sc autoroles bots {abled(not self.settings.autorole_bots).lower()[:-1]}"
                      f"`. By default, bots are **not** given autoroles."
                )

            if args[1].lower() == "enable":
                if self.settings.autorole_bots:
                    return await config.send(self.ctx, "Bots are already given autoroles.")
                else:
                    self.settings.autorole_bots = True
                    return await succ.send(
                        self.ctx, "Bots will now be given autoroles.",
                        d="Use `z!sc autoroles reassign` to update all present bots with the autoroles, automatically."
                    )

            elif args[1].lower() == "disable":
                if not self.settings.autorole_bots:
                    return await config.send(self.ctx, "Bots are already not given autoroles.")
                else:
                    self.settings.autorole_bots = False
                    return await succ.send(self.ctx, "Bots will no longer be given autoroles.")

            else:
                raise commands.CommandError(f"Invalid argument `{args[1]}`.")

        elif args[0].lower() == "clear":
            if not zeph.server_settings[self.ctx.guild.id].autoroles:
                return await config.send(self.ctx, "There are no autoroles to clear.")

            if await confirm(
                f"Disable all {len(self.settings.autoroles)} autoroles?", self.ctx, self.au,
                add_info="This will *not* remove any roles from members who already have them, but will stop roles "
                         "from being automatically added to new members.\n\n"
            ):
                self.settings.autoroles.clear()
                return await succ.send(self.ctx, "Autoroles cleared.")

            else:
                return await config.send(self.ctx, "Autoroles were not changed.")

        else:
            raise commands.CommandError(f"Invalid argument `{args[0]}`.")

    async def _aitch(self, *args: str):
        if not args:
            exceptions = sorted([zeph.get_channel(g) for g in self.settings.h_exceptions], key=lambda c: c.position)
            if exceptions:
                embed_fs = {
                    "Default": f"{zeph.emojis['aitch']} is currently **{abled(self.settings.h_default).lower()}** in "
                    f"most channels, and will be initially {abled(self.settings.h_default).lower()} in new channels.",
                    "Exceptions": f"{zeph.emojis['aitch']} is currently **{abled(not self.settings.h_default).lower()}"
                    f"** in **{len(exceptions)} {plural('channel', len(exceptions))}:** "
                    f"{', '.join(g.mention for g in exceptions[:10])}"
                    + (f", and **{round(len(exceptions) - 10)}** more" if len(exceptions) > 10 else "")
                }
            else:
                embed_fs = {
                    "Default": f"{zeph.emojis['aitch']} is currently **{abled(self.settings.h_default).lower()}** in "
                    f"**all channels**, and will be initially {abled(self.settings.h_default).lower()} in new channels."
                }

            return await config.send(
                self.ctx, "Aitch Settings",
                d="Zephyrus will respond with " + zeph.strings["aitch"] +
                  " to messages consisting of solely `h`. This is on by default, but can be disabled.\n\n"
                  "To enable or disable across all channels at once:\n`z!sc aitch enable/disable`\n"
                  "To enable or disable on a channel-by-channel basis:\n`z!sc aitch channels`",
                fs=embed_fs
            )

        elif args[0].lower() == "enable":
            if self.settings.h_default is True:
                if self.settings.h_exceptions:
                    self.settings.h_exceptions.clear()
                    return await succ.send(self.ctx, f"{zeph.emojis['aitch']} enabled in all channels.")
                else:
                    return await config.send(self.ctx, f"{zeph.emojis['aitch']} is already enabled in all channels.")
            else:
                self.settings.h_default = True
                self.settings.h_exceptions.clear()
                return await succ.send(self.ctx, f"{zeph.emojis['aitch']} enabled in all channels.")

        elif args[0].lower() == "disable":
            if self.settings.h_default is False:
                if self.settings.h_exceptions:
                    self.settings.h_exceptions.clear()
                    return await succ.send(self.ctx, f"{zeph.emojis['aitch']} disabled in all channels.")
                else:
                    return await config.send(self.ctx, f"{zeph.emojis['aitch']} is already disabled in all channels.")
            else:
                self.settings.h_default = False
                self.settings.h_exceptions.clear()
                return await succ.send(self.ctx, f"{zeph.emojis['aitch']} disabled in all channels.")

        elif args[0].lower() in ["channel", "channels"]:
            return await AitchNavigator(self.ctx).run(self.ctx)

        else:
            raise commands.CommandError(f"Invalid argument `{args[0]}`.")

    async def _sampa(self, *args: str):
        if not args:
            return await config.send(
                self.ctx, "SAMPA Settings",
                d=f"In the style of the late Connie bot, Zephyrus will automatically interpret X-SAMPA text surrounded "
                  f"by x/slashes/ or x[brackets] preceded by a lowercase `x`, and Z-SAMPA text in z/slashes/ or "
                  f"z[brackets] preceded by a lowercase `z`. This is on by default, but can be disabled.\n\n"
                  f"To enable or disable:\n`z!sc sampa enable/disable`\n\n"
                  f"SAMPA interpretation is currently **{abled(self.settings.sampa).lower()}**."
            )

        elif args[0].lower() == "disable":
            if not self.settings.sampa:
                return await config.send(self.ctx, "SAMPA interpretation was already disabled.")
            else:
                self.settings.sampa = False
                return await succ.send(self.ctx, "SAMPA interpretation disabled.")

        elif args[0].lower() == "enable":
            if self.settings.sampa:
                return await config.send(self.ctx, "SAMPA interpretation was already enabled.")
            else:
                self.settings.sampa = True
                return await succ.send(self.ctx, "SAMPA interpretation enabled.")

        else:
            raise commands.CommandError(f"Invalid argument `{args[0]}`.")

    async def _emotes(self, *args: str):
        if not args:
            meaning = "they **can be accessed anywhere** through `z!e` and **are included** in `z!esearch` results" \
                if self.settings.public_emotes else \
                "they **cannot be accessed** through `z!e` and **are not included** in `z!esearch results`"
            return await config.send(
                self.ctx, "Emote Privacy Settings",
                d="Discord bots have the ability to use any custom emoji from any server of which they are a member, "
                  "in any other server. Zephyrus takes advantage of this feature using the commands `z!emoji` and "
                  "`z!react` to allow users to display or react with an emoji from a different server. If a server's "
                  "emotes are set to public, the emotes and their names may be seen through the use of `z!emoji` and "
                  "`z!esearch` in other servers. The server's name is not displayed.\n\n"
                  "Emotes are public by default, but may be made private at any time.\n\n"
                  "To change settings:\n`z!sc emotes public/private`\n\n"
                  f"This server's emotes are currently **{'public' if self.settings.public_emotes else 'private'}**, "
                  f"meaning {meaning}."
            )

        elif args[0].lower() == "public":
            if self.settings.public_emotes:
                return await config.send(self.ctx, "This server's emotes were already public.")
            else:
                self.settings.public_emotes = True
                return await succ.send(self.ctx, "This server's emotes are now accessible through `z!e`.")

        elif args[0].lower() == "private":
            if self.settings.public_emotes:
                self.settings.public_emotes = False
                return await succ.send(self.ctx, "This server's emotes are no longer accessible through `z!e`.")
            else:
                return await config.send(self.ctx, "This server's emotes were already private.")

@zeph.command(
    aliases=["sc"], usage="z!sconfig help",
    description="Server configuration options.",
    help="**Admin command.** Allows you to view and change various server configuration options, such as join/leave "
         "messages, custom prefixes, etc. Use `z!sconfig help` for more specific details."
)
async def sconfig(ctx: commands.Context, func: str = None, *args: str):
    if not ctx.guild:
        raise commands.CommandError("This command can only be run in a server.")

    if not ctx.author.guild_permissions.manage_guild:
        raise commands.CommandError("You don't have permission to do that.")

    if not func:
        return await config.send(
            ctx, "Server Configuration",
            d="This command allows you to edit Zephyrus's various server configuration options, like welcome messages, "
              "custom prefixes, etc. Use `z!sconfig help` for more info."
        )

    return await SConfigInterpreter(ctx).run(func, *args)

@zeph.command(
    name="selfroles", aliases=["sr", "selfrole", "roles"], usage="z!selfroles",
    description="Allows you to edit your self-assigned roles.",
    help="Allows you to browse the server's list of self-assigned roles, and assign them to or remove them from "
         "yourself via a menu."
)
async def selfroles_command(ctx: commands.Context):
    if not ctx.guild:
        raise commands.CommandError("This command can only be run in a server.")

    if not ctx.guild.me.guild_permissions.manage_roles:
        return await config.send(
            ctx, "Selfroles are not configured for this server.",
            d="If you're an admin, see `z!sc selfroles` for more info."
        )

    if not sorted_assignable_roles(ctx.guild, True):
        return await config.send(ctx, "This server has no self-assigned roles.")

    return await SelfRoleNavigator(sorted_assignable_roles(ctx.guild, True), ctx, "assign").run(ctx)