from tkinter import *
from timeit import default_timer
from time import sleep
import signal
import sys
import time
from tkinter import filedialog
from PIL import Image
from PIL import ImageTk
import cv2
import imutils
import numpy as np
import glob
import os
import serial
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
plt.style.use('ggplot')
from PIL import ImageTk,Image
import serial, time, csv, ast,datetime
import numpy as np
import pandas as pd
from scipy import signal
from scipy.interpolate import lagrange
from scipy.fft import fft, fftfreq
from scipy import interpolate
import matplotlib
import datetime
from datetime import timedelta
import time
from os import remove

root = Tk()
root.geometry("1000x1000")
root.title("Medidor SER")

esp32 = serial.Serial('/dev/ttyUSB0', 230400, timeout=0.5)
esp32.flushInput()

if not os.path.exists('images/'):
    os.makedirs('images/')
"""
if not os.path.exists('images/fft'):
    os.makedirs('images/fft')

if not os.path.exists('images/fft/voltage'):
    os.makedirs('images/fft/voltage')

if not os.path.exists('images/fft/current'):
    os.makedirs('images/fft/current')

if not os.path.exists('images/señal'):
    os.makedirs('images/señal')

if not os.path.exists('images/señal/voltage'):
    os.makedirs('images/señal/voltage')
"""

font = {'family': 'serif',
        'color':  'darkred',
        'weight': 'normal',
        'size': 8,
        }

"""
folder = "images/señal/voltage/"
def BorrarArchivos():
      count1=0
      count2=0
      count3=0
      for f in os.listdir("images/señal/voltage/"):
                 #print(f)
                 count1=count1+1
      for f in os.listdir("images/fft/current/"):
                 #print(f)
                 count2=count2+1
      for f in os.listdir("images/fft/voltage/"):
                 #print(f)
                 count3=count3+1
        #print(count)
      try:
          if(count2>5):
               for i in os.listdir("images/señal/voltage"):
                    print(i)
                    os.remove(f'images/señal/voltage/{i}')
          if(count2>10):
               for i in os.listdir("images/fft/current"):
                    print(i)
                    os.remove(f'images/fft/current/{i}')
          if(count3>10):
               for i in os.listdir("images/fft/voltage"):
                    print(i)
                    os.remove(f'images/fft/voltage/{i}')

      except OSError:
               print("Imagen does not exists or is inaccessible")
               pass
"""

def BorrarArchivos():
      count1=0
      for f in os.listdir("images/"):
                 #print(f)
                 #print(count1)
                 count1=count1+1

      try:
          if(count1>10):
               for i in os.listdir("images/"):
                    #print(i)
                    os.remove(f'images/{i}')

      except OSError:
               print("Imagen does not exists or is inaccessible")
               pass

def graphVoltageCurrent(listVoltage,listCurrent,samplings): ##Grafica corriente y Voltaje
        global labelsfp
        global valuesvoltage
        global valuescurrent
        global maxvoltaje
        global minvoltaje
        #global maxcorriente
        #global mincorriente
        tiempo = 1/(samplings*(0.001/4200))
        tiempoms = np.arange(0,tiempo,tiempo/4096)
        my_formatted_list = [ '%.2f' % elem for elem in tiempoms ]

        #f = interpolate.interp1d(tiempoms, listVoltage)
        #xnew = np.arange(0, 4096, 5)  # 2550
        # print(f'largo xnew : {len(xnew)}')
        #ynew = f(xnew)
        valores = listVoltage#[200:4000]
        valores2 = listCurrent#[200:4000]
        #valores=round(val,1)
        valuesvoltage = [ i for i in valores ]
        valuescurrent = [ i for i in valores2 ]
        #labelsfp = [ i for i in range(0,len(valuesvoltage))]
        labelsfp =  [ i for i in my_formatted_list]

        maxvoltaje = max(valores)+300
        minvoltaje = min(valores)-300

        #plt.savefig(imagenVoltaje)
        #plt.close(fig)

def graphCurrent(list_fftVoltage, i):
       # y = np.linspace(0,len(list_fftVoltage),len(list_fftVoltage))
        i = str(i)
        x = list_fftVoltage*10
        print(f'corriente fase : {i}')
        plt.figure(figsize=(15,5))
        plt.plot(x)
        oldepoch = time.time()
        st = datetime.datetime.fromtimestamp(oldepoch).strftime('%Y-%m-%d-%H:%M:%S')
        plt.title("Corriente Fase"+i+".",fontdict=font)
        plt.ylabel("Corriente (mA-Peak-Peak)",fontdict=font)
        plt.xlabel("Tiempo(s)",fontdict=font)
        #print("images/señal/current"+i+"/"+st+"Current"+i+".png")
        plt.savefig("images/señal/current"+i+"/"+st+"Current.png")
        #plt.close(fig)

def graphVoltageFFT(list_fftVoltages,samplings):
    N = 4096
    T = 1 / samplings
    yf = fft(list_fftVoltages)
    Label(root, text=round(samplings,2),font=('Arial', 16)).grid(row=10, column=1)
    print(f'largo yf : {len(yf)}')
    xf = fftfreq(N, T)[:N] #tiene un largo de 4096
    print(f'largo xf : {len(xf)}')
    fig = plt.figure(figsize=(35,15))
    ejey = 20*np.log10(yf[:N])
    if (len(xf)>2550):
           f = interpolate.interp1d(xf, ejey)
           xnew = np.arange(0, 2550, 1) #2550
           print(f'largo xnew : {len(xnew)}')
           ynew = f(xnew)
           n = 0
           ax = fig.add_subplot(111)
           ax.plot(xnew,ynew)
           rangex = np.zeros(56)
           for h in range(0, 2600, 50):
             rangex[n]=h
             n = n+1
           ax.xaxis.set_ticks(rangex)
           ax.grid(True)
           plt.title("FFT Voltaje Fase 1",fontdict=font)
           ax.set_xlabel('Frecuencia (Hz)',fontdict=font)
           ax.set_ylabel('|dB|',fontdict=font)
           oldepoch = time.time()
           st = datetime.datetime.fromtimestamp(oldepoch).strftime('%Y-%m-%d-%H:%M:%S')
           plt.savefig("images/fft/voltage/"+st+"Voltage1.png")

           thd_array = []
           for i in range(46, 2596,50):
                a = np.amax(np.abs(ynew[i:i+8]))
                thd_array.append(a)
                #print(i)
                #print(a)
           sum = np.sum([thd_array[1:51]])
           raiz = np.sqrt(sum)
           thd_final = (raiz/thd_array[0])
           print(f'largo thd_array : {len(thd_array)}')
           print(f'thd_final : {thd_final}'+' %')
           Label(root, text=round(thd_final,2),font=('Arial', 16)).grid(row=6, column=1)

def graphCurrentFFT(list_fftVoltages,samplings):
    N = 4096
    T = 1 / samplings
    yf = fft(list_fftVoltages)
    xf = fftfreq(N, T)[:N]
    #print(f'largo xf : {len(xf)}')
    fig = plt.figure(figsize=(35,15))
    ejey = 20*np.log10(yf[:N])
    if (len(xf)>2550):
         f = interpolate.interp1d(xf, ejey)
         xnew = np.arange(0, 2550, 1)
         #print(f'largo xnew : {len(xnew)}')
         ynew = f(xnew)

         n = 0
         ax = fig.add_subplot(111)
         ax.plot(xnew,ejeyabsolut)
         rangex = np.zeros(56)
         for h in range(0, 2600, 50):
           rangex[n]=h
           n = n+1
         ax.xaxis.set_ticks(rangex)
         ax.grid(True)
         plt.title("FFT Corriente Fase"+i+".",fontdict=font)
         ax.set_xlabel('Frecuencia (Hz)',fontdict=font)
         ax.set_ylabel('|dB|',fontdict=font)
         oldepoch = time.time()
         st = datetime.datetime.fromtimestamp(oldepoch).strftime('%Y-%m-%d-%H:%M:%S')
         plt.savefig("images/fft/current"+i+"/"+st+"Current"+i+".png")

         thd_array = []
         for i in range(46, 2596,50):
              a = np.amax(np.abs(ynew[i:i+8]))
              thd_array.append(a)
              #print(i)
              #print(a)
         sum = np.sum([thd_array[1:51]])
         raiz = np.sqrt(sum)
         thd_final = (raiz/thd_array[0])

def getMaxValues(myList, quantity):
        return(sorted(list(set(myList)), reverse=True)[:quantity])
        #print(f'max : {max(myList)}')

def getMinValues(myList, quantity):
        return(sorted(list(set(myList)))[:quantity])
        #print(f'max : {max(myList)}')

vrms=0.0
def VoltRms(maximovoltaje2):
    if(maximovoltaje2<13):
        vrms=0
        print(f'vrms : {vrms}')
        return vrms
    vrms=maximovoltaje2*0.65
    return vrms

irms=0.0
def CurrentRms(maximocorriente2):
    if(maximocorriente2>430):
         irms = maximocorriente2*0.0133
    else:
         irms=(0.0046 + 0.000282*maximocorriente2 - 0.00000328*(maximocorriente2**2) + 0.0000000167*(maximocorriente2**3) - 0.0000000000382*(maximocorriente2**4) + 0.0000000000000322*(maximocorriente2**5))*maximocorriente2

    return irms

vrms0=0.0
def VoltajeRms(listVoltage):
    global vrms0
    N = len(listVoltage)
    Squares = []

    for i in range(0,N,1):    #elevamos al cuadrado cada termino y lo amacenamos
         listsquare = listVoltage[i]*listVoltage[i]
         Squares.append(listsquare)

    SumSquares=0
    for i in range(0,N,1):    #Sumatoria de todos los terminos al cuadrado
         SumSquares = SumSquares + Squares[i]

    MeanSquares = (1/N)*SumSquares #Dividimos por N la sumatoria

    vrms0=np.sqrt(MeanSquares)

    #print(f'Voltaje RMS : {vrms0}')
    return vrms0

irms0=0.0
def CorrienteRms(listCurrent):
    global irms0

    N = len(listCurrent)
    Squares = []

    for i in range(0,N,1):    #elevamos al cuadrado cada termino y lo amacenamos
         listsquare = listCurrent[i]*listCurrent[i]
         Squares.append(listsquare)

    SumSquares=0
    for i in range(0,N,1):    #Sumatoria de todos los terminos al cuadrado
         SumSquares = SumSquares + Squares[i]

    MeanSquares = (1/N)*SumSquares #Dividimos por N la sumatoria

    irms0=np.sqrt(MeanSquares)

    #print(f'Corriente RMS : {irms0}')
    return irms0

def CalculoDesfase(NoVoltageoffset,NoCurrentoffset,samplings):
    try:
        tiempo = 1/(samplings*(0.001/4200))
        tiempo2=(tiempo/4200)
        peakind = signal.find_peaks_cwt(NoVoltageoffset,np.arange(1,100))
        peakind2 = signal.find_peaks_cwt(NoCurrentoffset,np.arange(1,100))
        peakindvoltajems = peakind* tiempo2
        peakindcorrientems = peakind2* tiempo2
        dif=(peakindvoltajems-peakindcorrientems)
        print(np.mean(dif[1:10]/8.333))
    except:
        pass

DATVoltajeCGE=0.0
phasevoltajeCGE=0.0
FDVoltajeCGE=0.0

def VoltageFFT(list_fftVoltages, samplings,i):
    global j
    j = str(i)
    global DATVoltajeCGE1
    global phasevoltajeCGE
    global FDVoltajeCGE
    global ejeyabsolutv
    global xnewv
    #global FaseArmonicoFundamentalVoltaje
    N = len(list_fftVoltages)
    T = 1 / samplings
    list_fftVoltages -= np.mean(list_fftVoltages)
    datosfft = list_fftVoltages * np.hamming(4096)

    yf = np.fft.rfft(datosfft)
    #yf = fft(list_fftVoltages)
    #ejeyfase =  2.0/N * np.abs(yf[:50])
    #index_max2 = np.argmax(ejeyfase[:50])
    #a2 = yf[index_max2]/2048
    xf = fftfreq(N, T)[:N//2]  # tiene un largo de 4096
    #ejey = 2.0/N * np.abs(yf[:N//2])
    if (samplings > 5100):
           #f = interpolate.interp1d(xf, ejey)
           f = interpolate.interp1d(xf, yf[:N//2] )
           xnewv = np.arange(0, 2575, 1)  # 2550
           # print(f'largo xnew : {len(xnew)}')
           ynew = f(xnewv)
           ejeyabsolutv =  2.0/4096 * np.abs(ynew)#ynew
           #n = 0
           #fig = plt.figure(figsize=(7,2))
           #ax = fig.add_subplot(3,1,3)
           #ax.plot(xnew,ejeyabsolut)
           #rangex = np.zeros(28)
           #for h in range(50, 2600, 100):
           #  rangex[n]=h
           #  n = n+1
           #ax.xaxis.set_ticks(rangex)
           #ax.grid(True)
           #plt.title("FFT Voltaje",fontdict=font)
           #ax.set_xlabel('Frecuencia (Hz)',fontdict=font)
           #ax.set_ylabel('|dB|',fontdict=font)
           #oldepoch = time.time()
           #st = datetime.datetime.fromtimestamp(oldepoch).strftime('%Y-%m-%d-%H:%M:%S')
           #imagenVoltajeFFT = f'images/fft/voltage/{st}-Voltagefft.png'
           #plt.savefig(imagenVoltajeFFT)
           #load = Image.open(imagenVoltajeFFT)
           #renderfftVoltage = ImageTk.PhotoImage(load)
           #Label(root, image=renderfftVoltage).grid(column=2, row=13, rowspan=6)
           FD = []
           complejo = []
           real=[]
           imag=[]
           #dccomponent = max(ynew[0:10])
           z=0
           for i in range(45, 2575, 50):
                 a2 = max(ynew[i:i+10])
                 arra = max(ejeyabsolutv[i:i+10])
                 complejo.append(a2)
                 #index_max = np.argmax(ejeyabsolut[i-10:i+20])
                 #print(f'a : {a}')
                 #a = ynew[i+index_max]
                 real1 = a2.real
                 real.append(real1)
                 imag1 = a2.imag
                 imag.append(imag1)
                 #radiani = np.arctan(real1/imag1)
                 #degrees = math.degrees(radians)
                 #print(f'index max2 : {i+index_max}')
                 z=z+1
                 FD.append(arra)
                 #print(f'Armonico numero:{z} {i+index_max} + magnitud de {arra} + magnitud2 {abs(ynew[i+index_max])} + forma rectangular de {a} o {a*2/N} y radianes : {np.angle(a)}')
                 #print(f'Armonico corriente numero: {z} =>  {round(arra,3)} + {a2} + .. + {round(radiani,4)})')
                 #print(f'Armonico corriente numero: {z} =>  {round(arra,3)}')

           FD2=[]
           for i in range(0,len(FD)):
               if(FD[i]>(FD[0]/10)):
                   FD2.append(FD[i])

           SumMagnitudEficaz = (np.sum([FD[0:len(FD)]]))
           #print(f'Vrms total: {round(SumMagnitudEficaz,2)}')
           Magnitud1 = FD[0]
           if(j=="1"):
                 global sincvoltaje1
                 phasevoltajeCGE = np.arctan(real[0]/(imag[0]))
                 #FaseArmonicoFundamentalVoltaje1=round(np.angle(complejo[0]),2)
                 FDVoltajeCGE = round(Magnitud1/SumMagnitudEficaz,2)
                 DATVoltajeCGE1= round(np.sqrt(((SumMagnitudEficaz**2)-(Magnitud1**2))/(Magnitud1**2)),2)
                 print(f'DAT Voltaje CGE: {round(DATVoltajeCGE1,2)}')
                 sincvoltaje1 = 1

DATCorrienteCGE = 0.0
FDCorrienteCGE= 0.0
FDCorrienteCGE= 0.0
phasecorrienteCGE= 0.0
FPCGE= 0.99
FPCGE0= 0.99
cosphiCGE= 0.0
FDCorrienteCGE1=0.0
DATCorrienteCGE1=0.0

def CurrentFFT(list_fftVoltages, samplings, i,irms):
    global DATCorrienteCGE
    global DATCorrienteCGE1
    global a2
    global FDCorrienteCGE
    global FDCorrienteCGE1
    global phasecorrienteCGE
    global FPCGE
    global FPCGE0
    global cosphiCGE
    global q
    global renderfftcurrent
    global xnew
    global ejeyabsolut

    q = str(i)
    N = len(list_fftVoltages)
    T = 1 / samplings
    list_fftVoltages -= np.mean(list_fftVoltages)
    datosfft = list_fftVoltages * np.hamming(4096)#np.kaiser(N,100)
    yf = np.fft.rfft(datosfft)
    #yf=fft(list_fftVoltages)
    #if (g == 1):
    #     print(f'Sampling corriente 1: {round(samplings,2)}')

    xf = fftfreq(N, T)[:N//2]
    if (samplings > 5100):
         f = interpolate.interp1d(xf,yf[:N//2])
         xnew = np.arange(0, 2575, 1)
         ynew = f(xnew)
         ejeyabsolut =  2.0/N * np.abs(ynew)
         #n = 0
         #fig = plt.figure(figsize=(7,2))
         #ax = fig.add_subplot(3,1,2)
         #ax.plot(xnew,ejeyabsolut)
         #rangex = np.zeros(28)
         #for h in range(50, 2600, 100):
         #  rangex[n]=h
         #  n = n+1
         #ax.xaxis.set_ticks(rangex)
         #ax.grid(True)
         #plt.title(f'FFT Corriente | DAT: {DATCorrienteCGE1}, FD: {FDCorrienteCGE1}',fontdict=font)
         #ax.set_xlabel('Frecuencia (Hz)',fontdict=font)
         #ax.set_ylabel('|dB|',fontdict=font)
         #oldepoch = time.time()
         #st = datetime.datetime.fromtimestamp(oldepoch).strftime('%Y-%m-%d-%H:%M:%S')
         #imagenCurrentFFT = f'images/fft/current/{st}-Currentfft.png'
         #plt.savefig(imagenCurrentFFT)
         #load = Image.open(imagenCurrentFFT)
         #renderfftcurrent = ImageTk.PhotoImage(load)
         #Label(root, image=renderfftcurrent).grid(column=2, row=7, rowspan=6)
         #p = int(i)
         #z=0
         FD= []
         #dccomponent = max(ynew[0:10])
         complejo = []
         real=[]
         imag=[]
         #dccomponent = max(ynew[0:10])
         for i in range(45, 2575, 50):
               a2 = max(ynew[i:i+10])
               arra = max(ejeyabsolut[i:i+10])
               complejo.append(a2)
               #index_max = np.argmax(ejeyabsolut[i-10:i+20])
               #print(f'a : {a}')
               #a = ynew[i+index_max]
               real1 = a2.real
               real.append(real1)
               imag1 = a2.imag
               imag.append(imag1)
               #radiani = np.arctan(real1/imag1)
               #degrees = math.degrees(radians)
               #print(f'index max2 : {i+index_max}')
               #z=z+1
               FD.append(arra)
               #print(f'Armonico numero:{z} {i+index_max} + magnitud de {arra} + magnitud2 {abs(ynew[i+index_max])} + forma rectangular de {a} o {a*2/N} y radianes : {np.angle(a)}')
               #print(f'Armonico corriente numero: {z} =>  {round(arra,3)} + {a2} + .. + {round(radiani,4)})')
         #FD2=[]
         #for i in range(0,len(FD)):
         #    if(FD[i]>(FD[0]/10)):
         #        FD2.append(FD[i])

         #print(f'FD2: {FD2}')
         #print(f'FD largo: {len(FD)}')
         SumMagnitudEficaz = (np.sum([FD[0:len(FD)]]))*0.01

         Magnitud1 = FD[0]*0.01
         ArmonicosRestantes=SumMagnitudEficaz-Magnitud1
         #print(f'Irms armonico 1 {q}: {round(Magnitud1,2)}')
         proporcion = irms/(np.sqrt(Magnitud1**2+ArmonicosRestantes**2))
         irmsarmonico1prop=Magnitud1*proporcion
         #irmstotalproporcionado=np.sqrt((irmsarmonico1prop**2)+(ArmonicosRestantes*proporcion)**2)
         #MagnitudArmonicoFundamentalCorriente=round(thd_array[0],3)
         #fp2=round((armonico1corriente*np.cos(phasevoltaje-phasen))/valor1,2)
         #FaseArmonicoFundamentalCorriente=round(np.angle(complejo[0]),2)

         #GradoArmonicoFundamentalCorriente=round(Grados,2)
         if(q=="1"):
             global sincvoltaje1
             FDCorrienteCGE1 = round(irmsarmonico1prop/irms,2)
             print(f'FDCorrienteCGE : {round(FDCorrienteCGE1,2)}')
             DATCorrienteCGE1 = np.sqrt((SumMagnitudEficaz**2-Magnitud1**2)/(Magnitud1**2))
             print(f'DAT corriente CGE: {round(DATCorrienteCGE1,2)}')
             phasecorrienteCGE = np.arctan(real[0]/(imag[0]))
             if (sincvoltaje1 == 1):
                 FPCGE0=np.cos(phasevoltajeCGE-phasecorrienteCGE)*FDCorrienteCGE1
                 print(f'Desfase voltaje: {phasevoltajeCGE}')
                 print(f'Desfase corriente: {phasecorrienteCGE}')
                 print(f'Desfase: {phasevoltajeCGE-phasecorrienteCGE}')
                 cosphiCGE=np.cos(phasevoltajeCGE-phasecorrienteCGE)
                 #if(FPCGE0>0.0):
                 #    FPCGE0=FPCGE0+0.05
                 #else:
                 #    FPCGE0=FPCGE0-0.05
                 #if(FPCGE0>=1.0):
                 #    FPCGE0=0.99
                 #if(FPCGE0<=-1.0):
                 #    FPCGE0=-0.99

                 #FP=np.cos(FaseArmonicoFundamentalVoltaje-FaseArmonicoFundamentalCorriente)
                 print(f'FP1 cge: {round(FPCGE0,2)}')
                 print(f'cos(phi) cge : {round(cosphiCGE,2)}')
                 sincvoltaje1=0
                 #return FPCGE
         #sincvolaje1=0

def graphVoltage(list_fftVoltage,list_FPCurrent,samplings):
        global ax
        #y = np.linspace(0,len(list_fftVoltage),1000)
        #x = len(list_fftVoltage)
        #print(f'largo : {x}')
        global render
        #plt.figure(figsize=(12,2))
        #plt.plot(list_fftVoltage)
        fig=plt.figure(figsize=(12,8))
        #plt.plot(list_fftVoltage, color="blue", label="Voltaje")
        #plt.plot(list_FPCurrent, color="green", label="Corriente")
        #plt.legend(loc='upper left')
        tiempo = 1/(samplings*(0.001/4200))
        tiempoms = np.arange(0,tiempo,tiempo/4096)

        ax = fig.add_subplot(5,1,1)
        ax.plot(tiempoms,list_fftVoltage,color="blue", label="Voltaje")
        ax.plot(tiempoms,list_FPCurrent,color="green", label="Corriente")
        ax.legend(loc='upper left')
        ax.set_xlabel('Tiempo (mS)',fontdict=font)
        plt.title(f'Voltaje y Corriente | V: {round(vrms1,2)}    |    I: {round(irms1,2)}     |     P-Activa: {round(ActivaCGEFase11,2)}   |    P-Aparente: {round(AparenteCGEFase11,2)}    |     P-Reactiva:{round(ReactivaCGEFase11,2)}    |    FP: {round(FPCGE0,2)}',fontdict=font)

        ax = fig.add_subplot(3,1,3)
        plt.title(f'FFT Corriente | DAT: {round(DATCorrienteCGE1,2)}, FD: {round(FDCorrienteCGE1,2)}',fontdict=font)
        ax.plot(xnew,ejeyabsolut)
        rangex = np.zeros(28)
        n=0
        for h in range(50, 2600, 100):
           rangex[n]=h
           n = n+1
        ax.xaxis.set_ticks(rangex)
        ax.grid(True)
        ax.set_xlabel('Frecuencia (Hz)',fontdict=font)
        #ax.set_ylabel('|dB|',fontdict=font)

        ax = fig.add_subplot(5,1,5)
        ax.plot(xnewv,ejeyabsolutv)
        rangex = np.zeros(28)
        n=0
        for h in range(50, 2600, 100):
           rangex[n]=h
           n = n+1
        ax.xaxis.set_ticks(rangex)
        ax.grid(True)
        ax.set_xlabel('Frecuencia (Hz)',fontdict=font)
        #ax.set_ylabel('|dB|',fontdict=font)

        oldepoch = time.time()
        st = datetime.datetime.fromtimestamp(oldepoch).strftime('%Y-%m-%d-%H:%M:%S')
        #plt.xlabel("Tiempo(ms)",fontdict=font)
        plt.title(f'FFT Voltaje |    DAT: {DATVoltajeCGE1}     |     FD: {FDVoltajeCGE}    |   cos phi: {round(cosphiCGE,2)}',fontdict=font)
        ax.set_xlabel('Frecuencia (Hz)',fontdict=font)
        #ax.set_ylabel('Pk-Pk',fontdict=font)
        imagenVoltaje = f'images/{st}.png'
        plt.savefig(imagenVoltaje)
        load = Image.open(imagenVoltaje)
        render = ImageTk.PhotoImage(load)
        Label(root, image=render).grid(column=2, row=1, rowspan=6)

a = datetime.datetime.now()
#energyCGEFase11 = 0.0
energyCGEFase11 = 0.0
energyCGEFase11Hour = 0.0
AparenteCGEFase11 = 0.0
ActivaCGEFase11 = 0.0
ReactivaCGEFase11 = 0.0
energyCGEFase1 = 0.0
AparenteCGEFase1 = 0.0
ActivaCGEFase1 = 0.0
ReactivaCGEFase1 = 0.0

def Potencias(irms,vrms):
          global a
          global energyCGEFase1
          global ActivaCGEFase1
          global AparenteCGEFase1
          global ReactivaCGEFase1
          global energyCGEFase11
          global energyCGEFase11Hour
          global AparenteCGEFase11
          global ActivaCGEFase11
          global ReactivaCGEFase11
          AparenteCGEFase11 = vrms*irms
          print(f'Energia CGE: {AparenteCGEFase11}')
          ActivaCGEFase11 = np.abs(vrms*irms*cosphiCGE)
          print(f'Activa CGE: {ActivaCGEFase11}')
          ReactivaCGEFase11 = vrms*irms*np.sin(phasevoltajeCGE-phasecorrienteCGE)
          print(f'Reactiva CGE: {ReactivaCGEFase11}')
          a2 = datetime.datetime.now()
          delta=(((a2 - a).microseconds)/1000+((a2 - a).seconds)*1000)/10000000000
          energyCGEFase11 += ActivaCGEFase11*delta*2.9
          energyCGEFase11Hour += ActivaCGEFase11*delta*2.9
          a = datetime.datetime.now()
          if(a2.minute==0 or a2.minute==1):
              energyCGEFase11Hour=0
          if(a2.hour==0 and a2.minute==0):
              energyCGEFase11=0
          if(a2.hour==0 and a2.minute==1):
              energyCGEFase11=0
          print(f'Energia CGE: {energyCGEFase11}')

vrms1=0.0
vrms2=0.0
vrms3=0.0
irms1=0.0
irms2=0.0
irms3=0.0
vrms11=0.0
vrms22=0.0
vrms33=0.0
irms11=0.0
irms22=0.0
irms33=0.0
modamaximovoltaje11=[]
modamaximocorriente11=[]
modamaximovoltaje22=[]
modamaximocorriente22=[]
modamaximovoltaje33=[]
modamaximocorriente33=[]

def sin_wave(A, fs, N,phi):

    x = np.arange(N)
    y = A*np.sin(2*np.pi*50*x/fs + phi)
    #plt.plot(x, y)
    #plt.xlabel('sample(n)')
    #plt.ylabel('voltage(V)')
    #plt.show()
    return y

def received():
    while True:
            try:
                esp32_bytes = esp32.readline()
                decoded_bytes = str(esp32_bytes[0:len(esp32_bytes)-2].decode("utf-8"))#utf-8
            except:
                print("Error en la codificación")
                continue

            np_array = np.fromstring(decoded_bytes, dtype=float, sep=',')
            #print(f'{len(np_array)}')
            if (len(np_array) == 8402):
                       #print("..")
                       if (np_array[0] == 11):
                            global modamaximovoltaje11
                            global modamaximocorriente11
                            global vrms1
                            global vrms11
                            global irms1
                            global irms11
                            global modavoltaje
                            global modacorriente
                            global samplings
                            #samplings=30000
                            samplings = np_array[-1]
                            list_FPVoltage3 = np_array[0:4200]
                            list_FPCurrent3 = np_array[4201:8400]
                            #list_FPVoltage3 = sin_wave(A=320, fs=samplings, N=4200, phi=0)#np_array[0:4200]
                            #list_FPCurrent3 = sin_wave(A=320, fs=samplings,N=4200, phi=4.18) #np_array[4201:8400]
                            #print(f'max inicio: {max(list_FPVoltage3)}')
                            sos = signal.butter(10, 2500, 'low', fs=samplings, output='sos')
                            list_FPVoltage2 = signal.sosfilt(sos, list_FPVoltage3)
                            #list_FPVoltage2 = savgol_filter(list_FPVoltage2,len(list_FPVoltage2)-1,))
                            #sos = signal.butter(4, 50, 'low', fs=samplings, output='sos')
                            list_FPCurrent2 = signal.sosfilt(sos, list_FPCurrent3)
                            #print(f'max inicio con filtro: {max(list_FPVoltage2)}')
                            list_FPVoltage = list_FPVoltage2[104:4200]
                            list_FPCurrent = list_FPCurrent2[103:4200]
                            #Valor dc de Voltaje

                            valoresmaximovoltajesinmedia=getMaxValues(list_FPVoltage, 50)
                            valoresminimovoltajesinmedia=getMinValues(list_FPVoltage, 50)
                            maximovoltaje = np.median(valoresmaximovoltajesinmedia)
                            minimovoltaje = np.median(valoresminimovoltajesinmedia)
                            mediadcvoltaje = (maximovoltaje+minimovoltaje)/2
                            # Valores maximo y minimos de voltaje sin componente continua
                            NoVoltageoffset=list_FPVoltage-mediadcvoltaje

                            vrms1=VoltajeRms(NoVoltageoffset)

                            if (len(modamaximovoltaje11)>=5):
                                modavoltaje=np.median(modamaximovoltaje11)
                                vrms1=VoltRms(modavoltaje)
                                print(f'Vrms CGE: {vrms1}')
                                VoltageFFT(NoVoltageoffset,samplings,1)

                                modamaximovoltaje11=[]
                            else:
                                modamaximovoltaje11.append(vrms1)
                                #print(f'array voltaje: {modamaximovoltaje2
                            #Valor dc de corriente

                            valoresmaxcorriente=getMaxValues(list_FPCurrent, 50)
                            valoresmincorriente=getMinValues(list_FPCurrent, 50)
                            maximocorriente = np.median(valoresmaxcorriente)
                            minimocorriente = np.median(valoresmincorriente)
                            mediadccorriente = (maximocorriente+minimocorriente)/2

                            # Valores maximo y minimos de corriente
                            NoCurrentoffset=list_FPCurrent-mediadccorriente

                            irms1=CorrienteRms(NoCurrentoffset)

                            if (len(modamaximocorriente11)>=5):
                                modacorriente=np.median(modamaximocorriente11)
                                irms1=CurrentRms(modacorriente)
                                print(f'Irms CGE: {irms1}')
                                CurrentFFT(NoCurrentoffset,samplings,1,irms1)
                                #print(f'MODA CORRIENTE CGE: {modacorriente}')
                                Potencias(irms1,vrms1)
                                modamaximocorriente11=[]
                                graphVoltage(NoVoltageoffset,NoCurrentoffset,samplings)
                                CalculoDesfase(NoVoltageoffset,NoCurrentoffset,samplings)
                                #BorrarArchivos()
                            else:
                                modamaximocorriente11.append(irms1)
                            #    print(f'array corriente: {modamaximocorriente2}')
            #Label(root, text=round(vrms1,2),font=('Arial', 16)).grid(row=1, column=1)
            #Label(root, text=round(irms1,2),font=('Arial', 16)).grid(row=2, column=1)
            #Label(root, text=round(energyCGEFase11,2),font=('Arial', 16)).grid(row=3, column=1)
            #Label(root, text=round(ActivaCGEFase11,4),font=('Arial', 16)).grid(row=4, column=1)
            #Label(root, text=round(AparenteCGEFase11,2),font=('Arial', 16)).grid(row=5, column=1)
            #Label(root, text=round(ReactivaCGEFase11,2),font=('Arial', 16)).grid(row=6, column=1)
            #Label(root, text=round(FPCGE0,2),font=('Arial', 16)).grid(row=7, column=1)
            #Label(root, text=round(cosphiCGE,2),font=('Arial', 16)).grid(row=8, column=1)

                            BorrarArchivos()
                            root.after(500, received)

#show_imagen_voltaje()
#show_imagen_voltaje_fft()
#lblInputImage.image = ""
#lblInputImage2.image = ""

"""
lblInputImage = Label(root)
lblInputImage.grid(column=2, row=1, rowspan=6)
lblInputImage2 = Label(root)
lblInputImage2.grid(column=2, row=8, rowspan=6)
"""
selected = IntVar()

#my_buttonCurrentFFT = Button(root, text="FFT Corriente", command=graphCurrentFFT)
#my_buttonCurrentFFT.grid(row=4,column=2)

#Label(root, text="Voltaje",font=('Arial', 16)).grid(row=1, column=0)
#Label(root, text="Corriente",font=('Arial', 16)).grid(row=2, column=0)
#Label(root, text="Energia",font=('Arial', 16)).grid(row=3, column=0)
#Label(root, text="Activa",font=('Arial', 16)).grid(row=4, column=0)
#Label(root, text="Aparente",font=('Arial', 16)).grid(row=5, column=0)
#Label(root, text="Reactiva",font=('Arial', 16)).grid(row=6, column=0)
#Label(root, text="FP",font=('Arial', 16)).grid(row=7, column=0)
#Label(root, text="cos phi",font=('Arial', 16)).grid(row=8, column=0)

root.after(500, received)
root.mainloop()


#encoding: utf8

import functools
import copy

from rejit.common import RejitError
from rejit.common import special_chars

from rejit.nfa import NFA
from rejit.dfa import DFA
from rejit.jitmatcher import JITMatcher

class RegexError(RejitError): pass

class RegexParseError(RegexError): pass

class RegexCompilationError(RegexError): pass

class RegexMatcherError(RegexError): pass

class Regex:
    def __init__(self, pattern=None):
        self.pattern = pattern
        self._ast = None
        self._final_ast = None
        self._matcher = None
        self._matcher_type = 'None'
        if self.pattern is not None:
            self._ast = self._parse(pattern)
            self._final_ast = self._transform(self._ast)
            self._matcher = self._compile(self._final_ast)
            self._matcher_type = 'NFA'

    def accept(self, s):
        if self._matcher:
            return self._matcher.accept(s)
        raise RegexMatcherError("No matcher found")

    def get_matcher_description(self):
        if self._matcher:
            return self._matcher.description
        raise RegexMatcherError("No matcher found")

    @property
    def description(self):
        return self.get_matcher_description()

    def compile_to_DFA(self):
        if self._matcher_type == 'DFA':
            return
        if self._matcher_type != 'NFA':
            raise RegexCompilationError(
                    "Can only compile NFA-type matcher to a DFA. Current matcher type: {}".format(self._matcher_type))
        self._matcher = DFA(self._matcher)
        self._matcher_type = 'DFA'

    def compile_to_x86(self):
        if self._matcher_type == 'JIT':
            return
        self.compile_to_DFA()
        self._matcher = JITMatcher(self._matcher)
        self._matcher_type = 'JIT'

    def _getchar(self):
        if self._input:
            self._last_char = self._input[0]
            self._input = self._input[1:]
        else:
            self._last_char = ''

    def _parse(self, pattern):
        self._input = pattern
        self._last_char = ''
        self._getchar()
        if not self._last_char:
            return ('empty',)
        else:
            ast = self._unionRE()
            if self._last_char == ')':
                raise RegexParseError('Unmatched parentheses')
            return ast

    def _compile(self, ast):
        if ast[0] == 'concat':
            return NFA.concat_many(list(map(self._compile, ast[1])))
        elif ast[0] == 'union':
            return NFA.union_many(list(map(self._compile, ast[1])))
        elif ast[0] == 'kleene-star':
            return NFA.kleene(self._compile(ast[1]))
        elif ast[0] == 'kleene-plus':
            return NFA.kleene_plus(self._compile(ast[1]))
        elif ast[0] == 'zero-or-one':
            return NFA.zero_or_one(self._compile(ast[1]))
        elif ast[0] == 'any':
            return NFA.any()
        elif ast[0] == 'empty':
            return NFA.empty()
        elif ast[0] == 'symbol':
            return NFA.symbol(ast[1])
        elif ast[0] == 'set':
            return NFA.char_set(ast[1],ast[2])
        raise RegexCompilationError("Unknown AST node: {node}".format(node=ast))

    def _transform(self, input_ast):
        return functools.reduce(
            lambda ast, transform: transform(ast),
            [
                functools.partial(self._flatten_nodes,'concat'),
                functools.partial(self._flatten_nodes,'union'),
                self._simplify_quant,
            ],
            input_ast)

    def _flatten_nodes(self, node_type, ast):
        # for a list of nodes return a list of transformed nodes
        if isinstance(ast, list):
            return list(map(functools.partial(self._flatten_nodes,node_type), ast))
        # for leaf nodes return a copy
        if ast[0] in ['any','empty','symbol','set']:
            return copy.deepcopy(ast)
        # for nodes with children return node with its children transformed by `_flatten_nodes`
            # for tuple based node ast[1:] are children
            # ('type', _flatten(child1), _flatten(child2))
            # for list based node ast[1] is a list of children
            # ('type', [ _flatten(child1), _flatten(child2)]
        if ast[0] != node_type:
            return tuple([ast[0]] + list(map(functools.partial(self._flatten_nodes,node_type), ast[1:])))
        # for `concat` node transform children with `flatten_nodes`
        left = self._flatten_nodes(node_type,ast[1][0])
        right = self._flatten_nodes(node_type,ast[1][1])
        # `concat` node list is created from lists extracted from children `concat` nodes, or by simply inserting other nodes
        node_list = (left[1] if left[0] == node_type else [left]) + (right[1] if right[0] == node_type else [right])
        return (node_type , node_list)

    def _simplify_quant(self, ast):
        quant_nodes = {'kleene-star','kleene-plus','zero-or-one'}
        if isinstance(ast, list):
            return list(map(self._simplify_quant, ast))
        if ast[0] in ['any','empty','symbol','set']:
            return copy.deepcopy(ast)
        if ast[0] not in quant_nodes:
            return tuple([ast[0]] + list(map(self._simplify_quant, ast[1:])))
        child = self._simplify_quant(ast[1])
        if child[0] in quant_nodes:
            if child[0] == ast[0] == 'kleene-plus':
                return ('kleene-plus', child[1])
            elif child[0] == ast[0] == 'zero-or-one':
                return ('zero-or-one', child[1])
            else:
                return ('kleene-star', child[1])
        else:
            return (ast[0], child)

    def _unionRE(self):
        ast1 = self._concatRE()
        if self._last_char == '|':
            self._getchar() # '|'
            ast2 = self._unionRE()
            return ('union',[ast1,ast2])
        return ast1

    def _concatRE(self):
        ast1 = self._kleeneRE()
        if self._last_char and self._last_char not in '|)':
            ast2 = self._concatRE()
            return ('concat', [ast1, ast2])
        return ast1

    def _kleeneRE(self):
        ast = self._elementaryRE()
        if self._last_char == '*':
            self._getchar() # '*'
            return ('kleene-star', ast)
        elif self._last_char == '+':
            self._getchar() # '+'
            return ('kleene-plus', ast)
        elif self._last_char == '?':
            self._getchar() # '?'
            return ('zero-or-one', ast)
        else:
            return ast

    def _elementaryRE(self):
        if self._last_char == '(':
            self._getchar()
            ast_paren = self._unionRE()
            if self._last_char != ')':
                raise RegexParseError('Expected ")", got {}'.format(self._last_char))
            self._getchar() # ')'
            return ast_paren
        elif self._last_char == '.':
            self._getchar() # '.'
            return ('any',)
        elif self._last_char == '[':
            return self._parse_charset()
        elif self._last_char == '':
            raise RegexParseError('Unexpected end of the pattern')
        else:
            return self._symbolRE()

    def _symbolRE(self):
        if self._last_char in special_chars and self._last_char != '\\':
            raise RegexParseError('Unescaped special character "{}" can\'t be used here'.format(self._last_char))
        if self._last_char == "\\":
            self._getchar() # '\'
        if not self._last_char:
            raise RegexParseError('Unexpected end of the pattern after an escape character "\\"')
        ast = ('symbol', self._last_char)
        self._getchar()
        return ast

    def _parse_charset(self):
        self._getchar() # '['
        symbol_list = []
        charset_desc = '['
        if self._last_char == '^':
            raise RegexParseError('Negative character set not supported')
        while self._last_char and self._last_char != ']':
            symbol1 = self._last_char
            self._getchar()
            if self._last_char == '-':
                self._getchar() # '-'
                if self._last_char:
                    charset_desc += symbol1 + '-'
                    symbol_list += list(Regex.char_range(symbol1,self._last_char))
                    charset_desc += self._last_char
                    self._getchar()
                else:
                    raise RegexParseError('Expected a symbol after "-" but the end of the pattern reached')
            else:
                charset_desc += symbol1
                symbol_list.append(symbol1)
        if self._last_char != ']':
            raise RegexParseError('Expected "]" but end of the pattern reached'.format(self._last_char))
        self._getchar() # ']'
        charset_desc += ']'
        ast = ('set',symbol_list,charset_desc)
        return ast

    @staticmethod
    def char_range(c1, c2):
        """Generates the characters from `c1` to `c2`, inclusive."""
        for c in range(ord(c1), ord(c2)+1):
            yield chr(c)


#!/usr/bin/env python

#coding: utf8

import handler

class Goodbot(handler.Handler):
	def privmsg(self, words):
		line = ' '.join(words)
		msg = line.split(':')[2]
		msg_words = msg.split(' ')
		nick = line.split(':')[1].split('!')[0]
		target = words[2]

		if target.find('#') != 0:
			target = nick

		if self.commands.getcmd(msg_words[0], 'goodbot'):
			try:
				count = int(self.commands.getvar("goodbot"))+1
			except TypeError:
				count = 1
			self.commands.setvar("goodbot", count)
			self.commands.msg("mod_goodbot_thanks", target, nick)
		elif self.commands.getcmd(msg_words[0], 'goodies') == 1:
			try:
				count = int(self.commands.getvar("goodbot"))
			except TypeError:
				count = 0
			self.commands.privmsg(target, self.commands.msg("mod_goodbot_goodies", getmsg=True)[0] % (nick, count))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on 2.22 2021
auther @ddecadall
"""
import logging
import sys
import shutil
import hashlib
import re
import zipfile

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

import os
from libs.modules.BaseModule import BaseModule

logging.basicConfig(stream=sys.stdout, format="%(levelname)s: %(message)s", level=logging.INFO)
log = logging.getLogger(__name__)

class AppMachine(BaseModule):
    const_key_seed_str = "http://mobile.appmachine.com/mobilewebservice.asmx"
    key_list = []

    def generateKey(self, key_seed_str):
        key_list = [0x0,0x0,0x0,0x0]
        num = 0
        for tmp_i in range(len(key_seed_str)):
            tmp_char = key_seed_str[tmp_i]
            if (((tmp_char.isalnum())|(tmp_char == "_")|(tmp_char == " "))&((tmp_char != " ")|((tmp_i<=0)|(key_seed_str[tmp_i-1]!=" ")))):
                num3 = self._limit_to_int32((num & 3)<<3)
                num4 = self._limit_to_int32(0xff << (num3 & 0x1f))
                num2 = ((key_list[(num & 0xf) >> 2] & num4)>>(num3 & 0x3f)) & 0xff
                num2 = (((num2 << 1)^(num2 >> 1 )) ^ ord(tmp_char.lower())) & 0xff
                tmp_num = key_list[(num & 0xf)>>2]
                key_list[(num & 0xf)>>2] = tmp_num & self._limit_to_int32((~num4)&0xff+0xffffff00)
			    #print(key_list[(num & 0xf)>>2])

                num5 = self._limit_to_int32((num2 << (num3 & 0x1f))& num4)|(key_list[(num & 0xf) >>2])
			    #print(hex(num5))
                key_list[(num & 0xf)>>2] = num5
                num +=1
			    #print(key_list)
        return key_list

    def convertByteListToIntList(self, target_byte_list):
        byte_length = len(target_byte_list)
        #print(target_byte_list)
        int_list = []
        for tmp_i in range(byte_length // 4):
            tmp_int = self._convert_list_to_int(target_byte_list[tmp_i*0x4:(tmp_i+1)*0x4])
            #print(target_byte_list[tmp_i * 0x4:0x4])
            #print(tmp_int)
            int_list.append(tmp_int)
        return int_list

    def decryptFile(self, enfile):
        enc_file = open(enfile,"rb")
        print(enfile)
        # get encrypted file length
        enc_file_content = enc_file.read()
        enc_file_length = self._convert_list_to_int(enc_file_content[:4])
        enc_file_content = enc_file_content[4:]
        result_byte_list = []

        remain_file_size = len(enc_file_content)
        # split file by 0x4000 byte , each block convert to int list and decrypt it
        while remain_file_size > 0 :
            if remain_file_size > 0x4000:
                remain_file_size = remain_file_size - 0x4000
                to_dec_content = enc_file_content[:0x4000]
                enc_file_content = enc_file_content[0x4000:]
                enc_file_int_list = self.convertByteListToIntList(to_dec_content)
                result_byte_list.extend(self.decryptContent(enc_file_int_list))

            else:
                #print(len(enc_file_content))
                #print(remain_file_size)
                if remain_file_size%8 != 0:
                    need_fill_length = 8 - (remain_file_size % 8 )
                    remain_file_size = (remain_file_size//8 + 1)*8
                    enc_file_content += bytes(need_fill_length)
                #print(len(enc_file_content))
                enc_file_int_list = self.convertByteListToIntList(enc_file_content)
                result_byte_list.extend(self.decryptContent(enc_file_int_list))
                break
        enc_file.close()
        return bytes(result_byte_list[:enc_file_length])

    # modified TEA decrypt algorithm
    def decryptContent(self, enc_int_list):
        key_list = self.key_list
        result_byte_list = []
        num = 13
        num6 = 0x9e3779b9
        index = 0

        enc_length = len(enc_int_list)
        #print(enc_length)
        #print(enc_int_list)
        while index < enc_length:
            num2 = self._limit_to_int32(enc_int_list[index])
            num3 = self._limit_to_int32(enc_int_list[index+1])

            num4 = 0xc6ef3720
            num5 = 0x20

            while(True):
                if(num5 <= 0):
                    num5 -= 1
                    enc_int_list[index] = self._limit_to_int32(num2^num)
                    enc_int_list[index+1] = self._limit_to_int32(num3)
                    num = self._limit_to_int32(self._limit_to_int32(num + 0x1b)%0x7ae0)
                    index += 2
                    break
                num5 -= 1
                # num3 -= (((num2<<4) ^ (num2>>5))+num2) ^ (num4 + this._key[(num4>>11)&3])
                num3 = self._limit_to_int32(num3 - self._limit_to_int32(self._limit_to_int32(self._limit_to_int32(self._limit_to_int32(num2<<4) ^ self._limit_to_int32(num2>>5))+num2) ^ self._limit_to_int32(num4 + key_list[(num4>>11)&3])))
                # num4 = num4 -num6
                num4 = self._limit_to_int32(num4 - num6)
                # num2 -= (((num3<<4) ^ (num3>>5))+num3) ^ (num4 + this._key[(num4 & 3)])
                num2 = self._limit_to_int32(num2 - self._limit_to_int32(self._limit_to_int32(self._limit_to_int32(self._limit_to_int32(num3<<4) ^ self._limit_to_int32(num3>>5))+num3) ^ self._limit_to_int32(num4 + key_list[(num4 & 3)])))

        #convert int32 list to byte list
        #print(enc_int_list)
        for tmp_i in range(len(enc_int_list)):
            result_byte_list.extend(self._convert_int_to_byte_list(enc_int_list[tmp_i],0x4))

        return result_byte_list

    def doSigCheck(self):
        if self.host_os == "android":
            return self._find_main_activity("\'app.Main\'")
        elif self.host_os == "ios":
            AppMachine_ios_sig = os.path.join("Appmachine.SuperPin.IOS.dll")
            #print(AppMachine_ios_sig)
            if self._check_file_exists(AppMachine_ios_sig):
                return True
        return False

    def doExtract(self, working_folder):
        if self.host_os == "android":
            return self.doExtractAndroid(working_folder)
        elif self.host_os == "ios":
            log.error("no ios ")

    def doExtractiOS(self, working_folder):

        launch_path = None

        extract_folder = self._format_working_folder(working_folder)
        if os.access(extract_folder, os.R_OK):
            shutil.rmtree(extract_folder)
        os.makedirs(extract_folder, exist_ok = True)
        tmp_folder = os.path.join(extract_folder, "tmp")
        os.makedirs(tmp_folder, exist_ok=True)

        self._ipa_extract(tmp_folder)

        self.app_dir_path = os.path.join(tmp_folder,self._ipa_app_path())
        self.resource_file_path = os.path.join(self.app_dir_path,"resources.zip")

        if os.path.exists(self.resource_file_path) == False:
            log.error("no resources.zip in path {} something wrong".format(self.resource_file_path))
            #sys.exit()
            return "", ""

        tmp_zip_file = zipfile.ZipFile(self.resource_file_path)
        tmp_zip_file.extractall(extract_folder)

        # delete tmp to walk the extract_folder
        shutil.rmtree(tmp_folder)
        self.key_list = self.generateKey(self.const_key_seed_str)

        apps_dat_path = os.path.join(extract_folder,"apps.dat")
        if os.path.exists(apps_dat_path) ==False:
            log.error("no apps.dat in resources.zip {} something wrong".format(apps_dat_path))
            #sys.exit()
            return "", ""

        apps_data = ET.parse(apps_dat_path)
        app_drawing_no = None
        for tmp_app_info in apps_data.getroot()[0]:
            if tmp_app_info.tag == "DrawingNo":
                app_drawing_no = tmp_app_info.text
        if app_drawing_no == None:
            log.error("no drawing no in apps.dat {} something wrong".format(apps_dat_path))
            #sys.exit()
            return "", ""
        #print(app_drawing_no)
        # delete all unrelated dir
        for tmp_file in os.listdir(extract_folder):
            if os.path.isdir(os.path.join(extract_folder,tmp_file)):
                if tmp_file != app_drawing_no:
                    shutil.rmtree(os.path.join(extract_folder,tmp_file))

        for root_path, dirs, files in os.walk(extract_folder):
            for tmp_file in files:
                if (tmp_file.endswith(".dat")&(tmp_file!="apps.dat")&(tmp_file!="marker.dat")):
                    target_file_path = os.path.join(root_path,tmp_file)
                    dec_result = self.decryptFile(target_file_path)
                    #replace extracted file with decypted content
                    n_target_file = open(target_file_path,"wb")
                    n_target_file.write(dec_result)
                    n_target_file.close()

        launch_path = "None"

        self._dump_info(extract_folder, launch_path)     #store the home page

        return extract_folder, launch_path

    def doExtractAndroid(self, working_folder):

        launch_path = None

        extract_folder = self._format_working_folder(working_folder)
        if os.access(extract_folder, os.R_OK):
            shutil.rmtree(extract_folder)
        os.makedirs(extract_folder, exist_ok = True)
        tmp_folder = os.path.join(extract_folder, "tmp")
        os.makedirs(tmp_folder, exist_ok=True)

        self._apktool(tmp_folder)

        #self._ipa_extract(tmp_folder)

        #self.app_dir_path = os.path.join(tmp_folder,self._ipa_app_path())
        self.assets_dir_path = os.path.join(tmp_folder,"assets")
        self.resource_file_path = os.path.join(self.assets_dir_path,"resources.zip")

        if os.path.exists(self.resource_file_path) == False:
            log.error("no resources.zip in path {} something wrong".format(self.resource_file_path))
            #sys.exit()
            return "", ""

        tmp_zip_file = zipfile.ZipFile(self.resource_file_path)
        tmp_zip_file.extractall(extract_folder)

        # delete tmp to walk the extract_folder
        shutil.rmtree(tmp_folder)
        self.key_list = self.generateKey(self.const_key_seed_str)
        #print(self.key_list)

        apps_dat_path = os.path.join(extract_folder,"apps.dat")
        if os.path.exists(apps_dat_path) ==False:
            log.error("no apps.dat in resources.zip {} something wrong".format(apps_dat_path))
            #sys.exit()
            return "", ""

        apps_data = ET.parse(apps_dat_path)
        app_drawing_no = None
        for tmp_app_info in apps_data.getroot()[0]:
            if tmp_app_info.tag == "DrawingNo":
                app_drawing_no = tmp_app_info.text
        if app_drawing_no == None:
            log.error("no drawing no in apps.dat {} something wrong".format(apps_dat_path))
            #sys.exit()
            return "", ""
        #print(app_drawing_no)
        # delete all unrelated dir
        for tmp_file in os.listdir(extract_folder):
            if os.path.isdir(os.path.join(extract_folder,tmp_file)):
                if tmp_file != app_drawing_no:
                    shutil.rmtree(os.path.join(extract_folder,tmp_file))

        for root_path, dirs, files in os.walk(extract_folder):
            for tmp_file in files:
                if (tmp_file.endswith(".dat")&(tmp_file!="apps.dat")&(tmp_file!="marker.dat")):

                    target_file_path = os.path.join(root_path,tmp_file)
                    dec_result = self.decryptFile(target_file_path)
                    #replace extracted file with decypted content
                    n_target_file = open(target_file_path,"wb")
                    n_target_file.write(dec_result)
                    n_target_file.close()

        launch_path = "None"

        self._dump_info(extract_folder, launch_path)     #store the home page

        return extract_folder, launch_path

def main():
    f = "./test_case/AppMachine/des.apk"    #后续会将当前脚本路径与之相拼接，得到最终detect_file路径
    appcan = AppMachine(f, "android")
    if appcan.doSigCheck():
        logging.info("AppCan signature Match")
        extract_folder, launch_path = appcan.doExtract("working_folder")
        log.info("{} is extracted to {}, the start page is {}".format(f, extract_folder, launch_path))

    return

if __name__ == "__main__":
    sys.exit(main())


import re
import json
import numpy
import collections
import openpyxl as xl

runners_wb = xl.load_workbook('Leaderboard.xlsx')
runners_sheet = runners_wb.active

with open('test.npy', 'rb') as f:
    runsArray = numpy.load(f, allow_pickle=True)

pop1anyDOS = []
pop1anyDOSNMG = []
pop1anyNES = []
pop1anyAppleII = []
pop1anyTG16 = []
pop1anySMS = []
pop1anyGBC = []
pop1anyAmiga = []
pop1anyMD = []
pop1anyGenesis = []
pop1anyLS = []
pop1anyLSNMG = []
pop1hundo = []
pop1hundoNMG = []
pop1pacifist = []
pop1istaria = []
pop1RPR = []
pop1SNESanyNW = []
pop1SNESany = []
pop1SNESanyNGS = []
pop1SNESSR = []
pop1SNES30AP = []
pop1SNESTQoL = []
pop1SNESEvol = []
pop1SNESTDC = []
pop2any = []
pop3dany = []
pop3danyAT = []
popanany = []
popsotACZip = []
popsotACZipl = []
popsotACNMG = []
popsotanyZip = []
popsotanyZipl = []
popsotanyNMG = []
popsothundoZip = []
popsothundoNMG = []
popsotGBAany = []
popwwanyZip = []
popwwanyZipl = []
popwwanyNMG = []
popwwTEZip = []
popwwTEZipl = []
popwwTENMG = []
popwwACNMG = []
popwwAChZipl = []
popwwAChNMG = []
popwwAAZip = []
popt2tanyZip = []
popt2tanyZipl = []
popt2tanyNMG = []
popt2tAPZip = []
popt2tAPZipl = []
popt2tAPNMG = []
popt2tPSPUSNMG = []
popclassicany = []
pop2k8anySt = []
pop2k8anyNMG = []
pop2k8AS = []
pop2k8Epilougeany = []
pop2k8Hacked = []
pop2k8anyDupe = []
poptfkanyCon = []
poptfkanyEmu = []
poptfsanySt = []
poptfsanyNEG = []
poptfsanyNMG = []
poptfsPSPany = []
poptfsWiianyNG = []
poptfsWiianyNGP = []
poptfsDSanyCon = []
poptfsDSanyEmu = []
mobileHA = []
mobileSnFR = []
mobileSoT = []
mobileWW = []
mobileT2T = []
mobileClassic = []
mobile2k8 = []
mobileTFS = []
multiSTanyZip = []
multiSTanyZipl = []
multiSTanyNMG = []
multiSTCompZip = []
multiSTCompZipl = []
multiSTCompNMG = []
runnersArray = {"":0}

for i in runsArray:
    if(i['category'] == 'rklx8pnk'):
        if(i['subcat'] == 'rqvvyeyq'):
            pop1anyDOS.append(i)
        if(i['subcat'] == '4qyxp34l'):
            pop1anyDOSNMG.append(i)
        if(i['subcat'] == '5le2xv6l'):
            pop1anyNES.append(i)
        if(i['subcat'] == '81w2ve51'):
            pop1anyAppleII.append(i)
        if(i['subcat'] == '81wm0j5q'):
            pop1anyTG16.append(i)
        if(i['subcat'] == 'zqo50dpl'):
            pop1anyGBC.append(i)
        if(i['subcat'] == 'p12z2m41'):
            pop1anySMS.append(i)
        if(i['subcat'] == '81pdo5v1'):
            pop1anyAmiga.append(i)
        if(i['subcat'] == '810y2o5l'):
            pop1anyMD.append(i)
        if(i['subcat'] == 'klrv7woq'):
            pop1anyGenesis.append(i)

    if(i['category'] == 'ndx9xq5d'):
        if(i['subcat'] == 'klrnw4w1'):
            pop1anyLS.append(i)
        if(i['subcat'] == '21dr7ggq'):
            pop1anyLSNMG.append(i)

    if(i['category'] == 'wkpq95gd'):
        if(i['subcat'] == '21ggx3m1'):
            pop1hundo.append(i)
        if(i['subcat'] == '21ggx3m1'):
            pop1hundoNMG.append(i)

    if(i['category'] == 'mkeloznd'):
        pop1pacifist.append(i)

    if(i['category'] == 'wdmvjyod'):
        pop1istaria.append(i)

    if(i['category'] == '9kv8ww8d'):
        pop1RPR.append(i)

    if(i['category'] == 'vdomz5y2'):
        pop1SNESanyNW.append(i)

    if(i['category'] == '8241l8m2'):
        pop1SNESany.append(i)

    if(i['category'] == 'wkpm95gk'):
        pop1SNESanyNGS.append(i)

    if(i['category'] == 'w20go3zk'):
        pop1SNESSR.append(i)

    if(i['category'] == 'mkev0xx2'):
        pop1SNES30AP.append(i)

    if(i['category'] == '5dw9l1n2'):
        pop1SNESTQoL.append(i)

    if(i['category'] == 'wdmxp4ek'):
        pop1SNESEvol.append(i)

    if(i['category'] == 'vdo8eo6k'):
        pop1SNESTDC.append(i)

    if(i['category'] == 'jdrpw3x2'):
        pop2any.append(i)

    if(i['category'] == 'jdz7lxvk'):
        pop3dany.append(i)

    if(i['category'] == 'z27zlok0'):
        pop3danyAT.append(i)

    if(i['category'] == 'rklv09qd'):
        popanany.append(i)

    if(i['category'] == 'zd3xnrdn'):
        if(i['subcat'] == '21d5m3le'):
            popsotACZip.append(i)
        if(i['subcat'] == '5q8pmrld'):
            popsotACZipl.append(i)
        if(i['subcat'] == '5leygm1o'):
            popsotACNMG.append(i)

    if(i['category'] == 'xd1v9wd8'):
        if(i['subcat'] == '4qy963l7'):
            popsotanyZip.append(i)
        if(i['subcat'] == 'mln926qp'):
            popsotanyZipl.append(i)
        if(i['subcat'] == '810prjlv'):
            popsotanyNMG.append(i)

    if(i['category'] == 'zd3lmz8d'):
        if(i['subcat'] == '013w9krq'):
            popsothundoZip.append(i)
        if(i['subcat'] == 'rqv6v2rl'):
            popsothundoNMG.append(i)

    if(i['category'] == '02q0gjky'):
        popsotGBAany.append(i)

    if(i['category'] == '02qnm92y'):
        if(i['subcat'] == '0q5mvvlp'):
            popwwanyZip.append(i)
        if(i['subcat'] == '4lxk5g12'):
            popwwanyZipl.append(i)
        if(i['subcat'] == 'zqo7k51y'):
            popwwanyNMG.append(i)

    if(i['category'] == 'wkp550dr'):
        if(i['subcat'] == '013ydrl5'):
            popwwTEZip.append(i)
        if(i['subcat'] == '5lexv6qo'):
            popwwTEZipl.append(i)
        if(i['subcat'] == 'rqvngr16'):
            popwwTENMG.append(i)

    if(i['category'] == 'mke9qpjd'):
        if(i['subcat'] == '21gy9d61'):
            popwwACNMG.append(i)

    if(i['category'] == '9kvoge82'):
        if(i['subcat'] == 'jqzkge2q'):
            popwwAChZipl.append(i)
        if(i['subcat'] == 'xqk56vnq'):
            popwwAChNMG.append(i)

    if(i['category'] == 'wdmx13ok'):
        if(i['subcat'] == 'jq63v07q'):
            popwwAAZip.append(i)

    if(i['category'] == '5dw11nkg'):
        if(i['subcat'] == 'p12p0vqx'):
            popt2tanyZip.append(i)
        if(i['subcat'] == '81w2wm14'):
            popt2tanyZipl.append(i)
        if(i['subcat'] == '81p25el7'):
            popt2tanyNMG.append(i)

    if(i['category'] == 'n2yvvm2o'):
        if(i['subcat'] == '810y85lv'):
            popt2tAPZip.append(i)
        if(i['subcat'] == '9qjorgl4'):
            popt2tAPZipl.append(i)
        if(i['subcat'] == 'rqvyeyq6'):
            popt2tAPNMG.append(i)

    if(i['category'] == 'zd3xvzrd'):
        if(i['subcat'] == '81wwzv51'):
            popt2tPSPUSNMG.append(i)

    if(i['category'] == '9d8pn3wk'):
        popclassicany.append(i)

    if(i['category'] == 'xd1rv6zk'):
        if(i['subcat'] == 'jqzvpd4l'):
            pop2k8anySt.append(i)
        if(i['subcat'] == 'gq793ndl'):
            pop2k8anyNMG.append(i)

    if(i['category'] == 'z276g30d'):
        pop2k8AS.append(i)

    if(i['category'] == 'zd30xped'):
        pop2k8Epilougeany.append(i)

    if(i['category'] == 'zd366lyd'):
        pop2k8Hacked.append(i)

    if(i['category'] == 'xd170vwd'):
        pop2k8anyDupe.append(i)

    if(i['category'] == '9kv74gjk'):
        if(i['subcat'] == '21go0xmq'):
            poptfkanyCon.append(i)
        if(i['subcat'] == 'gq7z37y1'):
            poptfkanyEmu.append(i)

    if(i['category'] == 'wdm4o54d'):
        if(i['subcat'] == 'zqomxg51'):
            poptfsanySt.append(i)
        if(i['subcat'] == 'jqzxo941'):
            poptfsanyNEG.append(i)
        if(i['subcat'] == '013w9jrq'):
            poptfsanyNMG.append(i)

    if(i['category'] == 'w201mqzk'):
        poptfsPSPany.append(i)

    if(i['category'] == 'zd3l4vnd'):
        if(i['subcat'] == 'xqkr6ey1'):
            poptfsWiianyNG.append(i)
        if(i['subcat'] == 'gq7nz5y1'):
            poptfsWiianyNGP.append(i)

    if(i['category'] == '9kvj3q0k'):
        if(i['subcat'] == '81wr08ml'):
            poptfsDSanyCon.append(i)
        if(i['subcat'] == 'zqo2vn5l'):
            poptfsDSanyEmu.append(i)

    if(i['category'] == 'wk67pnxd'):
        mobileHA.append(i)

    if(i['category'] == 'mkerym6d'):
        mobileSnFR.append(i)

    if(i['category'] == '9kvmp78k'):
        mobileSoT.append(i)

    if(i['category'] == 'rklqxvn2'):
        mobileWW.append(i)

    if(i['category'] == 'ndxmj7r2'):
        mobileT2T.append(i)

    if(i['category'] == 'vdoo0mvd'):
        mobileClassic.append(i)

    if(i['category'] == 'w20wpv5d'):
        mobile2k8.append(i)

    if(i['category'] == 'wdmwzlo2'):
        mobileTFS.append(i)

    if(i['category'] == 'rklxvvqk'):
        if(i['subcat'] == 'jq68rvlm0q503m1p'):
            multiSTanyZip.append(i)
        if(i['subcat'] == 'jq68rvlm4lxj62q2'):
            multiSTanyZipl.append(i)
        if(i['subcat'] == 'jq68rvlm814nw0ld'):
            multiSTanyNMG.append(i)
        if(i['subcat'] == '81w6oml40q503m1p'):
            multiSTCompZip.append(i)
        if(i['subcat'] == '81w6oml44lxj62q2'):
            multiSTCompZipl.append(i)
        if(i['subcat'] == '81w6oml4814nw0ld'):
            multiSTCompNMG.append(i)

def allotPoints(catArray):
    catArray = sorted(catArray, key=lambda d: d['time'])
    points = len(catArray)
    for j in catArray:
        if j['runner'] in runnersArray.keys():
            runnersArray[j['runner']] += points
            points -= 1
        else:
            runnersArray[j['runner']] = points
            points -= 1

def calcPoints(catArray, runner):
    catArray = sorted(catArray, key=lambda d: d['time'])
    points = len(catArray)
    runnerpoints = 0
    for j in catArray:
        if j['runner'] == runner:
            runnerpoints+=points
        points-=1
    return runnerpoints

allotPoints(pop1anyDOS)
allotPoints(pop1anyDOSNMG)
allotPoints(pop1anyNES)
allotPoints(pop1anyAppleII)
allotPoints(pop1anyTG16)
allotPoints(pop1anySMS)
allotPoints(pop1anyGBC)
allotPoints(pop1anyAmiga)
allotPoints(pop1anyMD)
allotPoints(pop1anyGenesis)
allotPoints(pop1anyLS)
allotPoints(pop1anyLSNMG)
allotPoints(pop1hundo)
allotPoints(pop1hundoNMG)
allotPoints(pop1pacifist)
allotPoints(pop1istaria)
allotPoints(pop1RPR)
allotPoints(pop1SNESanyNW)
allotPoints(pop1SNESany)
allotPoints(pop1SNESanyNGS)
allotPoints(pop1SNESSR)
allotPoints(pop1SNES30AP)
allotPoints(pop1SNESTQoL)
allotPoints(pop1SNESEvol)
allotPoints(pop1SNESTDC)
allotPoints(pop2any)
allotPoints(pop3dany)
allotPoints(pop3danyAT)
allotPoints(popanany)
allotPoints(popsotACZip)
allotPoints(popsotACZipl)
allotPoints(popsotACNMG)
allotPoints(popsotanyZip)
allotPoints(popsotanyZipl)
allotPoints(popsotanyNMG)
allotPoints(popsothundoZip)
allotPoints(popsothundoNMG)
allotPoints(popsotGBAany)
allotPoints(popwwanyZip)
allotPoints(popwwanyZipl)
allotPoints(popwwanyNMG)
allotPoints(popwwTEZip)
allotPoints(popwwTEZipl)
allotPoints(popwwTENMG)
allotPoints(popwwACNMG)
allotPoints(popwwAChZipl)
allotPoints(popwwAChNMG)
allotPoints(popwwAAZip)
allotPoints(popt2tanyZip)
allotPoints(popt2tanyZipl)
allotPoints(popt2tanyNMG)
allotPoints(popt2tAPZip)
allotPoints(popt2tAPZipl)
allotPoints(popt2tAPNMG)
allotPoints(popt2tPSPUSNMG)
allotPoints(popclassicany)
allotPoints(pop2k8anySt)
allotPoints(pop2k8anyNMG)
allotPoints(pop2k8AS)
allotPoints(pop2k8Epilougeany)
allotPoints(pop2k8Hacked)
allotPoints(pop2k8anyDupe)
allotPoints(poptfkanyCon)
allotPoints(poptfkanyEmu)
allotPoints(poptfsanySt)
allotPoints(poptfsanyNEG)
allotPoints(poptfsanyNMG)
allotPoints(poptfsPSPany)
allotPoints(poptfsWiianyNG)
allotPoints(poptfsWiianyNGP)
allotPoints(poptfsDSanyCon)
allotPoints(poptfsDSanyEmu)
allotPoints(mobileHA)
allotPoints(mobileSnFR)
allotPoints(mobileSoT)
allotPoints(mobileWW)
allotPoints(mobileT2T)
allotPoints(mobileClassic)
allotPoints(mobile2k8)
allotPoints(mobileTFS)
allotPoints(multiSTanyZip)
allotPoints(multiSTanyZipl)
allotPoints(multiSTanyNMG)
allotPoints(multiSTCompZip)
allotPoints(multiSTCompZipl)
allotPoints(multiSTCompNMG)

sorted_array = sorted(runnersArray.items(), key=lambda kv: kv[1], reverse=True)

runnercount = 0;
for i in sorted_array:
    runners_sheet.cell(row=runnercount+4, column=1).value = i[0]
    runners_sheet.cell(row=runnercount+4, column=2).value = i[1]
    runners_sheet.cell(row=runnercount+4, column=3).value = calcPoints(pop1anyDOS, i[0])
    runners_sheet.cell(row=runnercount+4, column=4).value = calcPoints(pop1anyDOSNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=5).value = calcPoints(pop1anyNES, i[0])
    runners_sheet.cell(row=runnercount+4, column=6).value = calcPoints(pop1anyAppleII, i[0])
    runners_sheet.cell(row=runnercount+4, column=7).value = calcPoints(pop1anyTG16, i[0])
    runners_sheet.cell(row=runnercount+4, column=8).value = calcPoints(pop1anyGBC, i[0])
    runners_sheet.cell(row=runnercount+4, column=9).value = calcPoints(pop1anySMS, i[0])
    runners_sheet.cell(row=runnercount+4, column=10).value = calcPoints(pop1anyAmiga, i[0])
    runners_sheet.cell(row=runnercount+4, column=11).value = calcPoints(pop1anyMD, i[0])
    runners_sheet.cell(row=runnercount+4, column=12).value = calcPoints(pop1anyGenesis, i[0])
    runners_sheet.cell(row=runnercount+4, column=13).value = calcPoints(pop1anyLS, i[0])
    runners_sheet.cell(row=runnercount+4, column=14).value = calcPoints(pop1anyLSNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=15).value = calcPoints(pop1hundo, i[0])
    runners_sheet.cell(row=runnercount+4, column=16).value = calcPoints(pop1hundoNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=17).value = calcPoints(pop1pacifist, i[0])
    runners_sheet.cell(row=runnercount+4, column=18).value = calcPoints(pop1istaria, i[0])
    runners_sheet.cell(row=runnercount+4, column=19).value = calcPoints(pop1RPR, i[0])
    runners_sheet.cell(row=runnercount+4, column=20).value = calcPoints(pop1SNESanyNW, i[0])
    runners_sheet.cell(row=runnercount+4, column=21).value = calcPoints(pop1SNESany, i[0])
    runners_sheet.cell(row=runnercount+4, column=22).value = calcPoints(pop1SNESanyNGS, i[0])
    runners_sheet.cell(row=runnercount+4, column=23).value = calcPoints(pop1SNESSR, i[0])
    runners_sheet.cell(row=runnercount+4, column=24).value = calcPoints(pop1SNES30AP, i[0])
    runners_sheet.cell(row=runnercount+4, column=25).value = calcPoints(pop1SNESTQoL, i[0])
    runners_sheet.cell(row=runnercount+4, column=26).value = calcPoints(pop1SNESEvol, i[0])
    runners_sheet.cell(row=runnercount+4, column=27).value = calcPoints(pop1SNESTDC, i[0])
    runners_sheet.cell(row=runnercount+4, column=28).value = calcPoints(pop2any, i[0])
    runners_sheet.cell(row=runnercount+4, column=29).value = calcPoints(pop3dany, i[0])
    runners_sheet.cell(row=runnercount+4, column=30).value = calcPoints(pop3danyAT, i[0])
    runners_sheet.cell(row=runnercount+4, column=31).value = calcPoints(popanany, i[0])
    runners_sheet.cell(row=runnercount+4, column=32).value = calcPoints(popsotanyZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=33).value = calcPoints(popsotanyZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=34).value = calcPoints(popsotanyNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=35).value = calcPoints(popsotACZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=36).value = calcPoints(popsotACZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=37).value = calcPoints(popsotACNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=38).value = calcPoints(popsothundoZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=39).value = calcPoints(popsothundoNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=40).value = calcPoints(popsotGBAany, i[0])
    runners_sheet.cell(row=runnercount+4, column=41).value = calcPoints(popwwanyZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=42).value = calcPoints(popwwanyZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=43).value = calcPoints(popwwanyNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=44).value = calcPoints(popwwTEZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=45).value = calcPoints(popwwTEZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=46).value = calcPoints(popwwTENMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=47).value = calcPoints(popwwACNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=48).value = calcPoints(popwwAChZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=49).value = calcPoints(popwwAChNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=50).value = calcPoints(popwwAAZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=51).value = calcPoints(popt2tanyZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=52).value = calcPoints(popt2tanyZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=53).value = calcPoints(popt2tanyNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=54).value = calcPoints(popt2tAPZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=55).value = calcPoints(popt2tAPZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=56).value = calcPoints(popt2tAPNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=57).value = calcPoints(popt2tPSPUSNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=58).value = calcPoints(popclassicany, i[0])
    runners_sheet.cell(row=runnercount+4, column=59).value = calcPoints(pop2k8anySt, i[0])
    runners_sheet.cell(row=runnercount+4, column=60).value = calcPoints(pop2k8anyNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=61).value = calcPoints(pop2k8AS, i[0])
    runners_sheet.cell(row=runnercount+4, column=62).value = calcPoints(pop2k8Epilougeany, i[0])
    runners_sheet.cell(row=runnercount+4, column=63).value = calcPoints(pop2k8Hacked, i[0])
    runners_sheet.cell(row=runnercount+4, column=64).value = calcPoints(pop2k8anyDupe, i[0])
    runners_sheet.cell(row=runnercount+4, column=65).value = calcPoints(poptfkanyCon, i[0])
    runners_sheet.cell(row=runnercount+4, column=66).value = calcPoints(poptfkanyEmu, i[0])
    runners_sheet.cell(row=runnercount+4, column=67).value = calcPoints(poptfsanySt, i[0])
    runners_sheet.cell(row=runnercount+4, column=68).value = calcPoints(poptfsanyNEG, i[0])
    runners_sheet.cell(row=runnercount+4, column=69).value = calcPoints(poptfsanyNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=70).value = calcPoints(poptfsPSPany, i[0])
    runners_sheet.cell(row=runnercount+4, column=71).value = calcPoints(poptfsWiianyNG, i[0])
    runners_sheet.cell(row=runnercount+4, column=72).value = calcPoints(poptfsWiianyNGP, i[0])
    runners_sheet.cell(row=runnercount+4, column=73).value = calcPoints(poptfsDSanyCon, i[0])
    runners_sheet.cell(row=runnercount+4, column=74).value = calcPoints(poptfsDSanyEmu, i[0])
    runners_sheet.cell(row=runnercount+4, column=75).value = calcPoints(mobileHA, i[0])
    runners_sheet.cell(row=runnercount+4, column=76).value = calcPoints(mobileSnFR, i[0])
    runners_sheet.cell(row=runnercount+4, column=77).value = calcPoints(mobileSoT, i[0])
    runners_sheet.cell(row=runnercount+4, column=78).value = calcPoints(mobileWW, i[0])
    runners_sheet.cell(row=runnercount+4, column=79).value = calcPoints(mobileT2T, i[0])
    runners_sheet.cell(row=runnercount+4, column=80).value = calcPoints(mobileClassic, i[0])
    runners_sheet.cell(row=runnercount+4, column=81).value = calcPoints(mobile2k8, i[0])
    runners_sheet.cell(row=runnercount+4, column=82).value = calcPoints(mobileTFS, i[0])
    runners_sheet.cell(row=runnercount+4, column=83).value = calcPoints(multiSTanyZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=84).value = calcPoints(multiSTanyZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=85).value = calcPoints(multiSTanyNMG, i[0])
    runners_sheet.cell(row=runnercount+4, column=86).value = calcPoints(multiSTCompZip, i[0])
    runners_sheet.cell(row=runnercount+4, column=87).value = calcPoints(multiSTCompZipl, i[0])
    runners_sheet.cell(row=runnercount+4, column=88).value = calcPoints(multiSTCompNMG, i[0])
    runnercount+=1;

runners_wb.save('Leaderboard.xlsx')


import copy
import math
import os
import os.path as osp
from types import SimpleNamespace

import matplotlib.pyplot as plt
import numpy as np
import plotly.graph_objects as go
import pytorch_lightning as pl
import torch
import torch.nn.functional as F
import torch_scatter
from hydra.utils import to_absolute_path
from matplotlib import cm
from plyfile import PlyData, PlyElement
from torch.utils.data import Dataset
from torch_geometric.data import DataLoader
from tqdm.auto import tqdm

from .laserscan import LaserScan, SemLaserScan
from .utils.projection import do_up_projection
from .utils.semantickitti import apply_learning_map, from_sem_to_color

NDEBUG = 50

class BaseSequence:
    _LaserScan_ = LaserScan
    _SemLaserScan_ = SemLaserScan

    def __init__(self, directory, options, mode):
        self.directory = directory
        self.set_id()

        self.mode = mode
        self.options = options

        self.set_file_names()
        self.init_euclidian2polar()

        self.set_DEBUG()

        if self.mode=="test" and self.options.save_test_preds:
            self.prepare_test_dir()

    def init_euclidian2polar(self):
        self.slice_ids = np.arange(self.options.slices_per_rotation)

        self.slice_theta_ref = np.stack([
            np.zeros(self.options.slices_per_rotation),
            2*math.pi * self.slice_ids/self.options.slices_per_rotation - math.pi,
            np.zeros(self.options.slices_per_rotation)
        ]).T
        self.voxel_res = np.array(self.options.voxel_res)
        self.voxel_res[1] = math.pi * self.voxel_res[1] / 180

        assert len(self.options.split_hierarchy) == self.options.n_hierarchy

        self.thin_voxel_res = self.voxel_res / np.prod(np.array(self.options.split_hierarchy), 0)#np.array(self.options.split_hierarchy)**self.options.n_hierarchy

        self.polar_min_z = self.options.polar_min_z
        self.polar_max_z = self.options.polar_max_z
        self.polar_max_r = self.options.polar_max_r
        self.voxelind_max = np.array([self.polar_max_r / self.thin_voxel_res[0], 2*math.pi / (self.thin_voxel_res[1] * self.options.slices_per_rotation) - 1, (self.polar_max_z - self.polar_min_z) / self.thin_voxel_res[2]]).astype(int)

        self.polarsliceref_min_bound = np.array(
            [0, 0, self.polar_min_z])

    def prepare_test_dir(self):
        if not os.path.exists("sequences"):
            os.system("mkdir sequences")
            f = open("sequences/description.txt", "w")
            f.write(f"project url: \n")
            f.write(f"publication url: \n")
            f.write(f"bibtex: \n")
            f.close()
        os.system(f"mkdir sequences/{self.id}")
        os.system(f"mkdir sequences/{self.id}/predictions")

    def set_DEBUG(self):
        self.DEBUG = self.options.DEBUG and self.mode not in ["train"]
        if self.DEBUG:
            if self.options.DEBUG != True:
                self.DEBUGselect = np.arange(min(len(self.scan_names), self.options.DEBUG)).astype(np.int64)
            else:
                self.DEBUGselect = np.linspace(
                    0, len(self.scan_names) - 1, NDEBUG).astype(np.int64)

    def set_id(self):
        raise NotImplementedError

    def set_file_names(self):
        self.scan_names = sorted(os.listdir(
            osp.join(self.directory, "velodyne")))

        self.has_labels = osp.exists(
            osp.join(self.directory, "labels")) and self.options.use_semantics
        if self.has_labels:
            self.label_names = sorted(os.listdir(
                osp.join(self.directory, "labels")))
            assert len(self.label_names) == len(
                self.scan_names), "Should have the same number of frames as labels"

    def __len__(self):
        if self.DEBUG:
            return len(self.DEBUGselect)
        return len(self.scan_names)

    def __getmeta__(self, idx):
        meta = {
            "scan_name": osp.join(self.directory, "velodyne", self.scan_names[idx]),
            "has_labels": self.has_labels,
        }

        if self.has_labels:
            meta["label_name"] = osp.join(
                self.directory, "labels", self.label_names[idx])

        return meta

    def __getscan__(self, idx):
        meta = self.__getmeta__(idx)

        if meta["has_labels"]:
            color_dict = self.options.color_map
            nclasses = len(color_dict)
            scan = self._SemLaserScan_(
                nclasses, color_dict
            )
            scan.open_scan(meta["scan_name"])
            scan.open_label(meta["label_name"])
        else:
            scan = self._LaserScan_()
            scan.open_scan(meta["scan_name"])

        return scan, self.poses[idx] if hasattr(self, "poses") else None

    def __getframe__(self, idx):
        raise NotImplementedError

    def __getallframes__(self, idx):
        frame = self.__getframe__(idx)

        for frameid, di in enumerate(self.options.temporal_scan_sequence):
            if self.mode == "train" or not self.options.eval_in_online_setup:
                new_idx = idx + di
            else:
                new_idx = -1

            if new_idx >= 0 and new_idx < len(self.scan_names):
                frame = self.concatenate_frames(
                    frame, 1+frameid, self.__getframe__(new_idx))
            else:
                frame = self.add_fake_frame(frame)
        frame.backprop = frame.frameid == 0
        return frame

    def __getlabels__(self, scan):
        point_y, point_inst = None, None
        if hasattr(scan, "sem_label"):
            point_y = torch.from_numpy(apply_learning_map(
                scan.sem_label.astype(np.int16),
                self.options.learning_map
            ))
        else:
            point_y = torch.zeros(scan.points.shape[0]).int()
        if self.options.use_instances:
            point_inst = torch.from_numpy(scan.inst_label.astype(np.int32))
        return point_y,point_inst

    def from_pos_scan_to_pos(self, pos_scan, pose):
        return np.hstack((pos_scan, np.ones((pos_scan.shape[0], 1), dtype=pos_scan.dtype))).dot(pose[:-1].T)

    def generate_aug(self):
        if self.options.full_feature_aug:
            return (2*math.pi*np.random.random(), np.random.choice(4, 1), np.random.uniform(0.95, 1.05, (1, 3)), np.random.normal(0, 0.2, (1, 3)))
        return (self.voxel_res[1]*(2*np.random.random()-1), np.random.choice(4, 1), np.random.uniform(0.95, 1.05, (1, 3)), np.random.normal(0, 0.2, (1, 3)))

class BaseSplit(Dataset):
    _DatasetSequence_ = BaseSequence

    def __init__(self, options, mode: str = "train"):
        self.options = copy.deepcopy(options)
        self.options.feature_aug = self.options.feature_aug and mode == "train"
        self.mode = mode

        self.create_sequences()
        self.frames = []
        for seqid, seq in self.sequences.items():
            self.frames += [(seqid, frame) for frame in range(len(seq))]

    def __repr__(self):
        o = f"{self.__class__.__name__} {self.mode} with {len(self.sequences)} sequences\n"
        sequences = [len(seq) for seq in self.sequences.values()]
        sequencessum = np.sum(sequences)
        o += f"Size of sequences: {sequencessum} <== {sequences}"
        return o

    def __len__(self):
        return len(self.frames)

    def __getitem__(self, idx):
        sequence, scan = self.frames[idx]
        return self.sequences[sequence][scan]

    def create_sequences(self):
        self.sequences = {
            int(seq): self._DatasetSequence_(
                osp.join(
                    self.options.data_dir,
                    "sequences",
                    "%02d" % seq
                ),
                self.options,
                self.mode
            ) for seq in self.options.split[self.mode]
        }

    def get_sequence(self, idx):
        return self.sequences[idx]

class BaseDataModule(pl.LightningDataModule):
    _DatasetSplit_ = BaseSplit

    def __init__(self, *args, **kwargs):
        super().__init__()

        self.myhparams = SimpleNamespace(**kwargs)
        self.myhparams.data_dir = to_absolute_path(self.myhparams.data_dir)

    def __repr__(self):
        o = [f"{self.__class__.__name__}"]
        for dataset in ["train", "val", "test"]:
            if hasattr(self, f"{dataset}_dataset"):
                o.append(getattr(self, f"{dataset}_dataset").__repr__())
        return "\n".join(o)

    def get_features_names(self):
        return [
            "r_centered", "theta_centered", "z_centered",
            "r", "theta_cos", "theta_sin",
            "z", "x", "y",
            "remission"
        ]

    def from_labels_to_color(self, labels):
        return from_sem_to_color(apply_learning_map(labels, self.myhparams.learning_map_inv), self.myhparams.color_map)

    def setup(self, stage: str = None):
        if stage in (None, 'fit'):
            self.train_dataset = self._DatasetSplit_(self.myhparams, "train")
            self.val_dataset = self._DatasetSplit_(self.myhparams, "valid")
        elif stage in (None, 'validate'):
            self.val_dataset = self._DatasetSplit_(self.myhparams, "valid")

        if stage in (None, 'test'):
            self.test_dataset = self._DatasetSplit_(self.myhparams, "test")

    def train_dataloader(self):
        return DataLoader(
            self.train_dataset,
            batch_size=self.myhparams.batch_size,
            shuffle=True,
            num_workers=self.myhparams.num_workers,
            persistent_workers=True
        )

    def val_dataloader(self):
        return DataLoader(
            self.val_dataset,
            batch_size=1,
            shuffle=False,
            num_workers=self.myhparams.num_workers,
            persistent_workers=True
        )

    def test_dataloader(self):
        return DataLoader(
            self.test_dataset,
            batch_size=1,
            shuffle=False,
            num_workers=self.myhparams.num_workers
        )

    def show(self, item, color="y", view="2d"):
        item = copy.deepcopy(item)

        if view == "3d":
            self.show_3d(item, color)
        elif view == "2d":
            self.show_2d(item, color)
        else:
            raise ValueError

    def get_color_from_item(self, item, color):
        if color == "y" and hasattr(item, "point_y"):
            color = self.from_labels_to_color(item.point_y).numpy()
        elif color == "pred_y" and hasattr(item, "point_pred_y"):
            color = self.from_labels_to_color(item.point_pred_y).numpy()
        elif color == "inst" and hasattr(item, "point_inst"):
            color = cm.get_cmap("tab20")(
                np.unique(item.point_inst, return_inverse=True)[1])
        elif color == "slice":
            color = (255*cm.get_cmap("tab20c")((4*item.voxelind[:, 0])% 20)).astype(int)
        elif color == "voxel":
            voxelind = torch.div(item.voxelind, torch.tensor([1] + list(np.prod(np.array(self.myhparams.split_hierarchy), 0))), rounding_mode="floor")

            maxi = (voxelind.max(0)[0] + 1).numpy()
            voxelind = (voxelind[:, 1:].numpy() * np.array([np.prod(maxi[2:]), np.prod(maxi[3:]), 1])).sum(-1)

            voxelind = np.random.permutation(np.prod(maxi[1:]))[voxelind] % 4
            voxelind = (voxelind + 4*item.voxelind[:, 0].numpy()) % 20
            color = (255*cm.get_cmap("tab20c")(voxelind)).astype(int)
        elif color == "theta" and hasattr(item, "rtz"):
            theta = item.rtz[:, 1]
            color = (255*cm.get_cmap("tab20c")((theta + math.pi)/(2*math.pi))).astype(int)
        elif color == "fiber" and hasattr(item, "fiber"):
            color = (255*cm.get_cmap("tab20c")(item.fiber%20)).astype(int)
        elif color == "time" and hasattr(item, "time"):
            time = (item.time - item.time.min()) / (item.time.max() - item.time.min())
            color = (255*cm.get_cmap("jet")(time.numpy())).astype(int)
        else:
            color = (255*cm.get_cmap("viridis")(item.features[:, -1])).astype(int)
        return color

    def show_2d(self, item, color):
        color_names = color.split(";")
        colors = [self.get_color_from_item(item, c) for c in color_names]

        n_seqs = 1 + len(self.myhparams.temporal_scan_sequence)
        plt.figure(figsize=(7*n_seqs, 7*len(color_names)))

        if hasattr(item, "voxelind"):
            frameid = torch.div(item.voxelind[:, 0], self.myhparams.slices_per_rotation, rounding_mode='floor')
        else:
            frameid = item.frameid

        pos = item.pos - item.pos[item.backprop].mean(0) * torch.tensor([1, 1, 0])

        for frame in range(frameid.max().int().item() + 1):
            isframe = frameid == frame
            proj = do_up_projection(pos[isframe])

            for i, (cname, thiscolor) in enumerate(zip(color_names, colors)):
                if cname in ["y", "pred_y"] and thiscolor.shape[0] != pos.shape[0]:
                    if frame == 0:
                        proj = do_up_projection(pos[item.backprop])
                        image = thiscolor[proj]
                    else:
                        proj = do_up_projection(pos[isframe])
                        image = 255*np.stack([proj < 0 for _ in range(3)], -1)
                else:
                    proj = do_up_projection(pos[isframe])
                    image = thiscolor[isframe][proj]

                if image is not None:
                    image[proj < 0] = 255
                    plt.subplot(len(color_names), n_seqs, (n_seqs-frame)+i*n_seqs)
                    plt.imshow(image)
                    plt.title(f"{cname} frame {frame}")

                    plt.axis("off")

        plt.show()

    def get_label_from_raw_feature(self, c):
        if c == "y":
            return "Ground truth"
        elif c == "pred_y":
            return "Prediction"
        elif c == "slice":
            return "Slice"
        elif c == "voxel":
            return "Partition"
        elif c == "time":
            return "Acquisition time"
        return c

    def show_3d(self, item, color, voxelize=True, has_error=True):
        has_error = has_error and hasattr(item, "point_pred_y")

        if item.pos.size(0) != item.point_y.size(0):
            for attr in ["pos", "sliceid", "frameid", "features", "voxelind", "time"]:
                if hasattr(item, attr) and len(getattr(item, attr)) == len(item.backprop):
                    setattr(item, attr, getattr(item, attr)[item.backprop])

        title = f"Frame {item.scanid} from Sequence {item.seqid}"
        if has_error:
            item.error = torch.logical_and(item.point_y != item.point_pred_y, item.point_y != 0)
            accuracy = (item.point_y[item.point_y != 0] == item.point_pred_y[item.point_y != 0]).float().mean().item()
            title += f"                    Accuracy = {100*accuracy:.2f}%"

        if voxelize:
            choice = torch.unique((item.pos / 0.25).int(), return_inverse=True, dim=0)[1]
            for attr in ["sliceid", "frameid", "voxelind", "time"]:
                if hasattr(item, attr) and len(getattr(item, attr)) == len(choice):
                    setattr(item, attr, torch_scatter.scatter_max(getattr(item, attr), choice, 0)[0])
            for attr in ["point_y", "point_pred_y", "point_inst"]:
                if hasattr(item, attr):
                    setattr(item, attr, torch_scatter.scatter_sum(F.one_hot(getattr(item, attr)), choice, 0).argmax(-1))
            for attr in ["pos", "features"]:
                if hasattr(item, attr):
                    setattr(item, attr, torch_scatter.scatter_mean(getattr(item, attr), choice, 0))
            if has_error:
                item.error = torch.logical_and(item.point_y != item.point_pred_y, item.point_y != 0)

        if item.pos.shape[0] > 2**15:
            choice = np.random.choice(item.pos.shape[0], 2**15, replace=False)
            for attr in ["pos", "point_y", "point_pred_y", "point_inst", "sliceid", "frameid", "features", "error", "voxelind", "time"]:
                if hasattr(item, attr):
                    setattr(item, attr, getattr(item, attr)[choice])

        datadtype = torch.float16
        item.pos -= item.pos.mean(0)
        margin = int(0.02 * 600)
        layout = go.Layout(
            title=title,
            width=1200,
            height=600,
            scene=dict(aspectmode='data', ),
            margin=dict(l=margin, r=margin, b=margin, t=4*margin),
            uirevision=True,
            showlegend=False
        )
        fig = go.Figure(
            layout=layout,
            data=go.Scatter3d(
                x=item.pos[:, 0].to(datadtype), y=item.pos[:, 1].to(datadtype), z=item.pos[:, 2].to(datadtype),
                mode='markers',
                marker=dict(size=2, color=self.get_color_from_item(item, color.split(";")[0])))
        )
        if has_error:
            error_pos = item.pos[item.error]
            fig.add_trace(
                go.Scatter3d(
                    opacity=0.,
                    x=error_pos[:, 0].to(datadtype),
                    y=error_pos[:, 1].to(datadtype),
                    z=error_pos[:, 2].to(datadtype),
                    mode='markers',
                    marker=dict(
                        size=2,
                        color="red"),
                    visible=True,
                    showlegend=False,
                )
            )

        updatemenus=[
            dict(
                buttons=list([
                    dict(
                        args=[{"marker": dict(size=2, color=self.get_color_from_item(item, c))}, [0]],
                        label=self.get_label_from_raw_feature(c),
                        method="restyle"
                    ) for c in color.split(";")
                ]),
            direction="down",
            pad={"r": 10, "t": 10},
            showactive=True,
            x=0.05,
            xanchor="left",
            y=0.88,
            yanchor="top"
            ),
        ]
        if has_error:
            updatemenus.append(
                dict(
                    buttons=[dict(
                        method='restyle',
                        label='Show error',
                        visible=True,
                        args=[{'opacity': [1., 0.], 'showlegend': False}],
                        args2=[{'opacity': [0.2, 1.], 'showlegend': False}],
                        #args=[{'visible': True, 'showlegend': False}, [1]],
                        #args2=[{'visible': False, 'showlegend': False}, [1]],
                        )],
                    pad={'r': 10, 't': 10},
                    showactive=True,
                    type='buttons',
                    xanchor='left',
                    x=0.05,
                    yanchor='top',
                    y=0.95, ),
            )

        fig.update_layout(updatemenus=updatemenus)
        fig.update_layout(scene_aspectmode='data')

        # Place the legend on the left
        fig.update_layout(
            legend=dict(
                yanchor="middle",
                y=0.5,
                xanchor="right",
                x=0.99))

        # Hide all axes and no background
        fig.update_layout(
            scene=dict(
                xaxis_title='',
                yaxis_title='',
                zaxis_title='',
                xaxis=dict(
                    autorange=True,
                    showgrid=False,
                    ticks='',
                    showticklabels=False,
                    backgroundcolor="rgba(0, 0, 0, 0)"
                ),
                yaxis=dict(
                    autorange=True,
                    showgrid=False,
                    ticks='',
                    showticklabels=False,
                    backgroundcolor="rgba(0, 0, 0, 0)"
                ),
                zaxis=dict(
                    autorange=True,
                    showgrid=False,
                    ticks='',
                    showticklabels=False,
                    backgroundcolor="rgba(0, 0, 0, 0)"
                )
            )
        )

        fig.show()

    def save_as_ply(self, item, filename=None, cfg=None, do_3d_tube=False, remove_unlabeled=True):

        if filename is None:
            filename = item.filename.split("/")[-1][:-5] + "ply"
            if do_3d_tube:
                filename = "tube" + filename

        vertex_val = self.get_vertex_val(item, cfg, do_3d_tube, remove_unlabeled)[0]

        PlyData([PlyElement.describe(vertex_val, 'vertex')]).write(filename)
        print("SAVED !")

    def get_vertex_val(self, item, cfg, do_3d_tube, remove_unlabeled=False):
        dtypes = [
            ('x', 'float32'), ('y', 'float32'), ('z', 'float32'),
            ('label', 'int16'), ('frame', 'int32'), ('batch', 'int32'), ('slice', 'int32'),
            ('islice', 'int32'), ('ir', 'int32'), ('itheta', 'int32'), ('iz', 'int32'),
            ('ztime', 'float32'), ('time', 'float32'),
            ('Red', 'uint8'), ('Green', 'uint8'), ('Blue', 'uint8')
        ]

        if do_3d_tube:
            dtypes += [(f'keep', 'bool')]
            anchor_point = item.pos[np.random.choice(item.pos.size(0))]
            if cfg is not None:
                r2 = cfg.model.transformer.restrict_attention_distance**2
            else:
                r2 = 10**2
            tube = ((item.pos - anchor_point)**2).sum(-1) < r2
        else:
            tube = item.frameid

        scalez = 0.075 * 20

        ztime = -(item.pos[:, 2] - (item.voxelind[:, 0]) / scalez)
        usebatch = (hasattr(item, "batch") and item.batch is not None)
        slice = item.voxelind[:, 0] + (item.batch if usebatch else 0) * (item.voxelind[:, 0].max() + 1)

        maxi, mini = item.pos.min(0)[0], item.pos.max(0)[0]

        size = item.point_y.size(0)
        concat = torch.cat([
            item.pos[:size],
            item.point_y.unsqueeze(-1),
            item.frameid.unsqueeze(-1)[:size], item.batch.unsqueeze(-1)[:size] if usebatch else item.frameid.unsqueeze(-1)[:size], slice.unsqueeze(-1)[:size],
            item.voxelind[:size],
            ztime.unsqueeze(-1)[:size], item.time.unsqueeze(-1)[:size],
            (256*((item.pos-mini)/(maxi-mini))).to(torch.uint8)[:size]
        ], -1)

        if hasattr(item, "point_pred_y"):
            concat = torch.cat([concat, item.point_pred_y.unsqueeze(-1)], -1)
            dtypes.append(('prediction', 'int16'))

        if do_3d_tube:
            concat = torch.cat([concat, tube.unsqueeze(-1)], -1)[tube]

        elif remove_unlabeled:
            concat = concat[item.point_y != 0]

        out = concat.numpy()

        out = np.array([tuple(o) for o in tqdm(out, leave=False)],
            dtype=dtypes
            )

        return out, dtypes


"""
Given an integer num, return the number of steps to reduce it to zero.
In one step, if the current number is even, you have to divide it by
2, otherwise, you have to subtract 1 from it.

Example 1:
Input: num = 14
Output: 6
Explanation:
Step 1) 14 is even; divide by 2 and obtain 7.
Step 2) 7 is odd; subtract 1 and obtain 6.
Step 3) 6 is even; divide by 2 and obtain 3.
Step 4) 3 is odd; subtract 1 and obtain 2.
Step 5) 2 is even; divide by 2 and obtain 1.
Step 6) 1 is odd; subtract 1 and obtain 0.

Example 2:
Input: num = 8
Output: 4
Explanation:
Step 1) 8 is even; divide by 2 and obtain 4.
Step 2) 4 is even; divide by 2 and obtain 2.
Step 3) 2 is even; divide by 2 and obtain 1.
Step 4) 1 is odd; subtract 1 and obtain 0.

Example 3:
Input: num = 123
Output: 12

Constraints:
    0 <= num <= 1e6
"""

class Solution:

    def number_of_steps(self, num: int) -> int:
        reduced, i = num, 0
        while True:
            if not reduced:
                return i
            if not reduced % 2:
                reduced /= 2
            else:
                reduced -= 1
            i += 1


import pygame

def load_tileset(img_path, tile_size):
    """
        Loads a tileset into pygame.Surface objects.

        Returns a list of pygame.Surface objects.

        args :
        * img_path : path of the source image (the tileset)
        * tile_size : a tuple of size 2 describing the size of one tile.

        returns:
        * rv : list of pygame.Surface objects, of size $tile_size
    """
    i = j = 0
    xinc, yinc, *_ = tile_size
    tile_size = (xinc, yinc)
    image = pygame.image.load(img_path)
    xmax, ymax = image.get_size()
    rv = []
    while i < xmax:
        while j < ymax:
            surf = pygame.Surface(tile_size)
            surf.blit(image, (0, 0), (i, j, i + xinc, j + yinc))
            rv.append(surf)
            j += yinc
        i += xinc
    return rv