import sys

import re
import pandas as pd

from datetime import datetime, timedelta
import sqlite3
import time

MAPS_TLAS = { 'Overpass' : 'ovp',
'Cache' : 'cch',
'Nuke' : 'nuke',
'Train' : 'trn',
'Mirage' : 'mrg',
'Cobblestone' : 'cbl',
'Inferno' : 'inf',
'Dust2' : 'd2',
'Tuscan' : 'tcn',
'Season' : 'ssn'
}

tmpidx = ['MatchID',
  'MapName',
  'Event',
  'MatchDate',
  'GOTV_link',
  'TeamLeft',
  'TeamRight',
  'TeamLeftFirstKills',
  'TeamRightFirstKills',
  'TeamLeftClutches',
  'TeamRightClutches',
  'TeamLeftWinPistol',
  'TeamRightWinPistol',
  'TeamLeftTElims',
  'TeamRightTElims',
  'TeamLeftCTElims',
  'TeamRightCTElims',
  'TeamLeftBombDefusals',
  'TeamRightBombDefusals',
  'TeamLeftBombExploded',
  'TeamRightBombExploded',
  'TeamLeftTWins',
  'TeamRightTWins',
  'TeamLeftCTWins',
  'TeamRightCTWins',
  'TeamLeftTotalRounds',
  'TeamRightTotalRounds'
]

def getMonthPerformance(team, match_date):
    md = match_date.isoformat()[:10]
    md_1month = (match_date - timedelta(days = 31)).isoformat()[:10]
    md_3month = (match_date - timedelta(days = 30*3)).isoformat()[:10]
    md_6month = (match_date - timedelta(days = 30*6)).isoformat()[:10]

    maps_1m = c.execute("SELECT * FROM maps WHERE (TeamLeft = ? OR TeamRight = ?) AND MatchDate > ?  AND MatchDate < ?", (team, team, md_1month, md)).fetchall()

    if maps_1m == []:
        TRounds_1m = "NA"
        CTRounds_1m = "NA"
        TotalRounds_1m = "NA"
        Wins_1m = "NA"
        TRoundsRate_1m = "NA"
        CTRoundsRate_1m = "NA"
        TotalRoundsRate_1m = "NA"
        WinsRate_1m = "NA"
    else:
        TRounds_1m = 0
        CTRounds_1m = 0
        TotalRounds_1m = 0
        Wins_1m = 0
        count = 0
        for m in maps_1m:
            count += 1
            if m[idx['TeamLeft']] == team:
                TRounds_1m += m[idx['TeamLeftTWins']]
                CTRounds_1m += m[idx['TeamLeftCTWins']]
                TotalRounds_1m += m[idx['TeamLeftTotalRounds']]
                Wins_1m += (1 if (m[idx['TeamLeftTotalRounds']] >= 16) else 0)
            else:
                TRounds_1m += m[idx['TeamRightTWins']]
                CTRounds_1m += m[idx['TeamRightCTWins']]
                TotalRounds_1m += m[idx['TeamRightTotalRounds']]
                Wins_1m += (1 if (m[idx['TeamRightTotalRounds']] >= 16) else 0)
        TRoundsRate_1m = TRounds_1m / count
        CTRoundsRate_1m = CTRounds_1m / count
        TotalRoundsRate_1m = TotalRounds_1m / count
        WinsRate_1m = Wins_1m / count

    maps_3m = c.execute("SELECT * FROM maps WHERE (TeamLeft = ? OR TeamRight = ?) AND MatchDate > ?  AND MatchDate < ?", (team, team, md_3month, md)).fetchall()

    if maps_3m == []:
        TRounds_3m = "NA"
        CTRounds_3m = "NA"
        TotalRounds_3m = "NA"
        Wins_3m = "NA"
        TRoundsRate_3m = "NA"
        CTRoundsRate_3m = "NA"
        TotalRoundsRate_3m = "NA"
        WinsRate_3m = "NA"
    else:
        TRounds_3m = 0
        CTRounds_3m = 0
        TotalRounds_3m = 0
        Wins_3m = 0
        count = 0
        for m in maps_3m:
            count += 1
            if m[idx['TeamLeft']] == team:
                TRounds_3m += m[idx['TeamLeftTWins']]
                CTRounds_3m += m[idx['TeamLeftCTWins']]
                TotalRounds_3m += m[idx['TeamLeftTotalRounds']]
                Wins_3m += (1 if (m[idx['TeamLeftTotalRounds']] >= 16) else 0)
            else:
                TRounds_3m += m[idx['TeamRightTWins']]
                CTRounds_3m += m[idx['TeamRightCTWins']]
                TotalRounds_3m += m[idx['TeamRightTotalRounds']]
                Wins_3m += (1 if (m[idx['TeamRightTotalRounds']] >= 16) else 0)
        TRoundsRate_3m = TRounds_3m / count
        CTRoundsRate_3m = CTRounds_3m / count
        TotalRoundsRate_3m = TotalRounds_3m / count
        WinsRate_3m = Wins_3m / count

    maps_6m = c.execute("SELECT * FROM maps WHERE (TeamLeft = ? OR TeamRight = ?) AND MatchDate > ?  AND MatchDate < ?", (team, team, md_6month, md)).fetchall()

    if maps_6m == []:
        TRounds_6m = "NA"
        CTRounds_6m = "NA"
        TotalRounds_6m = "NA"
        Wins_6m = "NA"
        TRoundsRate_6m = "NA"
        CTRoundsRate_6m = "NA"
        TotalRoundsRate_6m = "NA"
        WinsRate_6m = "NA"
    else:
        TRounds_6m = 0
        CTRounds_6m = 0
        TotalRounds_6m = 0
        Wins_6m = 0
        count = 0
        for m in maps_6m:
            count += 1
            if m[idx['TeamLeft']] == team:
                TRounds_6m += m[idx['TeamLeftTWins']]
                CTRounds_6m += m[idx['TeamLeftCTWins']]
                TotalRounds_6m += m[idx['TeamLeftTotalRounds']]
                Wins_6m += (1 if (m[idx['TeamLeftTotalRounds']] >= 16) else 0)
            else:
                TRounds_6m += m[idx['TeamRightTWins']]
                CTRounds_6m += m[idx['TeamRightCTWins']]
                TotalRounds_6m += m[idx['TeamRightTotalRounds']]
                Wins_6m += (1 if (m[idx['TeamRightTotalRounds']] >= 16) else 0)
        TRoundsRate_6m = TRounds_6m / count
        CTRoundsRate_6m = CTRounds_6m / count
        TotalRoundsRate_6m = TotalRounds_6m / count
        WinsRate_6m = Wins_6m / count

    return {'TRounds_1m' : TRounds_1m, 'TRoundsRate_1m' : TRoundsRate_1m, \
        'CTRounds_1m' : CTRounds_1m, 'CTRoundsRate_1m' : CTRoundsRate_1m, \
        'TotalRounds_1m': TotalRounds_1m, 'TotalRoundsRate_1m':TotalRoundsRate_1m, \
        'Wins_1m' : Wins_1m, 'WinsRate_1m' : WinsRate_1m, \
        'TRounds_3m' :  TRounds_3m, 'TRoundsRate_3m': TRoundsRate_3m, \
        'CTRounds_3m' : CTRounds_3m, 'CTRoundsRate_3m' : CTRoundsRate_3m,\
        'TotalRounds_3m' : TotalRounds_3m, 'TotalRoundsRate_3m' : TotalRoundsRate_3m,\
        'Wins_3m' : Wins_3m, 'WinsRate_3m' : WinsRate_3m, \
        'TRounds_6m' : TRounds_6m, 'TRoundsRate_6m' : TRoundsRate_6m, \
        'CTRounds_6m' : CTRounds_6m, 'CTRoundsRate_6m' : CTRoundsRate_6m, \
        'TotalRounds_6m' : TotalRounds_6m, 'TotalRoundsRate_6m' : TotalRoundsRate_6m, \
        'Wins_6m' : Wins_6m, 'WinsRate_6m' : WinsRate_6m}

idx = {}

for i in tmpidx:
    idx[i] = tmpidx.index(i)

numRows = 0

dataMatrix = None

# team lifetime winrate on map
conn = sqlite3.connect('csgo.db')
c = conn.cursor()

maps = c.execute("SELECT * FROM maps")

for m in maps.fetchall():
    # map affinity

    print( m[idx['TeamLeftTotalRounds']] , m[idx['TeamRightTotalRounds']] )
    if m[idx['TeamLeftTotalRounds']] > m[idx['TeamRightTotalRounds']]:
        y = 1
    elif m[idx['TeamLeftTotalRounds']] < m[idx['TeamRightTotalRounds']]:
        y = -1
    else:
        y = 0
        assert m[idx['TeamLeftTotalRounds']] == m[idx['TeamRightTotalRounds']]

    mapname = m[idx['MapName']]
    teamLeft = m[idx['TeamLeft']]
    teamRight = m[idx['TeamRight']]
    matchDate = m[idx['MatchDate']]
    matchDateText = matchDate.split()[0]
    matchTime = int(matchDate.split()[1][:2])  #hour digits of date
    matchDate = datetime.strptime(matchDate.split()[0] , "%Y-%m-%d")

    # team rating
    teamLeftRating = c.execute("SELECT * FROM team_history WHERE Team_ID = ?", (teamLeft,))
    closestDate = None
    closestEntry = None
    for entry in teamLeftRating.fetchall():
        d = datetime.strptime(entry[1], "%Y-%m-%d")
        if matchDate - d > timedelta(days=0):
            if closestDate is None:
                closestDate = d
                closestEntry = entry
            elif matchDate - d < matchDate - closestDate:
                closestDate = d
                closestEntry = entry
    if closestDate is None:
        teamLeftRating = 1.0
        teamLeftKD = 1.0
        teamLeftDiff = 0
    else:
        teamLeftRating = closestEntry[5]
        teamLeftKD = closestEntry[4]
        teamLeftDiff = closestEntry[3]

    teamRightRating = c.execute("SELECT * FROM team_history WHERE Team_ID = ?", (teamRight,))
    closestDate = None
    closestEntry = None
    for entry in teamRightRating.fetchall():
        d = datetime.strptime(entry[1], "%Y-%m-%d")
        if matchDate - d > timedelta(days=0):
            if closestDate is None:
                closestDate = d
                closestEntry = entry
            elif matchDate - d < matchDate - closestDate:
                closestDate = d
                closestEntry = entry
    if closestDate is None:
        teamRightRating = 1.0
        teamRightKD = 1.0
        teamRightDiff = 0
    else:
        teamRightRating = closestEntry[5]
        teamRightKD = closestEntry[4]
        teamRightDiff = closestEntry[3]

    # is new team
    newTeam = c.execute("SELECT MatchDate FROM maps WHERE TeamLeft = ? OR TeamRight = ? ORDER BY MatchDate ASC LIMIT 1", (teamLeft, teamLeft))
    x = newTeam.fetchone()
    if x is None:
        newTeamLeft = True
    else:
        if datetime.strptime(x[0].split()[0] , "%Y-%m-%d") - matchDate > timedelta(days=60):
            newTeamLeft = False
        else:
            newTeamLeft = True

    newTeam = c.execute("SELECT MatchDate FROM maps WHERE TeamLeft = ? OR TeamRight = ? ORDER BY MatchDate ASC LIMIT 1", (teamRight, teamRight))
    x = newTeam.fetchone()
    if x is None:
        newTeamRight = True
    else:
        if datetime.strptime(x[0].split()[0] , "%Y-%m-%d") - matchDate > timedelta(days=60):
            newTeamRight = False
        else:
            newTeamRight = True

    # time since last match
    lastMatch = c.execute("SELECT MatchDate FROM maps WHERE MatchDate < ? and ( TeamLeft = ? OR TeamRight = ? ) ORDER BY MatchDate DESC LIMIT 1", (matchDateText, teamLeft, teamLeft))
    x = lastMatch.fetchone()
    if x is None:
        teamLeftLastMatch = "NA"
    else:
        teamLeftLastMatch = (matchDate - datetime.strptime(x[0].split()[0] , "%Y-%m-%d")).days

    lastMatch = c.execute("SELECT MatchDate FROM maps WHERE MatchDate < ? and (TeamLeft = ? OR TeamRight = ?) ORDER BY MatchDate DESC LIMIT 1", (matchDateText, teamRight, teamRight))
    x = lastMatch.fetchone()
    if x is None:
        teamRightLastMatch = "NA"
    else:
        teamRightLastMatch = (matchDate - datetime.strptime(x[0].split()[0] , "%Y-%m-%d")).days

    #matches this week
    matchDateMinusWeek = (matchDate - timedelta(days=7)).isoformat()[:10]
    teamLeftThisWeek = c.execute("SELECT COUNT(*) FROM maps WHERE (TeamLeft = ? OR TeamRight = ?) AND MatchDate > ? and  MatchDate < ?",  (teamLeft, teamLeft, matchDateMinusWeek, matchDate)   ).fetchone()[0]
    teamRightThisWeek = c.execute("SELECT COUNT(*) FROM maps WHERE (TeamLeft = ? OR TeamRight = ?) AND MatchDate > ? and MatchDate < ?",  (teamRight, teamRight, matchDateMinusWeek, matchDate)   ).fetchone()[0]

    # map affinity
    map_affinity_left = c.execute("SELECT Winrate FROM map_affinity WHERE Team_ID = ? AND MapName = ?" , ( teamLeft, mapname)).fetchone()
    if map_affinity_left is None:
        map_affinity_left = .5
    else:
        map_affinity_left = map_affinity_left[0]
    map_affinity_right = c.execute("SELECT Winrate FROM map_affinity WHERE Team_ID = ? AND MapName = ?" , ( teamRight, mapname)).fetchone()
    if map_affinity_right is None:
        map_affinity_right = .5
    else:
        map_affinity_right = map_affinity_right[0]

    #map count
    teamLeftMapCount = int(c.execute("SELECT COUNT(*) FROM maps WHERE TeamLeft = ?", (teamLeft,)).fetchone()[0] ) + \
        int(c.execute("SELECT count(*) FROM maps WHERE TeamRight = ?", (teamLeft,)).fetchone()[0] )

    teamRightMapCount = int(c.execute("SELECT COUNT(*) FROM maps WHERE TeamLeft = ?", (teamRight,)).fetchone()[0] ) + \
        int(c.execute("SELECT COUNT(*) FROM maps WHERE TeamRight = ?", (teamRight,)).fetchone()[0] )

    teamLeftMonthlyPerf = getMonthPerformance(teamLeft, matchDate)
    teamRightMonthlyPerf = getMonthPerformance(teamRight, matchDate)

    prflen = len(teamLeftMonthlyPerf)

#    print(mapname, teamLeft, teamRight, matchDateText, matchTime, teamLeftRating , \
#        teamRightRating, teamLeftKD, teamLeftDiff, newTeamLeft, newTeamRight, \
#        teamLeftLastMatch, teamRightLastMatch, teamLeftThisWeek, map_affinity_left, teamLeftMapCount)

    print(numRows)
    numRows += 1

    if dataMatrix is None:
        columns = ['mapname', 'matchTime', 'teamLeftRating', 'teamRightRating', 'teamLeftKD', 'teamRightKD', \
            'teamLeftDiff', 'teamRightDiff', 'newTeamLeft', 'newTeamRight', \
            'teamLeftLastMatch', 'teamRightLastMatch', 'teamLeftThisWeek', 'teamRightThisWeek', \
            'map_affinity_left', 'map_affinity_right']

        columns += ['teamLeft' + i for i in teamLeftMonthlyPerf]
        columns += ['teamRight' + i for i in teamRightMonthlyPerf]
        columns += ['winner']
        dataMatrix = []

    datarow = [mapname, matchTime,  teamLeftRating ,  teamRightRating ,  teamLeftKD ,  teamRightKD , \
         teamLeftDiff ,  teamRightDiff ,  newTeamLeft ,  newTeamRight , \
         teamLeftLastMatch ,  teamRightLastMatch ,  teamLeftThisWeek ,  teamRightThisWeek , \
         map_affinity_left ,  map_affinity_right]

    datarow += [teamLeftMonthlyPerf[i[8:]] for i in columns[16:16+prflen]]
    datarow += [teamRightMonthlyPerf[i[9:]] for i in columns[16+prflen:-1]]
    datarow += [y]
    dataRow = dict(zip(columns, datarow))
    dataMatrix.append(dataRow)

dataMatrix =pd.DataFrame(dataMatrix)

print(dataMatrix)
import pickle
pickle.dump(dataMatrix, open('dataMatrix', 'wb'), pickle.HIGHEST_PROTOCOL)


import pygame
from Game.Objects.Bullet.Bullet import Bullet
from Game.Core.Event.Dispatcher.EventDispatcher import EventDispatcher
from Utils.Constant.Event.EventEnum import EventEnum
from Utils.Constant.Event.SubEventEnum import SubEventEnum

class InstantBullet(Bullet):

    _size = 1

    def draw(self, screen):
        if self._state == self.NEW:
            pygame.draw.line(screen, (255, 255, 255), self.get_position(), self._target.get_center(), self._size)
            self._state = self.MOVING

    def update(self):
        if self._state == self.MOVING:
            EventDispatcher.dispatch(
                EventEnum.BULLET,
                SubEventEnum.ENEMY_HIT,
                {
                    'bullet': self,
                    'damage': self._damage,
                    'target': self._target,
                }
            )
            self._state = self.TO_REMOVE


import imapclient, pyzmail, re, gspread, datetime
from oauth2client.service_account import ServiceAccountCredentials

def get_googlesheets_client():

    scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive']
    credentials = ServiceAccountCredentials.from_json_keyfile_name('insert full path to credentials .json file', scope)
    return gspread.authorize(credentials)

def connect_email(emailAdd, emailPass):
    """
    connects to email and searches with criteria, pulls regexMessage, cleans it, and returns regexMessage for regex_email function
    """
    conn = imapclient.IMAPClient('imap.gmail.com', ssl = True)
    conn.login(emailAdd, emailPass)
    return conn

def fetch_emails(conn, searchCriteria, emailInbox):
    """
    searches designated inbox for all the UIDS that match the searchCriteria and returns a list of those UIDs. Also prints a count of the number of emails that matched.
    """
    conn.select_folder(emailInbox, readonly=False)
    UIDs = conn.gmail_search(searchCriteria, charset='UTF-8')
    if len(UIDs) > 0:
        print("Matching emails found: " + str(len(UIDs)))
        return UIDs

    else:
        print("!NO MATCHING EMAILS FOUND!")
        exit()

def check_transaction_IDs(conn, UIDs, IDs_for_regex, trans_ID_list, transaction_data, USD_contributions):
    """
    iterates through each email, grabs that emails transaction ID through Regex, then checks if that ID has been recorded. If it has not, it proceeds to regex the entire message according to our IDs_for_regex by calling regex_email(). along the way we mark each email as 'seen' and increment the number of unrecorded transaction IDs.
    """
    unrecorded_transaction_IDs = 0
    for i in range(0,len(UIDs)):
        email_trans_ID, regexMessage = grab_email_IDs(i, conn, UIDs, IDs_for_regex)

        if email_trans_ID: #if an email exists
            email_trans_ID = email_trans_ID.group(1)

            if email_trans_ID not in trans_ID_list: #cross reference each ID with the existing ID list.
                unrecorded_transaction_IDs += 1
                conn.set_flags(UIDs[i],'\Seen')
                print("UNIQUE")
                regex_email(IDs_for_regex, transaction_data, regexMessage, email_trans_ID, USD_contributions)
            else:
                print("RECORDED\n"+email_trans_ID)

    if unrecorded_transaction_IDs == 0: #exit the program if there are no unrecorded IDs
        print("ALL TRANSACTIONS RECORDED")
        exit()

    if len(transaction_data) == 0: #exit the program if unable to grab any transaction data, which indicates a regex error
        print("=====\nNO TRANSACTION DATA")
        exit()

def grab_email_IDs(i, conn, UIDs, IDs_for_regex):
    """
    searches each email by UID for its transaction ID. returns that ID to be compared with the list of existing IDs.
    """
    print("======")
    rawEmail = conn.fetch(UIDs[i],['BODY[]','FLAGS'])
    regexMessage = pyzmail.PyzMessage.factory(rawEmail[UIDs[i]][b'BODY[]'])
    regexMessage = str(regexMessage.text_part.get_payload().decode('UTF-8'))
    email_trans_ID = re.search(IDs_for_regex['TransID'],regexMessage)
    return email_trans_ID, regexMessage

def regex_email(IDs_for_regex, transaction_data, regexMessage, email_trans_ID, USD_contributions):
    """
    regexes for all terms listed in IDs_for_regex, and appends that data to transaction_data.

    because some emails may be for other cryptocurrencies, it first checks to ensure that this email contains a btc transaction. then it iterates through each IDs_for_regex term and uses a try/except block to convert the right data to float.

    has a check near the bottom to ensure that the total USD contributions equal the sum of the USD in the transaction. Would be bad if this was not that same, and the amount does change from time to time.
    """
    btc_check = re.search(IDs_for_regex['BTC'], regexMessage)
    if btc_check.group(3) == "BTC":
        transaction_data[email_trans_ID] = {}

        for id in IDs_for_regex.keys():
            data = re.search(IDs_for_regex[id], regexMessage)

            if data:

                    transaction_data[email_trans_ID][id] = data.group(1)

                    try:
                        transaction_data[email_trans_ID][id] = float(transaction_data[email_trans_ID][id])
                    except ValueError:
                        pass
            else:
                print("NO MATCH FOUND - " + id)
                exit()

        for id,value in transaction_data[email_trans_ID].items(): #print out the contents of each email ID key.
            print(id + ": " + str(value))

        if transaction_data[email_trans_ID]['USD'] != sum(USD_contributions.values()):
            print("TRANSACTION AMOUNT DOES NOT EQUAL CONTRIBUTIONS")
            exit()

        print(transaction_data)

    elif btc_check.group(3) != "BTC": #skips email if not a btc transaction
        print("SKIPPED")

    return transaction_data

def input_data_to_sheet(USD_contributions, transaction_data, share_of_BTC, googlesheet_name, client):
    """
    uploads data to the googlesheet.

    loops through each ID in transaction_data and each person in USD_contributions, creating a new data_list of the relevant data to upload at each iterationof the loop. appends each value to the correct column one by one.

    Google APIs have a quota that cannot be exceeded, so this function contains a try/except block to monitor for that error.
    """

    for id in transaction_data:
        for person in USD_contributions:
            #data to upload
            data_list = [transaction_data[id]['Date'],
                        transaction_data[id]['TransID'],
                        transaction_data[id]['ConvRate'],
                        USD_contributions[person],
                        share_of_BTC[id][person]
                        ]
            #opens the worksheet of the person in USD_contributions
            worksheet = client.open(googlesheet_name).worksheet(person.title())
            update_row = len(worksheet.col_values(2)) + 1 #navigate to the row where values start, col titles, empty cells
            update_col = 2 #iterates after each item to move by 1 col to the right
            print("Updating " + person.title() + ", row " + str(update_row))
            for item in data_list:
                try:
                    worksheet.update_cell(update_row, update_col, item)
                except gspread.exceptions.APIError:
                    print("GOOGLE API QUOTA EXCEEDED. WAIT 1 MIN.")
                    exit()
                update_col += 1

def get_transID_list(client, googlesheet_name, tab_name):
    """
    get the list of recorded transaction IDs to cross reference with the IDs we will get from email. return that list of IDs.
    """
    worksheet = client.open(googlesheet_name).worksheet(tab_name)
    transaction_ID_list = worksheet.col_values(3)

    return transaction_ID_list

def split_up_BTC(USD_contributions, transaction_data):
    """
    takes the USD contributions per person (daily investment) and total BTC in each transaction to calculate each persons share of that total. records those portions into a share_of_BTC dictionary. also returns percent_of_total and totalUSD for debugging purposes. only share_of_BTC data will be uploaded to googlesheet.
    """
    print("======")

    #function that takes USDC contributions per person (daily investment) and total BTC to calculate each persons share of the bulk BTC purchase, returns total USDC and two dictionaries with contribution percentages and split of BTC.

    totalUSD = sum(USD_contributions.values())
    percent_of_total = {}
    share_of_BTC = {}

    for id in transaction_data: #establish dict key for each term in transaction_data
        percent_of_total[id]={}
        share_of_BTC[id]={}

        for person in USD_contributions:
            percent_of_total[id][person]=round(USD_contributions[person]/totalUSD,3) #calc each person's percentage
            share_of_BTC[id][person]=round(percent_of_total[id][person]*transaction_data[id]['BTC'],8)

    print("Percentages: " + str(percent_of_total))
    print("BTC split: " + str(share_of_BTC))

    return totalUSD, percent_of_total, share_of_BTC

def sort_googlesheet(client, googlesheet_name, USD_contributions):
    """
    sort the googlesheet by cycling through each tab and sorting each one by one
    """
    for person in USD_contributions:
        worksheet = client.open(googlesheet_name).worksheet(person.title())
        last_row = len(worksheet.col_values(2))
        worksheet.sort((2, 'des'),range="B4:" + "G" + str(last_row))


from copy import deepcopy as dc
import math
inset = [x**2 for x in range(1,101)]
maxnum = sum(inset[50:]) + 1
print(maxnum)
l = [0]*maxnum
coeffs = []
for i in range(51): coeffs.append(dc(l))
coeffs[0][0] = 1
for i in inset:
    print(math.sqrt(i))
    for setsize in range(len(coeffs)-2,-1,-1):
        for index in range(len(coeffs[setsize])):
            if coeffs[setsize][index] != 0:
                coeffs[setsize+1][index+i] += coeffs[setsize][index]
s = 0
for i in range(len(coeffs[50])):
    if coeffs[50][i] == 1: s += i
print(s)


# *Задача 20: * В настольной игре Скрабл (Scrabble) каждая буква имеет определенную ценность.
# В случае с английским алфавитом очки распределяются так:
# A, E, I, O, U, L, N, S, T, R – 1 очко;
# D, G – 2 очка; B, C, M, P – 3 очка;
# F, H, V, W, Y – 4 очка; K – 5 очков; J, X – 8 очков;
# Q, Z – 10 очков.
# А русские буквы оцениваются так: А, В, Е, И, Н, О, Р, С, Т – 1 очко;
# Д, К, Л, М, П, У – 2 очка; Б, Г, Ё, Ь, Я – 3 очка; Й, Ы – 4 очка;
# Ж, З, Х, Ц, Ч – 5 очков; Ш, Э, Ю – 8 очков; Ф, Щ, Ъ – 10 очков.
# Напишите программу, которая вычисляет стоимость введенного пользователем слова.
# Будем считать, что на вход подается только одно слово, которое содержит либо только английские,
# либо только русские буквы.
# *Пример:*
# ноутбук
#     12

dic_eng_rus={'AEIOULNSTR': 1, 'DG': 2, 'BCMP':3,'FHVWY': 4, 'k':5, 'JX':8, 'QZ':10,'АВЕИНОРСТ':1, 'ДКЛМПУ':2,
             'БГЁЬЯ':3, 'ЙЫ':4, 'ЖЗХЦЧ':5 , 'ШЭЮ':8,  'ФЩЪ':10}
text = input("Введите текст: ").upper()
word=list(text)
lst=[]
# Созадние списка из текста
#for i in range(len(text)):
#    word.append(text[i])
print(word)
# функция поиска буквы
def findletter(x):
    for key in dic_eng_rus:
        if x in key:
            lst.append(dic_eng_rus.get(key))
            return lst
templist=[]
for k in word:
   templist= findletter(k)
print(templist)
print(sum(templist))


import re, csv

with open("reserved_words_PHP.txt", "r") as f:
    final_result = []
    lines = f.readlines()
    for line in lines:
        if re.match(r"\W*_*[a-zA-Z]", line):
            word = line.strip()
            if word[-1] == ".":
                word = word[:-1]
            final_result.append(word)

print(final_result)

with open("PHP_reserved_words.csv", "w") as f:
    writer = csv.writer(f)
    for word in final_result:
        writer.writerow([word])


import imports
import speak_take

def TaskExe():
    speak_take.speak("Hello, I am jarvis!")
    speak_take.speak("How can i help you? ")

    def GoogleSearch(term):
        query = term.replace("jarvis","")
        query = query.replace("what is","")
        query = query.replace("how to","")
        query = query.replace("who is","")
        query = query.replace("what do we mean by","")
        query = query.replace("what do you mean by","")
        query = query.replace(" ","")

        Query = str(term)
        imports.pywhatkit.search(Query)
        if "google search" in Query:
            search = imports.wikipedia.summary(Query,2)
            speak_take.speak(f":According to your search : {search}")

        else:
            pass

    def speedtest():
        import speedtest
        speak_take.speak("Checking Speed")
        speed = speedtest.Speedtest()
        downloading = speed.download()
        correctDown= int(downloading/800000)
        uploading= speed.upload()
        correctUpload=int(uploading/800000)

        if 'uploading' in query:
            speak_take.speak(f"The uploading speed is {correctUpload} mbps")

        elif 'downloading' in query:
            speak_take.speak(f"The downloading speed is {correctDown} mbps")

        else:
            speak_take.speak(f"The downloading speed is {correctDown} mbp/s and uploading speed is {correctUpload} mbps")

    def takeHindi():
        command = imports.sr.Recognizer()
        with imports.sr.Microphone() as source:
            print("Listening.............")
            command.pause_threshold = 1
            audio = command.listen(source)

            try:
                print("Recognizing............")
                query = command.recognize_google(audio,language='en-in')
                print(f'you said : {query}')

            except:
                return "none"

            return query.lower()

    def Tran():
        speak_take.speak('Tell me the Line!')
        line = takeHindi()
        traslate = imports.Translator()
        result = traslate.translate(line)
        Text = result.text
        speak_take.speak(f"The Translation Of This Line Is:",Text)

    def Temp():
        search="temperature in pune"
        url = f"https://www.google.com/search?q={search}"
        r=imports.requests.get(url)
        data= imports.BeautifulSoup(r.text,"html.parser")
        temperature=data.find("div",class_="BNeawe").text
        speak_take.speak(f"the temperature outside is {temperature}")

    def music():
        speak_take.speak("Tell me the name of the song!")
        musicname = speak_take.takecommand()
        imports.pywhatkit.playonyt(musicname)
        speak_take.speak("Your song has been started!, Enjoy sir")

    def whatsapp():
        speak_take.speak("Tell me the name of the person")
        name = speak_take.takecommand()

        if 'abc' in name:
            speak_take.speak("Tell me the message")
            msg=speak_take.takecommand()
            speak_take.speak("tell me the time sir")
            speak_take.speak("time in hour")
            hour=int(speak_take.takecommand())
            speak_take.speak("time in minutes")
            min=int(speak_take.takecommand())
            imports.pywhatkit.sendwhatmsg("+919307665461",msg,hour,min,20)
            speak_take.speak("ok sir, sending whatsapp message")

        elif 'about' in name:
            speak_take.speak("Tell me the message")
            msg=speak_take.takecommand()
            speak_take.speak("tell me the time sir")
            speak_take.speak("time in hour")
            hour=int(speak_take.takecommand())
            speak_take.speak("time in minutes")
            min=int(speak_take.takecommand())
            imports.pywhatkit.sendwhatmsg("+919730863521",msg,hour,min,20)
            speak_take.speak("ok sir, sending whatsapp message")

        else:
            speak_take.speak("tell me the phone number")
            phone=int(speak_take.takecommand())
            ph = "+91" + phone
            speak_take.speak("Tell me the message")
            msg=speak_take.takecommand()
            speak_take.speak("tell me the time sir")
            speak_take.speak("time in hour")
            hour=int(speak_take.takecommand())
            speak_take.speak("time in minutes")
            min=int(speak_take.takecommand())
            imports.pywhatkit.sendwhatmsg(ph,msg,hour,min,20)
            speak_take.speak("ok sir, sending whatsapp message")

    def ChromeAuto():
        speak_take.speak('Chrome Automation Started!!')
        command = speak_take.takecommand()

        if 'close this tab' in command:
            imports.keyboard.press_and_release('ctrl + w')

        elif 'open new tab' in command:
            imports.keyboard.press_and_release('ctrl + t')

        elif 'open new window' in command:
            imports.keyboard.press_and_release('ctrl + n')

        elif 'history' in command:
            imports.keyboard.press_and_release('ctrl + h')

    def screenshot():
        speak_take.speak("Ok sir! What should i name that file? ")
        path = speak_take.takecommand()
        path1name  = path + ".png"
        path1 = "C:\\Users\\Ketan\\OneDrive\\Pictures\\Screenshots\\" +path1name
        kk = imports.pyautogui.screenshot()
        kk.save(path1)
        imports.os.startfile("C:\\Users\\Ketan\\OneDrive\\Pictures\\Screenshots")
        speak_take.speak("Here is your screenshot")

    def Dict():
        speak_take.speak('Dictionary Activated !!')
        speak_take.speak('Tell me the Word Sir...!')
        prob1 = speak_take.takecommand()

        if 'meaning' in prob1:
            # probl = probl.replace("what is the","")
            # probl = probl.replace("jarvis","")
            # probl = probl.replace("of","")
            probl = probl.replace("meaning of","")
            result = imports.diction.meaning(probl)
            speak_take.speak(f'The Meaning For {probl} is {result}')

        else:
            speak_take.speak("tejam")

    def openApps():
        speak_take.speak("Ok sir, Wait a second!")

        if "code" in query:
            imports.os.startfile("C:\\Users\\Ketan\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe")

        elif "brave" in query:
            imports.os.startfile("C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe")

        elif "whatsapp" in query:
            imports.os.startfile("C:\\Users\\Ketan\\AppData\\Local\\WhatsApp\\WhatsApp.exe")

        elif "chrome" in query:
            imports.os.startfile("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe")

        elif "facebook" in query:
            imports.webbrowser.open("https://www.facebook.com/")

        elif "youtube" in query:
            imports.webbrowser.open("https://www.youtube.com/")

        elif "instagram" in query:
            imports.webbrowser.open("https://www.instagram.com/")

        elif "maps" in query:
            imports.webbrowser.open("https://www.google.com/maps/@18.6793851,73.8536679,15z")

    def closeApps():
        speak_take.speak("Ok sir!, Wait a second..")

        if "chrome" in query:
            imports.os.system("TASKKILL /F /im chrome.exe")

        elif "brave" in query:
            imports.os.system("TASKKILL /F /im brave.exe")

        elif "code" in query:
            imports.os.system("TASKKILL /F /im Code.exe")

        elif "whatsapp" in query:
            imports.os.system("TASKKILL /F /im WhatsApp.exe")

    def YoutubeSearch(term):
        result = "https://www.youtube.com/results?search_query=" + term
        imports.webbrowser.open(result)
        speak_take.speak("This is what i found for your search")
        imports.pywhatkit.playonyt(term)
        speak_take.speak("This may also help you Sir")

    def YoutubeAuto():
        speak_take.speak("Whats your Command ?")
        comm = speak_take.takecommand()

        if 'pause' in comm:
            imports.keyboard.press('space bar')

        elif 'restart' in comm:
            imports.keyboard.press('0')

        elif 'mute' in comm:
            imports.keyboard.press('m')

        elif 'skip' in comm:
            imports.keyboard.press('l')

        elif 'back' in comm:
            imports.keyboard.press('j')

        elif 'full screen' in comm:
            imports.keyboard.press('f')

        elif 'film mode' in comm:
            imports.keyboard.press('t')

        speak_take.speak('Done Sir')

    while True:
        query=speak_take.takecommand()

        if "hello" in query:
            speak_take.speak("hello sir i am jarvis")
            speak_take.speak("your personal assistant")
            speak_take.speak("How may i help you?")

        elif "how are you" in query:
            speak_take.speak("I am fine sir")
            speak_take.speak("what about you")

        elif "you need a break" in query:
            speak_take.speak("ok sir")
            speak_take.speak("you can call me anytime")
            speak_take.speak("Just say Wake Up jarvis!")
            break

        elif "bye" in query:
            speak_take.speak("bye sir")
            speak_take.speak("have a good day")
            break

        elif "youtube search" in query:
            query = query.replace("youtube search","")
            query = query.replace("jarvis","")
            YoutubeSearch(query)

        elif "google search" in query:
            speak_take.speak("This is what i found for your search sir!")
            query = query.replace("google search","")
            query = query.replace("jarvis","")
            # pywhatkit.search(query)
            GoogleSearch(query)
            speak_take.speak("Done Sir!!")

        elif "website" in query:
            speak_take.speak("Ok sir, Launching")
            query = query.replace("website","")
            query = query.replace("jarvis","")
            query = query.replace(" ","")
            web1 = query.replace("open","")
            web2 = "https://www." + web1 + ".com"
            imports.webbrowser.open(web2)
            speak_take.speak("launched sir!")

        elif "launch" in query:
            speak_take.speak("Tell me the name of the website!")
            name = speak_take.takecommand()
            web = 'https://www.' + name + '.com'
            imports.webbrowser.open(web)
            speak_take.speak("Done Sir!")

        # elif "whatsapp message" in query:
        #     whatsapp()

        elif "play song" in query:
            music()

        elif "wikipedia" in query:
            speak_take.speak("Searching wikipedia")
            query=query.replace("jarvis","")
            query=query.replace("wikipedia","")
            wiki= imports.wikipedia.summary(query,2)
            speak_take.speak(f"According to Wikipedia :{wiki}")

        elif "screenshot" in query:
            screenshot()

        elif "open facebook" in query:
            openApps()

        elif "open instagram" in query:
            openApps()

        elif "open code" in query:
            openApps()

        elif "open youtube" in query:
            openApps()

        elif "open brave" in query:
            openApps()

        elif "open chrome" in query:
            openApps()

        elif "open map" in query:
            openApps()

        elif "open whatsapp" in query:
            openApps()

        elif "repeat my words" in query:
            speak_take.speak("speak sir")
            ab= speak_take.takecommand()
            speak_take.speak(f"you said : {ab}")

        elif "close chrome" in query:
            closeApps()

        elif "close brave" in query:
            closeApps()

        elif "close whatsapp" in query:
            closeApps()

        elif "close code" in query:
            closeApps()

        elif "chrome automation" in query:
            ChromeAuto()

        elif 'stop' in query:
            imports.keyboard.press('spacebar')

        elif 'restart' in query:
            imports.keyboard.press('0')

        elif 'mute' in query:
            imports.keyboard.press('m')

        elif 'skip' in query:
            imports.keyboard.press('l')

        elif 'back' in query:
            imports.keyboard.press('j')

        elif 'full screen' in query:
            imports.keyboard.press('f')

        elif 'film mode' in query:
            imports.keyboard.press('t')

        elif 'pause' in query:
            imports.keyboard.press('k')

        elif 'youtube tool' in query:
            YoutubeAuto()

        elif 'dictionary' in query:
            Dict()

        elif 'joke' in query:
            get = imports.pyjokes.get_joke()
            speak_take.speak(get)

        elif "that was pretty cringe" in query:
            speak_take.speak("reaaaalllllly??????????????????")

        elif 'alarm' in query:
            speak_take.speak("enter the time")
            time=input(": Enter the time :")

            while True:
                Time_Ac= imports.datetime.datetime.now()
                now=Time_Ac.strftime("%H:%M:%S")

                if now==time:
                    speak_take.speak("time to wake up sir")
                    imports.playsound('D:\\jarvis project\\newsound.mp3')
                    speak_take.speak("Alarm closed")

                elif now>time:
                    break

        elif "remember that" in query:
            rememberMsg = query.replace("remember that","")
            rememberMsg = query .replace("jarvis","")
            speak_take.speak("You Told me to remind you that :" +rememberMsg)
            remember = open("data.txt","w")
            remember.write(rememberMsg)
            remember.close()

        elif "what do you remember" in query:
            remember = open("data.txt","r")
            speak_take.speak("You told me to remind that" +remember.read())

        elif 'please search' in query:
            import wikipedia as googleScrap
            query = query.replace("jarvis","")
            query = query.replace("google search","")
            query = query.replace("google","")
            speak_take.speak("This is what i found on the web")

            try:
                # pywhatkit.search(query)
                result = googleScrap.summary(query,2)
                speak_take.speak(result)

            except:
                speak_take.speak("no speakable data available")

        elif 'temperature' in query:
            Temp()

        # elif 'translator' in query:
        #     Tran()

        elif 'downloading speed' in query:
            speedtest()

        elif 'uploading speed' in query:
            speedtest()

        elif 'internet speed' in query:
            speedtest()

        elif 'whatsapp message' in query:
            query = query.replace("jarvis","")
            query = query.replace("send","")
            query = query.replace("whatsapp message","")
            query = query.replace("to","")
            name = query

            if 'laptop' in name:
                numb = "9067084690"
                speak_take.speak(f"Whats the Message For {name}")
                mess = speak_take.takecommand()
                whatsapp.whatsapp(numb,mess)

            elif 'pretty' in name:
                numb = "9307665461"
                speak_take.speak(f"Whats the Message For {name}")
                mess = speak_take.takecommand()
                whatsapp.whatsapp(numb,mess)

            elif 'bag' in name:
                numb = "8484037377"
                speak_take.speak(f"Whats the Message For {name}")
                mess = speak_take.takecommand()
                whatsapp.whatsapp(numb,mess)

            elif 'jarvis' in name:
                gro = "KsjFp8jhhPA9UHap9dRHcU"
                speak_take.speak(f"Whats the Message for {name} ")
                mess = speak_take.takecommand()
                whatsapp.Whatsapp_Grp(gro,mess)

TaskExe()


# --------------
class_1 = ['Geoffrey Hinton','Andrew Ng','Sebastian Raschka','Yoshua Bengio']

class_2 = ['Hilary Mason','Carla Gentry','Corinna Cortes']

new_class = class_1 + class_2

print(new_class)

new_class.append('Peter Warden')

print(new_class)

new_class.remove('Carla Gentry')

print("new_class should have",new_class)

courses = {"Math":65,"English":70,"History":80,"French":70,"Science":60}
print("The created dictionary courses should have",courses,"as the keys and values")
values = courses.values()
total=sum(values)
percentage=total/500
print("The calculated value of total should be",total,"and that of percentage should be",percentage)

mathematics={"Geoffrey Hinton":78,"Andrew Ng":95,"Sebastian Raschka":65,"Yoshua Benjio":50,"Hilary Mason":70,"Corinna Cortes":66,"Peter Warden":75}

topper = max(mathematics,key=mathematics.get)
print("The value stored in topper should be",topper)

full_name='Ng' + ' ' + 'Andrew'
certificate_name=full_name.upper()
print("certificate_name is",certificate_name)


# -*- coding: utf-8 -*-
from HandIsomorphism.hand_isomorphism_encapsulation import HandIsomorphismEncapsulation as HandIsomorphism

def test_hand_isomorphism():
    hi = HandIsomorphism()
    hi.setup(rounds=4, cards_per_round=[2, 5])
    size = hi.get_size()
    print(size)

    cards = [0, 1, 2]

    for i in range(169):
        cards = hi.hand_unindex(index=i)
        index = hi.index_hand(cards=cards)
        assert index == i

test_hand_isomorphism()


from random import randint
#gerando um numero aleatorio
n = randint(0,9)
# função que verifica se o palpite esta correto
def verificarPalpite():
	#pegando o palpite
	x = int(input("Digite um palpite entre 0 e 9: "))
	#numero de tentativas até acertar
	tentativas = 1;
	# loop até o palpite ser o numero gerado
	while(x != n):
		tentativas += 1
		print("Você errou, tente novamente");
		x = int(input("Digite um palpite ente 0 e 9: "));

	print("Parabéns, você acertou e precisou de {} tentativa(s)".format(tentativas))

verificarPalpite()


import asyncio
import logging

from asyncua import Client

class HelloClient:
    def __init__(self, endpoint):
        self.client = Client(endpoint)

    async def __aenter__(self):
        await self.client.connect()
        return self.client

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        await self.client.disconnect()

async def main():
    async with HelloClient("opc.tcp://localhost:4840/freeopcua/server/") as client:
        root = client.nodes.root
        print("Root node is: ", root)
        objects = client.nodes.objects
        print("Objects node is: ", objects)

        hellower = await objects.get_child("0:Hellower")
        print("Hellower is: ", hellower)

        resulting_text = await hellower.call_method("0:SayHello", False)
        print(resulting_text)

        resulting_text = await hellower.call_method("1:SayHello2", True)
        print(resulting_text)

        resulting_array = await hellower.call_method("1:SayHelloArray", False)
        print(resulting_array)

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO)
    asyncio.run(main())


import pygame
import pygame.gfxdraw
class Main_Menu():
    def __init__(self, screen_size: tuple, screen, start_game, quit_game):
        super().__init__()
        # Functions that call back to main
        self.start_game = start_game
        self.quit_game = quit_game
        self.screen_width, self.screen_height = screen_size[0], screen_size[1]
        self.screen = screen
        events = pygame.event.get()
        sprites = pygame.sprite.Group()
        sprites.add(Button(
                        pygame.Color((110,220,110)),
                        pygame.Color((50,200,50)),
                        pygame.Rect((self.screen_width/2)-50, (self.screen_height/2.2), 100, 50),
                        lambda b: self.Play(),
                        'Play',
                        pygame.Color('black')))

        sprites.add(Button(
                        pygame.Color((220,110,110)),
                        pygame.Color((200,50,50)),
                        pygame.Rect((self.screen_width/2)-50, (self.screen_height/1.7), 100, 50),
                        lambda b: self.Quit(),
                        'Quit'))
        sprites.update(events)
        sprites.draw(self.screen)

    def Play(self):
        self.start_game()

    def Quit(self):
        self.quit_game()

class Button(pygame.sprite.Sprite):
    # 1) no need to have 4 parameters for position and size, use pygame.Rect instead
    # 2) let the Button itself handle which color it is
    # 3) give a callback function to the button so it can handle the click itself
    def __init__(self, color, color_hover, rect, callback, text='', outline=None):
        super().__init__()
        self.text = text
        # a temporary Rect to store the size of the button
        tmp_rect = pygame.Rect(0, 0, *rect.size)

        # create two Surfaces here, one the normal state, and one for the hovering state
        # we create the Surfaces here once, so we can simple blit them and dont have
        # to render the text and outline again every frame
        self.org = self._create_image(color, outline, text, tmp_rect)
        self.hov = self._create_image(color_hover, outline, text, tmp_rect)

        # in Sprites, the image attribute holds the Surface to be displayed...
        self.image = self.org
        # ...and the rect holds the Rect that defines it position
        self.rect = rect
        self.callback = callback

    def _create_image(self, color, outline, text, rect):
        # function to create the actual surface
        # see how we can make use of Rect's virtual attributes like 'size'
        img = pygame.Surface(rect.size)
        font = pygame.font.SysFont('comicsans', 20)
        if outline:
            # here we can make good use of Rect's functions again
            # first, fill the Surface in the outline color
            # then fill a rectangular area in the actual color
            # 'inflate' is used to 'shrink' the rect
            img.fill(outline)
            img.fill(color, rect.inflate(-4, -4))
        else:
            img.fill(color)

        # render the text once here instead of every frame
        if text != '':
            text_surf = font.render(text, 1, pygame.Color('black'))
            # again, see how easy it is to center stuff using Rect's attributes like 'center'
            text_rect = text_surf.get_rect(center=rect.center)
            img.blit(text_surf, text_rect)
        return img

    def update(self, events):
        # here we handle all the logic of the Button
        pos = pygame.mouse.get_pos()
        hit = self.rect.collidepoint(pos)
        # if the mouse in inside the Rect (again, see how the Rect class
        # does all the calculation for use), use the 'hov' image instead of 'org'
        self.image = self.hov if hit else self.org
        for event in events:
            # the Button checks for events itself.
            # if this Button is clicked, it runs the callback function
            if event.type == pygame.MOUSEBUTTONDOWN and hit:
                self.callback(self)


import numpy as np
import tensorflow as tf
import tensorflow.keras as keras
from keras.callbacks import Callback

tf.keras.backend.set_floatx('float64')

from multiprocessing import Pool
# from multiprocess import Pool
from keras_pickle_wrapper import KerasPickleWrapper

INPUT_DIM = 4
OUTPUT_DIM = 3

ACTIVATION_DEFAULT = 'sigmoid'
SGD_LR_DEFAULT = 0.01

class WeightsCallback(Callback) :
    # Only works if epochs=1 !
    def __init__(self, epochs, batch_size, train_size, batch_sample_period) :
        self.batch_sample_period = batch_sample_period
        self.n_samples = (epochs * train_size) // (batch_size * batch_sample_period)

        if batch_sample_period == 1 :
            self.on_batch_end = self.on_batch_end_simple
        else :
            self.batch_sample_points = set(range(0, train_size // batch_size, batch_sample_period))
            self.on_batch_end = self.on_batch_end_samples

        self.weights_list = [None] * self.n_samples
        # self.batch_loss = np.empty(self.n_samples)

    def on_batch_end_simple(self, batch, logs=None) :
        self.weights_list[batch] = self.model.get_weights()
        # self.batch_loss[sample_id] = logs['loss']
    def on_batch_end_samples(self, batch, logs=None) :
        if batch in self.batch_sample_points :
            sample_id = batch//self.batch_sample_period
            self.weights_list[sample_id] = self.model.get_weights()
            # self.batch_loss[sample_id] = logs['loss']

l1_norm = lambda m1, m2 : np.sum(np.abs(m1 - m2))

def get_random_init(i, h, o, sigma=1, rng=np.random.default_rng()) :
    dtype = np.float64
    return [
        rng.normal(0, sigma, (i, h)).astype(dtype),
        np.zeros(h).astype(dtype),
        rng.normal(0, sigma, (h, o)).astype(dtype),
        np.zeros(o).astype(dtype)
    ]

def pert_normal(w1, mean=0, std=0.01) :
    w2 = [w.copy() for w in w1]

    w2[0] += np.random.normal(mean, std, w2[0].shape)
    w2[2] += np.random.normal(mean, std, w2[2].shape)

    return w2

def pert_uniform(w1, eps=0.05) :    # Does this actually sample the "ball" uniformly?
    w2 = [w.copy() for w in w1]

    w2[0] += np.random.random_sample(w2[0].shape) * (2 * eps) - eps
    w2[2] += np.random.random_sample(w2[2].shape) * (2 * eps) - eps

    return w2

def flatten_weights(epochs_dict) :
    '''
    Flatten a dict (epochs) of dicts (batches) of lists of arrays (weights) into a list of arrays.
    '''
    batch_weights_flat = []
    for batches in epochs_dict.values() :
        for batch_weights in batches.values() :
            batch_weights_flat.append(batch_weights)

    return batch_weights_flat

def get_dist(w1, w2) :
    # Sum of L1 norm of all matrices in each weights
    return np.sum([l1_norm(u, v) for u, v in zip(w1, w2)])

def get_dists_fromdicts(w1, w2s) :
    '''
    Input is dicts (epochs) of dicts (batches) of weights (i.e. lists of weight matrices).
    '''
    dists = []
    for w2 in w2s :
        dists.append([get_dist(ws1, ws2) for ws1, ws2 in zip(flatten_weights(w1), flatten_weights(w2))])

    return np.array(dists).T    # Convert to plot-friendly format before returning

def get_dists_fromlists(w1, w2s) :
    '''
    Input is list of weights (i.e. lists of weight matrices).
    '''
    dists = []
    for w2 in w2s :
        dists.append([get_dist(ws1, ws2) for ws1, ws2 in zip(w1, w2)])

    return np.array(dists).T    # Convert to plot-friendly format before returning

def get_dists_frommat(w1, w2s) :
    '''
    Input is lists of composite matrices.
    '''
    dists = []
    for w2 in w2s :
        dists.append([l1_norm(u, v) for u, v in zip(w1, w2)])

    return np.array(dists)

def get_dists_dataset_frommat(ws) :
    dists = np.empty((*ws.shape[:2], ws.shape[2]-1, ws.shape[3]))    # *(n_eps, n_init), n_pert, n_sample
    for i_eps in range(dists.shape[0]) :
        for i_init in range(dists.shape[1]) :
            dists[i_eps, i_init] = get_dists_frommat(ws[i_eps, i_init, 0], ws[i_eps, i_init, 1:])

    return dists

def get_permuted_sequences(x_train, y_train, epochs_eff, rng) :
    x_train_eff = np.repeat(x_train, epochs_eff, axis=0)
    y_train_eff = np.repeat(y_train, epochs_eff, axis=0)
    perm = rng.permutation(x_train_eff.shape[0])

    return x_train_eff[perm], y_train_eff[perm]

def get_nonpermuted_sequences(x_train, y_train, epochs_eff) :
    '''
    Do not permute sequences
    '''
    x_train_eff = np.concatenate([x_train] * epochs_eff)
    y_train_eff = np.concatenate([y_train] * epochs_eff)
    # perm = rng.permutation(x_train_eff.shape[0])

    return x_train_eff, y_train_eff

def train(
        xs, ys,
        initial_weights,
        h_dim=16,
        batch_size=1, epochs=1, batch_sample_period=1, n_sample_points=None,
        val_xy=None,
        activation=ACTIVATION_DEFAULT,
        verbose=0,
        shuffle=False,
        return_loss=True,
        return_model=False,
        lr=SGD_LR_DEFAULT,
        with_softmax=False,
        with_sigmoid=False
        ) :

    if n_sample_points is not None :    # Sample points override sample period
        # print(epochs, xs.shape[0], batch_size, n_sample_points)
        batch_sample_period = epochs * xs.shape[0] // batch_size // n_sample_points

    # Infer h_dim from weights
    h_dim = initial_weights[1].size     # Length of first (hidden layer) bias vector

    w_const = keras.initializers.Constant

    model = keras.models.Sequential([
        keras.Input((INPUT_DIM,)),
        keras.layers.Dense(
            h_dim,
            kernel_initializer=w_const(initial_weights[0]),
            bias_initializer=w_const(initial_weights[1]),
            activation=activation
        ),
        keras.layers.Dense(
            OUTPUT_DIM,
            kernel_initializer=w_const(initial_weights[2]),
            bias_initializer=w_const(initial_weights[3]),
            activation=activation
        )
    ])

    if with_softmax :
        model.layers[-1].activation = tf.keras.activations.softmax
    elif with_sigmoid :
        model.layers[-1].activation = tf.keras.activations.sigmoid

    model.compile(
        loss=keras.losses.SparseCategoricalCrossentropy(),
        optimizer=keras.optimizers.legacy.SGD(learning_rate=lr),
        # optimizer='rmsprop',
        # optimizer='adam',
        metrics=[keras.metrics.SparseCategoricalAccuracy()]
    )

    # print(epochs, batch_size, xs.shape[0], batch_sample_period)
    weights_callback = WeightsCallback(epochs, batch_size, xs.shape[0], batch_sample_period)
    # batch_loss = []
    # batch_sample_points = set(range(0, xs.shape[0] // batch_size, batch_sample_period))
    # batch_loss_callback = keras.callbacks.LambdaCallback(
    #     on_batch_end=lambda batch, logs : batch_loss.append(logs['loss'])
    # )

    # model = KerasPickleWrapper(model)

    # history = model().fit(  # Calling the wrapped model to get original
    history = model.fit(
        xs, ys,
        validation_data=val_xy,
        batch_size=batch_size,
        epochs=epochs,
        callbacks=[
            weights_callback,
            # batch_loss_callback,
        ],
        shuffle=shuffle,
        verbose=verbose,
        use_multiprocessing=True
    )

    # Record inital weights and shift batch weights to correspond to loss
    ws = weights_callback.weights_list
    ws.insert(0, initial_weights)
    del ws[-1]

    out_list = [ws]

    if return_loss :    # Compute and return training loss at each snapshot
        loss = get_loss_from_weights_list(ws, model, xs, ys)
        out_list.append(loss)

    if return_model : out_list.append(model)

    if len(out_list) > 1 :      # If mutliple return as tuple
        return tuple(out_list)
    else : return out_list[0]   # If only weights return as object

def train_multiple_seq(n_pert, w1_init, eps, **train_args) :
    '''
    Train an initial condition defined by `w1_init` and `n_pert` perturbations, and return the weights for all.
    '''
    # if 'return_loss' not in train_args :
    #     train_args['return_loss'] = True

    # Train 'reference' instance
    # w1, l1 = train(initial_weights=w1_init, **train_args)
    w1 = train(initial_weights=w1_init, **train_args)

    # Train perturbations
    w2s = []
    # l2s = []
    for i in range(n_pert) :
        # w2, l2 = train(initial_weights=pert_uniform(w1_init, eps), **train_args)
        w2 = train(initial_weights=pert_uniform(w1_init, eps), **train_args)
        w2s.append(w2)
        # l2s.append(l2)

    ws = [w1, *w2s]
    # ls = [l1, *l2s]

    # return ws, ls
    return ws

# Not working — Need to revise
def train_multiple(n_pert, pert_std, n_processes=None, **train_args) :
    xs = train_args.pop('xs')
    ys = train_args.pop('ys')

    h_dim = train_args.pop('h_dim')
    w1_init = get_random_init(INPUT_DIM, h_dim, OUTPUT_DIM)
    w1, bl1 = train(xs, ys, w1_init, **train_args)

    w2s = []
    # h2s = []
    bl2s = []
    with Pool(n_processes) as pool :
        w2_inits = [pert_normal(w1_init, 0, pert_std) for _ in range(n_pert)]
        args = [([xs, ys, w2_inits[i]], train_args) for i in range(n_pert)]
        # print(args)
        res = pool.starmap(train_wrapper, args)

        for w2, bl2 in res :
            w2s.append(w2)
            bl2s.append(bl2)

    # dists = []
    # for w2 in w2s :
    #     dists.append([np.sum([l1_norm(u, v) for u, v in zip(ws1, ws2)]) for ws1, ws2 in zip(flatten_weights(w1), flatten_weights(w2))])

    dists = get_dists_fromdicts(w1, w2s)

    return dists, [bl1, bl2s]

def train_wrapper(args, kwargs) :
    # print(args)
    # print(kwargs)
    return train(*args, **kwargs)

def weights_to_matrix(ws, i, h, o) :
    # ws is a list of 4 matrices
    ws_shape = (i+1 + h+1), (h + o)
    ws_mat = np.zeros(ws_shape)
    ws_mat[:i,:h] = ws[0]
    ws_mat[i,:h] = ws[1]
    ws_mat[i+1:-1,h:] = ws[2]
    ws_mat[-1,h:] = ws[3]

    return ws_mat

def matrix_to_weights(ws_mat, i, h, o) :
    ws = [[], [], [], []]
    ws[0] = ws_mat[:i,:h]
    ws[1] = ws_mat[i,:h]
    ws[2] = ws_mat[i+1:-1,h:]
    ws[3] = ws_mat[-1,h:]

    return ws

def weights_list_to_matrices(ws, i, h, o) :
    ws_shape = (i+1 + h+1), (h + o)
    n_pert = len(ws)
    n_samples = len(ws[0])
    ws_mat = np.zeros((n_pert, n_samples, *ws_shape))

    for i_pert in range(n_pert) :
        for i_pt in range(n_samples) :
            ws_mat[i_pert, i_pt, ...] = weights_to_matrix(ws[i_pert][i_pt], i, h, o)

    return ws_mat

def matrices_to_weights_list(ws_mat, i, h, o) :
    # ws_shape = (i+1 + h+1), (h + o)
    n_pert = ws_mat.shape[0]
    n_samples = ws_mat.shape[1]
    ws = []

    for i_pert in range(n_pert) :
        ws_pert = []
        for i_pt in range(n_samples) :
            ws_pert.append(matrix_to_weights(ws_mat[i_pert, i_pt, ...], i, h, o))
        ws.append(ws_pert)

    return ws

def get_loss_from_weights_list(ws, model, xs, ys) :
    """
    Return `model.loss()` for `xs` and `ys` from each set of weights in `ws`.

    Not a very time efficient solution, but to improve it I would need to
    implement tf training at a lower level...

    Or I could just define my model with a custom loss_tracker ?
    """
    loss = np.empty(len(ws))
    for i, w in enumerate(ws) :
        model.set_weights(w)
        loss[i] = model.loss(ys, model(xs)).numpy()

    return loss

def get_loss_from_matrices(ws, model, xs, ys) :
    """
    Return `model.loss()` for `xs` and `ys` from each weight matrix in `ws`.
    """
    h_dim = ws.shape[-1] - OUTPUT_DIM
    if ws.ndim > 3 :
        ws = matrices_to_weights_list(ws, INPUT_DIM, h_dim, OUTPUT_DIM)   # This could cause confusion if input is for multiple perturbations
    else :
        ws = matrices_to_weights_list(np.expand_dims(ws, axis=0), INPUT_DIM, h_dim, OUTPUT_DIM)[0]

    loss = get_loss_from_weights_list(ws, model, xs, ys)

    return loss

def get_empty_model(h_dim) :
    model = keras.models.Sequential([
        keras.Input((INPUT_DIM,)),
        keras.layers.Dense(
            h_dim,
            activation=ACTIVATION_DEFAULT
        ),
        keras.layers.Dense(
            OUTPUT_DIM,
            activation=ACTIVATION_DEFAULT
        )
    ])

    model.compile(
        loss=keras.losses.SparseCategoricalCrossentropy(),
        optimizer=keras.optimizers.legacy.SGD(learning_rate=SGD_LR_DEFAULT),
        metrics=[keras.metrics.SparseCategoricalAccuracy()]
    )

    return model


# Vasiliu Ana - IIA3

import random
import time
import math
from Cryptodome.Util import number

def test_output(output):
    print("Procentaj de 0: " + str(output.count("0") / len(output) * 100))
    print("Procentaj de 1: " + str(output.count("1") / len(output) * 100))

def blum_blum_generator():
    p = number.getPrime(512)
    while p % 4 != 3:
        p = number.getPrime(512)
    q = number.getPrime(512)
    while q % 4 != 3:
        q = number.getPrime(512)

    n = p * q
    l = pow(2, 10)
    s = int(time.time())
    x = [pow(s, 2) % n]
    output = chr(x[0] % 2 + ord("0"))
    for i in range(1, l):
        x.append(pow(x[i - 1], 2) % n)
        output += chr(x[i] % 2 + ord("0"))
    return output

start_time = time.time()
bb_output = blum_blum_generator()
elapsed_time = time.time() - start_time

print("Timp de executie pentru generatorul Blum-Blum: ")
print("%s secunde" % elapsed_time)

print("Rezultat generator Blum-Blum: ")
print(bb_output)
test_output(bb_output)

def jacobi(n, k):
    n %= k
    result = 1
    while n != 0:
        while n % 2 == 0:
            n /= 2
            if (k % 8) in (3, 5):
                result = -result
        n, k = k, n
        if n % 4 == 3 and k % 4 == 3:
            result = -result
        n %= k
    if k == 1:
        return result
    else:
        return 0

def jacobi_generator():
    false_pos_prob = 0.000000000000000000000001
    p = number.getPrime(512)
    while not (number.isPrime(p, false_pos_prob)) or p % 4 != 3:
        p = number.getPrime(512)
    q = number.getPrime(512)
    while not (number.isPrime(q, false_pos_prob)) or q % 4 != 3:
        q = number.getPrime(512)
    n = p * q
    l = pow(2, 10)
    a = int(time.time() * 1000)
    while math.gcd(a, n) != 1:
        a = int(time.time())
    output = ""
    for i in range(1, l):
        jacobi_symbol = jacobi(a + i, n)
        if jacobi_symbol == 0:
            return "error"
        if jacobi_symbol == -1:
            jacobi_symbol = 0
        output += chr(jacobi_symbol + ord("0"))
    return output

start_time = time.time()
jacobi_output = jacobi_generator()
while jacobi_output == "error":
    jacobi_output = jacobi_generator()
elapsed_time = time.time() - start_time

print("\nTimp de executie pentru generatorul Jacobi: ")
print("%s secunde" % elapsed_time)

print("Rezultat generator Jacobi: ")
print(jacobi_output)
test_output(jacobi_output)

def lfsr():
    l = 10
    pol = [10, 3]
    pos = []
    for i in range(0, len(pol)):
        pos.append(l - pol[i])

    d = random.getrandbits(l)
    t = pow(2, l) - 1
    output = [d & 1]
    for i in range(0, t):
        a = (d >> pos[0]) & 1
        b = (d >> pos[1]) & 1
        res = a ^ b
        d = d >> 1
        mask = 1 << (l - 1)
        d = d & ~mask
        if res == 1:
            d = d | (1 << (l - 1))
        output.append(d & 1)
    return output

start_time = time.time()
lfsr_output = lfsr()
elapsed_time = time.time() - start_time

print("\nTimp de executie pentru generatorul LFSR: ")
print("%s secunde" % elapsed_time)

print("Rezultat generator LFSR: ")
print(lfsr_output)


import numpy as np
import matplotlib.pyplot as plt
from math import *
# import ROOT
import matplotlib.patches as mpatches
import statistics
from copy import deepcopy
import sys
import os
import random
import yaml
import matplotlib
import glob
from grepfunc import grep_iter
import re
import json
import argparse
from termcolor import colored
import mplhep as hep
from matplotlib.backends.backend_pdf import PdfPages
from mpl_toolkits.axes_grid1 import AxesGrid
from scipy.stats import sem

regex_typelabel=re.compile("Q")
regex_amodule=re.compile("dPosXYZ")
regex_rmodule=re.compile("dRotXYZ")
labels=["Tx","Ty","Tz","Rx","Ry","Rz"]
positions=["x_global","y_global","z_global"]
trackInfo=["nTracks","nHits"]
stations = ["T1", "T2", "T3"]
layers = ["U", "V", "X1", "X2"]

colors = ['black', 'blue', 'red', 'green', 'yellow', 'magenta', 'brown', 'cyan']
markers = ['o', 'x', 'd', 'D', '.', 'v', 's', 'p']

def open_alignment(thisfile,convergence=True):
    with open(thisfile) as f:
        align_output=json.load(f)

    convergences=align_output.pop("converged")

    for alignable in align_output.keys():
        for label in labels+positions+trackInfo:
            if "FT" in alignable:
                align_output[alignable][label]=[float(ele.strip(',')) for ele in align_output[alignable][label]]

    if convergence:
        align_output["convergence"]=convergences
    return align_output

def convertGlobal(align_external,halfmoduleAlignables):
    align_output=deepcopy(align_external)
    for alignable in halfmoduleAlignables:
        # rule for readout flip in x per layer:
        if "Quarter0" in alignable or "Quarter1" in alignable or "Q0" in alignable or "Q1" in alignable:
            for label in ["Tx","Rx","Ry","Ty"]:
                if label in align_output[alignable].keys():
                    align_output[alignable][label]=[-value for value in align_output[alignable][label]]
            # minus sign on x and Rx and y and Ry for all iterations
        if "X1" in alignable or "V" in alignable:
            # minus sign on x and Rx and z and Rz for all iterations
            for label in ["Tx","Rx","Rz","Tz"]:
                if label in align_output[alignable].keys():
                    align_output[alignable][label]=[-value for value in align_output[alignable][label]]

    return align_output

def get_unc(input):
    n_files = len(input)
    Tx_chi2 = [[] for _ in range(n_files)]
    Ty_chi2 = [[] for _ in range(n_files)]
    Tz_chi2 = [[] for _ in range(n_files)]

    Rx_chi2 = [[] for _ in range(n_files)]
    Ry_chi2 = [[] for _ in range(n_files)]
    Rz_chi2 = [[] for _ in range(n_files)]
    for i in range(n_files):
        Tx_chi2[i].append(input[i]['Tx_chi2'][0])
        Ty_chi2[i].append(input[i]['Ty_chi2'][0])
        Tz_chi2[i].append(input[i]['Tz_chi2'][0])

        Rx_chi2[i].append(input[i]['Rx_chi2'][0])
        Ry_chi2[i].append(input[i]['Ry_chi2'][0])
        Rz_chi2[i].append(input[i]['Rz_chi2'][0])

    return np.array(Tx_chi2), np.array(Ty_chi2), np.array(Tz_chi2), np.array(Rx_chi2), np.array(Ry_chi2), np.array(Rz_chi2)

files_Tx = [\
         "txt_files/parsed_out/parsedlog_269045_Tx_0_2mm.json",
         "txt_files/parsed_out/parsedlog_269045_Tx_0_4mm.json",
         "txt_files/parsed_out/parsedlog_269045_Tx_0_6mm.json",
         "txt_files/parsed_out/parsedlog_269045_Tx_0_8mm.json",
         "txt_files/parsed_out/parsedlog_269045_base_noSurvey.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_2mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_3mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_4mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_5mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_6mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_7mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_8mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_9mm_noS.json",
         "txt_files/parsed_out/Tx/parsedlog_269045_Tx_10mm_noS.json",
]
files_Ty = [\
         "txt_files/parsed_out/parsedlog_269045_base_noSurvey.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_2mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_3mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_4mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_5mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_6mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_7mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_8mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_9mm_noS.json",
         "txt_files/parsed_out/Ty/parsedlog_269045_Ty_10mm_noS.json",
]
files_Rx = [\
        "txt_files/parsed_out/parsedlog_269045_base_noSurvey.json",
        "txt_files/parsed_out/Rx/parsedlog_Rx_03mrad.json",
        "txt_files/parsed_out/Rx/parsedlog_Rx_04mrad.json",
        "txt_files/parsed_out/Rx/parsedlog_Rx_05mrad.json"
]
files_Ry = [\
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_4mrad.json", # 0.0004
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_3mrad.json", # 0.0003
        "txt_files/parsed_out/parsedlog_269045_base_noSurvey.json", # 0.0002 base
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_1mrad.json", # 0.0001
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_02mrad.json", # 0.00002
        "txt_files/parsed_out/Ry/parsedlog_Ry_3microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_2microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_1microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_8microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_6microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_5microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_4microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_44microrad.json",
        "txt_files/parsed_out/Ry/parsedlog_Ry_0_2microrad.json",
]

legendlabels_Tx=[\
        "0_2mm",
        "0_4mm",
        "0_6mm",
        "0_8mm",
        "base",
        "2mm",
        "3mm",
        "4mm",
        "5mm",
        "6mm",
        "7mm",
        "8mm",
        "9mm",
        "0mm"
]
legendlabels_Ty = [\
        "base",
        "2mm",
        "3mm",
        "4mm",
        "5mm",
        "6mm",
        "7mm",
        "8mm",
        "9mm",
        "10mm",
]
legendlabels_Rx = [\
        "base",
        "03mrad",
        "04mrad",
        "05mrad",
]

legendlabels_Ry = [\
        "04mrad",
        "03mrad",
        "base",
        "01mrad",
        "0_02mrad",
        "3microRad",
        "2microRad",
        "1microRad",
        "08microRad",
        "06microRad",
        "05microRad",
        "0_44microRad",
        "04microRad",
        "02microRad",
]

def get_chi2_values(input_files):
    outputs=[open_alignment(thisfile) for thisfile in input_files]
    plotted_alignables=[]
    for align_block in outputs:
        thislist=[]
        for key in align_block.keys():
            thislist.append(key)
        plotted_alignables.append(thislist)
    outputs=[convertGlobal(align_block,plotted_alignables[0]) for align_block in outputs]

    Tx_chi2_tx = get_unc(outputs)[0]
    Ty_chi2_tx = get_unc(outputs)[1]
    Tz_chi2_tx = get_unc(outputs)[2]
    Rx_chi2_tx = get_unc(outputs)[3]
    Ry_chi2_tx = get_unc(outputs)[4]
    Rz_chi2_tx = get_unc(outputs)[5]

    return Tx_chi2_tx, Ty_chi2_tx, Tz_chi2_tx, Rx_chi2_tx, Ry_chi2_tx, Rz_chi2_tx

chi2_values_from_Tx_changes = get_chi2_values(files_Tx)
chi2_values_from_Ty_changes = get_chi2_values(files_Ty)
chi2_values_from_Rx_changes = get_chi2_values(files_Rx)
chi2_values_from_Ry_changes = get_chi2_values(files_Ry)

Tz_chi2_vals = [2586.76]

# 7mm and 8mm are missing, last entries are 5mm, 6mm, 7mm, 10mm
Tx_chi2_tz = [15692/768, 8182.69/768, 5278.57/768, 5770.66/768, 12196.7/768, 9622.9/768, 10590.6/768]
Ty_chi2_tz = [1127.64/768, 1122.31/768, 1119.17/768, 1120.25/768, 1125.93/768, 1125.58/768, 1126.06/768]
Tz_chi2_tz = [646.69/768, 287.418/768, 161.672/768, 103.47/768, 71.8544/768, 52.791/768, 25.8676/768]
Rx_chi2_tz = [3086.38/768, 3086.38/768, 3086.38/768, 3086.38/768, 3086.38/768, 3086.38/768, 3086.38/768]
Ry_chi2_tz = [0.00366032/768, 0.0036605/768, 0.00369746/768, 0.00370069/768, 0.0036554/768, 0.00368278/768, 0.0036503/768]
Rz_chi2_tz = [708.737/768, 679.203/768, 658.385/768, 658.059/768, 697.696/768, 678.97/768,  687.408/768]

Tz_data = np.array([Tx_chi2_tz, Ty_chi2_tz, Tz_chi2_tz, Rx_chi2_tz, Ry_chi2_tz, Rz_chi2_tz])
print(Tz_data)
correct_order = [2, 0, 1, 3, 6, 4, 5, 7, 10, 8, 9, 11]
Tx_err = [0.2, 0.4, 0.6, 0.8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # microns
Ty_err = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # microns
Tz_err = [2,3,4,5,6,7,10] # microns

Rx_err = [0.2, 0.3, 0.4, 0.5] # microrad
Ry_err = [0.4, 0.3, 0.2, 0.1, 0.02, 0.003, 0.002, 0.001, 0.0008, 0.0006, 0.0005, 0.00044, 0.0004, 0.0002] # microrad

total_n_dofs = 768

def plotting(x_range, data, dofs, label):
    if label == 'Ry':
        x_vals = np.linspace(0, 14, 14)
        print(f"label: {label}")
        print(data[0])
        plt.plot(x_vals, data[0] / dofs, marker='.', linestyle='--', label='Tx_chi')
        plt.plot(x_vals, data[1] / dofs, marker='.', linestyle='--', label='Ty_chi')
        plt.plot(x_vals, data[2] / dofs, marker='.', linestyle='--', label='Tz_chi')
        plt.plot(x_vals, data[3] / dofs, marker='.', linestyle='--', label='Rx_chi')
        plt.plot(x_vals, data[4] / dofs, marker='.', linestyle='--', label='Ry_chi')
        plt.plot(x_vals, data[5] / dofs, marker='.', linestyle='--', label='Rz_chi')
        plt.xticks(x_vals, x_range, rotation=45)
        plt.xlabel('[mu rad]')
    if label == 'Tz':
        print(f"label: {label}")
        print(data[0])
        plt.plot(x_range, data[0], marker='.', linestyle='--', label='Tx_chi')
        plt.plot(x_range, data[1], marker='.', linestyle='--', label='Ty_chi')
        plt.plot(x_range, data[2], marker='.', linestyle='--', label='Tz_chi')
        plt.plot(x_range, data[3], marker='.', linestyle='--', label='Rx_chi')
        plt.plot(x_range, data[4], marker='.', linestyle='--', label='Ry_chi')
        plt.plot(x_range, data[5], marker='.', linestyle='--', label='Rz_chi')
    if label in ['Tx', 'Ty']:
        print(f'else: {label}')
        print(data)
        plt.plot(x_range, data[0] / dofs, marker='.', linestyle='--', label='Tx_chi')
        plt.plot(x_range, data[1] / dofs, marker='.', linestyle='--', label='Ty_chi')
        plt.plot(x_range, data[2] / dofs, marker='.', linestyle='--', label='Tz_chi')
        plt.plot(x_range, data[3] / dofs, marker='.', linestyle='--', label='Rx_chi')
        plt.plot(x_range, data[4] / dofs, marker='.', linestyle='--', label='Ry_chi')
        plt.plot(x_range, data[5] / dofs, marker='.', linestyle='--', label='Rz_chi')
        plt.xlabel('[mu m]')
    if label == 'Rx':
        print(f'else: {label}')
        print(data)
        plt.plot(x_range, data[0] / dofs, marker='.', linestyle='--', label='Tx_chi')
        plt.plot(x_range, data[1] / dofs, marker='.', linestyle='--', label='Ty_chi')
        plt.plot(x_range, data[2] / dofs, marker='.', linestyle='--', label='Tz_chi')
        plt.plot(x_range, data[3] / dofs, marker='.', linestyle='--', label='Rx_chi')
        plt.plot(x_range, data[4] / dofs, marker='.', linestyle='--', label='Ry_chi')
        plt.plot(x_range, data[5] / dofs, marker='.', linestyle='--', label='Rz_chi')
        plt.xlabel('[mu rad]')
    plt.hlines(1, min(x_range), max(x_range), 'black')
    plt.legend()
    plt.grid()
    plt.title(f'chi2 / dof changing only {label} error')
    plt.ylabel('chi2 per dof')
    # plt.show()
    plt.savefig(f'chi2_plots/{label}_out.pdf')
    plt.clf()

plotting(Tx_err, chi2_values_from_Tx_changes, total_n_dofs, 'Tx')
plotting(Ty_err, chi2_values_from_Ty_changes, total_n_dofs, 'Ty')
plotting(Tz_err, Tz_data, total_n_dofs, 'Tz')
plotting(Rx_err, chi2_values_from_Rx_changes, total_n_dofs, 'Rx')
plotting(Ry_err, chi2_values_from_Ry_changes, total_n_dofs, 'Ry')

errors_v1 = [0.001, 0.0015, 0.004, 0.0002, 0.0002, 0.0002]
errors_v2 = [0.0005, 0.002, 0.002, 0.0002, 0.0002, 0.0002]
errors_v3 = [0.0002, 0.002, 0.002, 0.0002, 0.0002, 0.0002]
errors_v4 = [0.00018, 0.0015, 0.0018, 0.0004, 0.00000044, 0.00019]

from scipy.stats import linregress
x1 = Tx_err[0:7]
y1 = (chi2_values_from_Tx_changes[0][0:7] / total_n_dofs).T[0]
print(x1, y1)
erg = linregress(x1, y1)
print(erg)

# try curve fit
from scipy.optimize import curve_fit
def func(x, a, b, c):
    return a * np.exp(-b * x) + c
def Gauss(x, A, B):
    y = A*np.exp(-1*B*x**2)
    return y
x2 = Tx_err[6:]
y2 = (chi2_values_from_Tx_changes[0][6:] / total_n_dofs).T[0]

xfull = np.array(Tx_err)
yfull = np.array((chi2_values_from_Tx_changes[0] / total_n_dofs).T[0])

xdata1 = np.array(x1)
ydata1 = np.array(y1)

xdata2 = np.array(x2)
ydata2 = np.array(y2)

plt.plot(xfull, yfull, 'k-', label='data')
popt1, pcov1 = curve_fit(func, xdata1, ydata1)
popt2, pcov2 = curve_fit(func, xdata2, ydata2)
plt.plot(xdata1, func(xdata1, *popt1), 'r-',label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt1))
# plt.plot(xdata2, func(xdata2, *popt2), 'b-',label='fit: A=%5.3f, B=%5.3f, c=%5.3f' % tuple(popt2))
plt.axvline(x = 0.29, color = 'g', linestyle='dashed', label='Tx unc. = 0.29 micron')
plt.axhline(y = 1, color = 'y', linestyle='dashed', label='chi2 / dof = 1')
plt.grid()
plt.legend()
plt.xlabel('Tx unc [micron]')
plt.ylabel('chi2 / dof')
plt.title('Tx uncertainty tuning')
plt.savefig('fit_Tx.pdf')
plt.show()
# print(func(xdata, *popt))

# FIXME:
# 1. correct the x label to be LaTeX formated
# 2. Tx: more alignments between 0 and 1 microns, also fit a function (linear, polynomial 2nd order and find chi2 / dof = 1)
# 3. Ty: more alignments between 1.2 and 1.7 microns
# 4. Tz: more alignments between 1 and 2 microns
# 5. Rx: more alignments around 0.4 micro rad (0.0004 milli rad)
# 6. Ry: more alignments roughly at 0.00044 microns
# 7: Rz: please also do a plot for Rz around 0.0002 milli rad


import pymunk

from ecs.core.components.gfx import GfxAnimatedSprite
from ecs.core.components.light import LightFx
from ecs.core.components.physic import PhysicDisc
from ecs.core.main.entity import Entity
from ecs.user.idle.counters import UserCounter
from ecs.user.script.gfxpos import LightFollowGfx
from ecs.user.script.phyuserscripts import PhyGfxLink
from shmup.common.constants import *
from random import randint

from shmup.scripts.destroyFish import DestroyFish
from shmup.scripts.fishMove import FishMove

class FishFactory():

    FINAL_SIZE = 128

    def __init__(self, gfxFishList, entFishList):
        self._gfxFishList = gfxFishList
        self._entFishList = entFishList

        fs = FishFactory.FINAL_SIZE
        FISH01 = {"path": "resources/images/fishes/fish01.png",
                  "textureName":"fish01",
                  "size": (1280, 768),
                  "finalSize":(1.30*fs,1.30*fs),
                  "phyRadius": (fs // 2) * 0.85,
                  "nbSprites": (5, 4),
                  "speed":5,
                  "period":1,
                  "amplitude":0,
                  "offset":(-8,0),
                  "life":5
                  }
        FISH02 = {"path": "resources/images/fishes/fish02.png",
                  "textureName":"fish02",
                  "size": (512, 160),
                  "finalSize":(3*fs,3*fs),
                  "phyRadius": (fs // 2) * 0.8,
                  "nbSprites": (2, 2),
                  "speed":9,
                  "period":1,
                  "amplitude":0,
                  "offset":(-96,0),
                  "life":3
                  }
        FISH03 = {"path": "resources/images/fishes/fish03.png",
                  "textureName":"fish03",
                  "size": (1280, 480),
                  "finalSize":(2*fs,2*fs),
                  "phyRadius": (fs // 2) * 0.85,
                  "nbSprites": (5, 4),
                  "speed":8,
                  "period":1,
                  "amplitude":0,
                  "offset":(-64,0),
                  "life":1
                  }
        FISH04 = {"path": "resources/images/fishes/fish04.png",
                  "textureName":"fish04",
                  "size": (800, 1024),
                  "finalSize":(1.3*fs,1.3*fs),
                  "phyRadius": (fs // 2) * 0.85,
                  "nbSprites": (4, 4),
                  "speed":2,
                  "period":1,
                  "amplitude":0,
                  "offset":(0,0),
                  "life":6
                  }
        JELLY  = {"path": "resources/images/fishes/jelly.png",
                  "textureName":"jelly",
                  "size": (800, 512),
                  "finalSize":(1.7*fs,1.7*fs),
                  "phyRadius": (fs // 2) * 0.7,
                  "nbSprites": (5, 2),
                  "speed":2.5,
                  "period":2,
                  "amplitude":10,
                  "offset":(0,64),
                  "life":3
                  }
        LANTERN = {"path": "resources/images/fishes/lantern.png",
                   "textureName": "lantern",
                   "size": (1024, 800),
                  "finalSize":(1.6*fs,1.6*fs),
                   "phyRadius":(fs//2)*0.85,
                   "nbSprites": (4, 4),
                   "speed":5,
                  "period":1,
                  "amplitude":5,
                  "offset":(-8,-8),
                  "life":4
                 }
        SQUID  = {"path": "resources/images/fishes/squid.png",
                  "textureName":"squid",
                 "size": (512, 192),
                  "finalSize":(3*fs,3*fs),
                  "phyRadius": (fs // 2) * 0.85,
                  "nbSprites": (2, 3),
                  "speed":6,
                  "period":1,
                  "amplitude":2.5,
                  "offset":(-64,0),
                  "life":4
                 }
        self._fishes = [
            FISH01,
            FISH02,
            FISH03,
            FISH04,
            JELLY,
            LANTERN,
            SQUID   ]

    def create(self):

        # Create entity
        eFish = Entity("Fish")

        # Select random shape
        idx   = randint(0,len(self._fishes)-1)
        fish  = self._fishes[idx]
        nbX   = fish["nbSprites"][0]
        nbY   = fish["nbSprites"][1]
        W     = fish["size"][0] // nbX
        H     = fish["size"][1] // nbY
        fSize = fish["finalSize"]
        phyRadius = fish["phyRadius"]
        speed = fish["speed"]
        ampl  = fish["amplitude"]
        period= fish["period"]
        offset= fish["offset"]
        life = fish["life"]
        params = {
            "filePath": fish["path"],
            "textureName": fish["textureName"],
            "spriteBox": (nbX,nbY,W,H),
            "startIndex": 0,
            "endIndex": (W*H)-1,
            "frameDuration": 1 / 10,
            "size": fSize,
            "position":(SCREEN_WIDTH+(W//2),randint(H//2+100,SCREEN_HEIGHT-(H//2)-100)),
        }
        fishGfx     = GfxAnimatedSprite(params, ZIDX_FISHES, "fishGfx")
        fishMove    = FishMove(fishGfx, speed, ampl, period)
        fishDestroy = DestroyFish(eFish, fishGfx, self._gfxFishList, self._entFishList)

        params = {
            "mass"         : 0.01,
            "radius"       : phyRadius,
            "mode"         : pymunk.Body.KINEMATIC,
            "pos"          : (10000,10000),
            "sensor"       : False,
            "collisionType": COLL_TYPE_FISH,
        }
        fishPhy = PhysicDisc(params,"fishPhy")
        phyGfxLink = PhyGfxLink(fishPhy, fishGfx, offset, "phygfxlink")

        # Player life
        lifeCmp = UserCounter(0, life, life, True, "fishLife")

        # add all components to entity
        eFish.addComponent(fishGfx)
        eFish.addComponent(fishMove)
        eFish.addComponent(fishDestroy)
        eFish.addComponent(fishPhy)
        eFish.addComponent(phyGfxLink)
        eFish.addComponent(lifeCmp)
        if idx == 5:
            # light
            light = LightFx((-1000, -1000), fSize[0]/2, (255, 255, 0), "soft")
            lightFollow = LightFollowGfx(fishGfx, light, None, (-fSize[0]/2.3,fSize[1]/16))
            eFish.addComponent(light)
            eFish.addComponent(lightFollow)

        # return result
        return eFish


import retro
import numpy as np
# from pygame.locals import *
# import pygame
# import cv2
# import imutils

# screen = pygame

env = retro.make('SonicTheHedgehog-Genesis', 'GreenHillZone.Act1')
env.reset()
done = False

while not done:
    env.render()
    action = [1,1,0,0,0,0,0,0,0,0,0,0];
    ob, rew, done, info = env.step(action)

    print("Action ", action, "Reward ", rew, " Ob ", np.shape(ob))


class TreeNode:
    def __init__ (self) :
        self.left = None
        self.data = None
        self.right = None

    # @return: None
    # insert 구현
    def insert(self, data):                         # None을 만나면 새로운 노드 삽입
        new_node = TreeNode()                       # TreeNode의 new_node 객체 생성
        cur_node = self.root                        # root에 cur_node 객체 대입

        while cur_node is not None:                 #cur_node가 None이 아닐 때까지 돌면서
            if new_node.data < cur_node.data :      # 새 삽입 데이터가 현재 노드의 데이터보다 작을 때,
                cur_node = cur_node.left            # 현재위치 -> 현재왼쪽 자식으로 현위치 이동
            else:
                cur_node = cur_node.right

            """
            만약 new_node.data < cur_node.data 일 때 왼쪽 자식노드로 이동
            만약 new_node.data >= cur_node.data 일 때 오른쪽 자식노드로 이동

            만약 cur_node가 None을 만나면
            """

        cur_node = new_node

    # if found, return true,
    # if not found, return false
    # @return: bool
    # 검색 기능
    def find(self) :
        find_name = input()

        current = self.root

        while True :
            if find_name == current.data :
                print(find_name,'을(를) 찾음')
                break
            elif find_name < current.data :
                if current.left == None:
                    print('없음')
                    break
                current = current.left
            else:
                if current.right == None:
                    print('없음')
                    break
                current = current.right

    # @return: None
    # remove 구현
    def delete(self, data):
        node = TreeNode()               # 노드 객체 생성

        # 말단 노드일떄
        # 지우고 none
        # 자식이 하나일 떄
        # 자식으로 대체하고 현재노드 삭제
        # 자식이 둘일 때
        # 1. 왼쪽 자식의 가장 큰(오른쪽 끝)으로 교체
        # 2. 오른쪽 자식의 가장 작은(왼쪽 끝)으로 교체
        # 1 or 2 선택

# root -> left -> right
def preorder(tree):             #전위
    # TODO: Implmement preorder tree
    None

# left -> root -> right
def inorder(tree):              #중위
    # TODO: Implmement inorder tree
    None

# left -> right -> root
def postorder(tree):            #후위
    # TODO: Implmement postorder tree
    None

# https://hongku.tistory.com/160
'''
t = Tree()
for i in range(1, 16):
    t.insert(i)

preorder(t)
# printed result
# 1 2 4 8 9 5 10 11 3 6 12 13 7 14 15

inorder(t)
# printed result
# 8 4 9 2 10 5 11 1 12 6 13 3 14 7 15

postorder(t)
# printed result
# 8 9 4 10 11 5 2 12 13 6 14 15 7 3 1
'''


import sys
sys.stdin = open('input.txt')

def inorder(n):                             # 중위순회
    if n:
        inorder(ch1[n])
        print(st[n], end='')
        inorder(ch2[n])

for idx in range(1, 11):
    K = int(input())                        # 노드 수
    ch1 = [0] * (K+1)                       # 왼쪽 자식 리스트
    ch2 = [0] * (K+1)                       # 오른쪽 자식 리스트
    st = [0] * (K+1)                        # 문자열 리스트
    for i in range(K):
        lst = list(input().split())         # 입력 리스트

        if len(lst) == 4:                   # 리스트 길이가 4이면 (= 자식이 둘 다 있으면)
            ch1[int(lst[0])] = int(lst[2])  # 왼쪽 자식 입력
            ch2[int(lst[0])] = int(lst[3])  # 오른쪽 자식 입력
            st[int(lst[0])] = lst[1]        # 문자열 입력

        elif len(lst) == 3:
            ch1[int(lst[0])] = int(lst[2])
            st[int(lst[0])] = lst[1]

        elif len(lst) == 2:
            st[int(lst[0])] = lst[1]

    print(f'#{idx}', end=' ')               # 출력
    inorder(1)
    print()


import os
opcion = True
tupla=("Nombre","Precio")
lista_productos=[]

def listar():
    print("\n\t\t\t Productos en inventario: \n")
    for key in lista_productos:
            print()
            print("\t\t\t------------------------------")
            print("\t\t\t",tupla[0],key[tupla[0]])
            print("\t\t\t",tupla[1],key[tupla[1]])
            print("\t\t\t------------------------------")

def insertar():
    while True:
        print("\t\t\t Agregar producto  \n""\t\t\t------------------------------")
        producto={}
        for i in range(0,len(tupla)):
            key=input(f"\t\t\t Ingrese {tupla[i]}: ")
            while key == "":
                print()
                print("\t\t\t ----------------------------------")
                print()
                print("\t\t\t Debe ingresar al menos un caracter.")
                print()
                print("\t\t\t ----------------------------------")
                print()
                key=input(f"\t\t\t ingrese {tupla[i]}: ")
            if (tupla[i]==tupla[1]):
                while not key.isdigit():
                    print()
                    print("\t\t\t ----------------------------------")
                    print()
                    print("\t\t\t Debes ingresar un valor numerico.")
                    print()
                    print("\t\t\t ----------------------------------")
                    print()
                    key=input(f"\t\t\t ingrese {tupla[i]}: ")
            producto[tupla[i]]=key
        lista_productos.append(producto)
        add=input("\t\t\t Presiona enter para agregar otro producto. De lo contrario escribe ""NO"" y presiona enter: ")
        if (add=='no' or add=='No' or add=='NO'or add=='nO'):
            break

def buscar(nombre):
    index = 0
    while index < len(lista_productos):
        if (nombre == lista_productos[index][tupla[0]]):
            return index
        index = index + 1
    return False

def modificar():
    while True:
        print("\n\t\t Registros: \n")
        listar()
        print("\n\t\t Modificar: \n")
        new_producto=input("\t\t\t Ingrese el producto a modificar: ")
        index = buscar(new_producto)
        if (not isinstance(index,bool)):
            modificar=input("\t\t\t Ingrese el nombre del nuevo producto: ")
            lista_productos[index][tupla[0]] =  modificar
            break
        else:
            print("\n\t\t\t El dato ingresado no existe el registro." )
            input("\t\t\t Presione enter para continuar...")
            os.system("clear")

def sumar():
    suma=0
    for key in lista_productos:
            suma=suma+int(key[tupla[1]])
    print("\t\t\t El valor total de los productos es $",suma)

def eliminar():
    print("\n\t\t Registros: \n")
    listar()
    eliminar=input("\t\t\t Ingrese el producto a eliminar: ")
    index = buscar(eliminar)
    if (not isinstance(index,bool)):
        lista_productos.pop(index)
    else:
        print("\t\t\t No existe el elemento a eliminar")

while opcion:
    os.system("clear")
    print()
    print("\t\t *** Programa de inventario ***\n")
    print ("\t\t\t ##### MENU ######")
    print ("\t\t\t------------------------")
    print()
    print ("\t\t\t a - Añadir un producto al inventario / Ver productos en inventario")
    print()
    print ("\t\t\t b - Salir")
    print()
    print ("\t\t\t------------------------")
    print()
    opcion_menu = input ("\t\t\t Selecciona una opcion:  ")

    if (opcion_menu == 'a'):
        flag_submenu = True
        while flag_submenu:
            os.system("cls")
            print ("\t\t\t##### SUBMENU ######")
            print ("\t\t\t------------------------")
            print()
            print ("\t\t\t 1 - Agregar un producto")
            print ("\t\t\t 2 - Productos ingresados")
            print ("\t\t\t 3 - Editar un producto ")
            print ("\t\t\t 4 - Valor total de todos los productos ingresados ")
            print ("\t\t\t 5 - Eliminar un producto")
            print ("\t\t\t 6 - Volver al menu principal")
            print()
            print ("\t\t\t------------------------")

            opcion_submenu = input("\t\t\t Ingrese una opcion submenu: ")
            if (opcion_submenu == '1'):
                print()
                insertar()
            elif (opcion_submenu == '2'):
                listar()
                input("\t\t\t Presione enter para continuar...")
            elif (opcion_submenu == '3'):
                modificar()
                input("\t\t\t Presione enter para continuar...")
            elif (opcion_submenu == '4'):
                sumar()
                input("\t\t\t Presione enter para continuar...")
            elif (opcion_submenu == '5'):
                eliminar()
                input("\t\t\t Presione enter para continuar...")
            elif (opcion_submenu == '6'):
                input("\t\t\t Has seleccionado volver. Presione enter para continuar...")
                flag_submenu = False
            else:
                print()
                print ("\t\t\t------------------------")
                input("\t\t\t OPCION INVALIDA. Presiona enter para continuar...")
                print ("\t\t\t------------------------")
                os.system("clear")

    elif (opcion_menu == 'b') :
        print("\t\t\t Has seleccionado Salir.")
        opcion = False
    else:
        print()
        print ("\t\t\t------------------------------------------------")
        input("\t\t\t Opcion no valida. Presione enter para continuar...")
        os.system("cls")


print("Welcome to the Python CLI Calculator tool...")

first_num = int(input("Enter first number: "))
second_num = int(input("Enter second number: "))
arith_type = int(input("1 to Add,\n2 to Subtract, \n3 to multiply, \n4 to Divide, \n5 to find remainder \n Enter an operation type: "))

def add():
  print(first_num, "added to", second_num, "is", first_num + second_num )

def subtract():
  print(first_num, "subtracts", second_num, "to give", first_num - second_num)

def divide():
  print(first_num, "divided by", second_num, "gives", first_num / second_num)

def multiply():
  print(first_num, "multiplied by", second_num, "gives",first_num * second_num)

def modulo():
   print(first_num, "divided by", second_num, "gives a remainder of",first_num % second_num)

if arith_type == 1:
  add()
elif arith_type == 2:
  subtract()
elif arith_type == 3:
   multiply()
elif arith_type == 4:
   divide()
elif arith_type == 5:
  modulo()
else:
  print("Invalid Arithmetic type")


import requests
from bs4 import BeautifulSoup

url = "https://www.daad.de/deutschland/studienangebote/studiengang/en/?a=result&q=&degree=&courselanguage=&locations=&admissionsemester=&sort=name&page=1"

first_page = requests.get(url)

first_page_soup = BeautifulSoup(first_page.text, 'html.parser')

hit = first_page_soup.find('p', class_='loading')

# course_name = hit.h3.a.text

print(hit.ul)


import pandas as pd
import numpy as np
import datetime
import glob
import os

def load_option(filename):
    """ Loads as a Series and parses dates automatically """
    df = pd.read_csv(filename, index_col=0, parse_dates=True, dayfirst=True)
    return df["Scada Output"]

def instantaneous_deriv(array):
    """ Return average time weighted deltas from an array """

    delta = array[0] - array[1:]
    time_weighted_delta = delta / np.arange(4, len(array)*4, 4)
    return time_weighted_delta.mean()

def get_max_stamp(series):
    """ Get the time stamp at the epoc point """
    return series.index[series.argmax()]

def determine_epoc(series, window=75, func=instantaneous_deriv):
    """ Determine the Epoc Location from a Series,

    Takes an optional window size and deriviative function in order to
    calculate. Default window size is set to 300 seconds

    Will return the time stamp of the maximum deriviative point

    """

    # Apply the deriv function over the window
    changes = pd.rolling_apply(series, window=window+1, func=func)
    changes = changes.dropna()

    # Shift the index back to the original location.
    changes.index = series.index[:-window]

    return get_max_stamp(changes)

def from_epoc(series, epoc):
    """ Performs a couple time manipulations in order to determine
    relative timings
    """
    df = pd.DataFrame(series)
    df.index.name = "Timestamp"
    df["Timestamp"] = df.index
    df["Epoc Time"] = df["Timestamp"] - epoc
    df["Epoc Seconds"] = np.array((df["Timestamp"] - epoc).values / 1000000000, dtype=np.int64)
    return df

def cumulative_deviation(df, epoc):

    df["Cumulative Deviation"] = df["Scada Output"] - df["Scada Output"].ix[epoc]
    return df

def instantaneous_deviation(df, epoc):
    cutter = lambda x: x[0] - x[1]
    df["Instantaneous Deviation"] = pd.rolling_apply(df["Scada Output"],
                                                     2, cutter)
    return df

def average_deviation(df, epoc):
    df["Average Deviation"] = (df["Scada Output"] - df["Scada Output"].ix[epoc]) / df["Epoc Seconds"]

    return df

def percent_epoc_max(df, epoc):
    df["Percentage of Epoc"] = df["Scada Output"] / df["Scada Output"].ix[epoc]
    return df

def percent_capacity(df, capacity):
    df["Percentage of Capacity"] = df["Scada Output"] / capacity
    return df

def cumulative_delta_capacity(df, epoc):
    df["Delta Capacity"] = df["Percentage of Capacity"] - df["Percentage of Capacity"].ix[epoc]

    return df

def farm_capacity(farm):

    capacities = {"West Wind": 135,
                  "Tararua": 66,
                  "All Tararua": 250,
                  "Te Apiti": 92,
                  "Tararua WC": 90,
                  "White Hill": 57,
                  "Mahinerangi": 36,
                  "Te Uku": 64,
                  "All": 590,
                  "North Island": 500,
                  "South Island": 95}

    return capacities[farm]

def get_farm_name(fName):

    base = os.path.basename(fName)
    rmext = os.path.splitext(base)[0]
    rmopt = rmext.split('_')[:-2]
    return ' '.join(rmopt)

def process_option(fName):

    series = load_option(fName)
    stamp = determine_epoc(series)

    farm_name = get_farm_name(fName)
    capacity = farm_capacity(farm_name)

    df = from_epoc(series, stamp)
    df = cumulative_deviation(df, stamp)
    df = instantaneous_deviation(df, stamp)
    df = average_deviation(df, stamp)
    df = percent_epoc_max(df, stamp)
    df = percent_capacity(df, capacity)
    df = cumulative_delta_capacity(df, stamp)

    return df

def recursive_method(masterdirectory):

    nd_name = os.path.join(masterdirectory, "metric_data")
    if not os.path.exists(nd_name):
        os.mkdir(nd_name)

    full_dir = os.path.join(masterdirectory, "option_data")

    for directory, subdir, files in os.walk(full_dir):
        if files:
            new_dir = directory.replace('option_data', 'metric_data')
            if not os.path.exists(new_dir):
                os.makedirs(new_dir)

            for f in files:
                abs_path = os.path.join(directory, f)
                save_path = abs_path.replace('option_data', 'metric_data')
                df = process_option(abs_path)
                df.to_csv(save_path, header=True, index=True)