#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 21 15:29:25 2020

@author: sergio.lordano
"""
import numpy
import numpy as np
from matplotlib import pyplot as plt
from scipy.interpolate import interp1d
from scipy.optimize import minimize
from optlnls.math import derivate

def crop_height_error_matrix(matrix, L, W, height_offset=False):
    """
    This function receives as input a 2D mirror height error matrix and crop it\
    to new length and width within the matrix range. Input and Output format is a 2D matrix in which\
    the first row is the longitudinal coordinates, the first column is sagittal coordinates,\
    elements [1:,1:] are the corresponding height errors and element [0,0] is unused.\
    Length, width and matrix must be in same units.\n
    :matrix: 2D matrix in the above mentioned format
    :L: total mirror length in which the matrix must be cropped
    :W: total mirror width in which the matrix must be cropped
    :height_offset: if True, a height offset will be added in all points so that the median value of central line is zero.
    """
    import numpy as np
    idx_min_L = np.abs(matrix[0,:]-(-L/2.0)).argmin() # search for the coordinate values which are the closest from desired L and W.
    idx_max_L = np.abs(matrix[0,:]-(L/2.0)).argmin()
    idx_min_W = np.abs(matrix[:,0]-(-W/2.0)).argmin()
    idx_max_W = np.abs(matrix[:,0]-(W/2.0)).argmin()
#    print(idx_min_L, idx_max_L, idx_min_W, idx_max_W)

    height2D_part = matrix[idx_min_W:idx_max_W+1,idx_min_L:idx_max_L+1]
    height2D_fmt = np.zeros((len(height2D_part[:,0])+1,len(height2D_part[0,:])+1))
    height2D_fmt[0,:][1:] = matrix[0,:][idx_min_L:idx_max_L+1]
    height2D_fmt[:,0][1:] = matrix[:,0][idx_min_W:idx_max_W+1]
    if(height_offset):
        vert_displacement = (max(height2D_part[int(len(height2D_part)/2.0)+1,:])+min(height2D_part[int(len(height2D_part)/2.0)+1,:]))/2.0
    else:
        vert_displacement = 0
    height2D_fmt[1:,1:] = height2D_part - vert_displacement

    return height2D_fmt

def write_shadow_height_error(matrix, file_name, unit_factor, delimiter=' '):
    """
    Creates a mirror height error file in shadow/dabam format from a 2D matrix in which\
    the first row is the longitudinal coordinates, the first column is sagittal coordinates,\
    elements [1:,1:] are the corresponding height errors and element [0,0] is unused.\n
    :matrix: 2D matrix in the above mentioned format
    :file_name: file name containing extension, e.g. 'mirror_error.dat'
    :unit_factor: unit factor from matrix units, that is, 1e+3 to write file in [mm] if matrix is in [m]
    :delimiter: space as standard for shadow format, or alternatively tab.
    """
    nl = len(matrix[:,0])-1
    nc = len(matrix[0,:])-1
    with open(file_name, 'w') as ofile:
        ofile.write('{0}'.format(nl)+ delimiter +'{0}\n'.format(nc))
        for line in range(nl+1):
            for column in range(nc+1):
                if not(line==0 and column==0):
                    ofile.write('{0:.3e}'.format(matrix[line, column]*unit_factor)+delimiter)
            ofile.write('\n')
    ofile.closed

def from_shadow_to_matrix(file_path, unit_factor):
    """
    Imports a mirror height error file in shadow/dabam format and returns a 2D matrix (in meters) in which\
    the first row is the longitudinal coordinates, the first column is sagittal coordinates,\
    elements [1:,1:] are the corresponding height errors and element [0,0] is unused.\n
    :file_path: path to file including file name.
    :unit_factor: unit factor from the file units to meter, that is, 1e-3 if file is in [mm] for instance.

    """
    import numpy as np
    data = [] # array to store all elements in 1D list
    with open(file_path, 'r') as datafile: # reads and append every element sequentially until readline() fails.
        while True:
            read_line = datafile.readline()
            if not read_line: break
            for element in read_line.split():
                data.append(element)
    datafile.close()

    nl, nc = int(data[0]), int(data[1])
    matrix_data = np.zeros((nl+1, nc+1)) # allocate matrix
    matrix_data[0,:][1:] = np.array(data[2:nc+2], dtype='float')*unit_factor # [m] Longitudinal coordinates
    for line in range(nl): # associate elements in data array to the matrix lines
        matrix_data[line+1,:] = np.array(data[1+(nc+1)*(line+1):1+(nc+1)*(line+2)], dtype='float')*unit_factor # [m]
    return matrix_data

def SRW_figure_error(file_name, unit_factor, angle_in, angle_out, orientation_x_or_y, crop=False, height_offset=False, L=5e3, W=5e3):
    """
    Returns an instance of srwlib.SRWLOptT() which simulates a mirror height error in SRW module.\
    To run this function, it is necessary to have srwlib able to import.\n
    :file_name: filename of file in shadow/dabam format
    :unit_factor: unit factor from the file units to meter, that is, 1e-3 if file is in [mm] for instance.
    :angle_in: incidence angle [rad] relative to mirror surface
    :angle_out: reflection angle [rad] relative to mirror surface
    :orientation_x_or_y: 'x' for horizontal or 'y' for vertical deflection
    :crop: (optional) if True, crops the matrix to new length L and width W and optionally offset data to make median value equal zero
    :height_offset: if True, a height offset will be added in all points so that the median value of central line is zero.
    :L: total mirror length in which the matrix must be cropped
    :W: total mirror width in which the matrix must be cropped
    """
    from srwlib import srwl_opt_setup_surf_height_2d

    height2D = from_shadow_to_matrix(file_name, unit_factor)
    if(crop):
        height2D_cropped = crop_height_error_matrix(height2D, L, W, height_offset)
        print('Actual L x W: {0:.3f} m x {1:.3f} m'.format(height2D_cropped[0,-1]-height2D_cropped[0,1], height2D_cropped[-1,0]-height2D_cropped[1,0]))
        return srwl_opt_setup_surf_height_2d(height2D_cropped, orientation_x_or_y, angle_in, angle_out)
    else:
        return srwl_opt_setup_surf_height_2d(height2D, orientation_x_or_y, angle_in, angle_out)

def analyze_height_error(filelist, unit_factor, plotting=True, workingFolder=''):

    import os
    from optlnls.math import derivate_keeping_size, psd
    from optlnls.plot import set_ticks_size

    # === CREATES SUBFOLDERS === #
    if(workingFolder != ''):
        os.chdir(workingFolder)

    if not (os.path.exists('2D')): os.mkdir('2D')
    if not (os.path.exists('meridional')): os.mkdir('meridional')
    if not (os.path.exists('sagittal')): os.mkdir('sagittal')
    if not (os.path.exists('PSD')): os.mkdir('PSD')
    if not (os.path.exists('statistics')): os.mkdir('statistics')

    for slopefile in filelist:

        # *** Finds adequate dimensions and writes new Shadow input file *** #
        filename = slopefile.split('/')[-1]
        slope2d = from_shadow_to_matrix(slopefile, unit_factor)
        new_mtx = slope2d #mypkg.select_part_surface(slope2d, L, W)
        nW = len(new_mtx[1:,0])
        nL = len(new_mtx[0,1:])
        realL = new_mtx[0,-1]-new_mtx[0,1]
        realW = new_mtx[-1,0]-new_mtx[1,0]
        X = new_mtx[0, 1:]

        if(0):
            write_shadow_height_error(new_mtx, filename.split('.')[0]+'_ctr.dat', unit_factor, delimiter=' ')

        #==================================================#
        # MERIDIONAL
        #==================================================#
        mer_cut = new_mtx[int(np.ceil((len(new_mtx)-1)/2)+1),1:]
        mer_cut_slope = derivate_keeping_size(X, mer_cut)
        PV_mer_cut = (np.max(mer_cut)-np.min(mer_cut))*1e9
        std_height = np.std(mer_cut)*1e9
        std_slope = np.std(mer_cut_slope)*1e6

        PSD, frequencies = psd(X, mer_cut)

        mer_errors = []
        for i in [-2, -1, 0, +1, +2]:
            idx = int(np.ceil((nW-1)/2.0)+1 + ((nW-1)/5.0)*i)
            mer_errors.append(calc_errors(new_mtx[0,1:], new_mtx[idx,1:], new_mtx[idx,0]))
        mer_errors = np.array(mer_errors)

        length_scan = []
        for i in [realL/8.0, realL/4.0, realL/2.0]:
            idx_i, idx_f = np.abs(X+i).argmin(), np.abs(X-i).argmin()
            length_scan.append(calc_errors(X[idx_i:idx_f+1], new_mtx[int(np.ceil((nW-1)/2.0)+1),1:][idx_i:idx_f+1], X[idx_f]-X[idx_i]))
        length_scan = np.array(length_scan)

        #==================================================#
        # SAGITTAL
        #==================================================#
        sag_cut = new_mtx[1:,int(np.ceil((len(new_mtx[0,:])-1)/2)+1)]
        sag_cut_slope = derivate_keeping_size(new_mtx[1:,0], sag_cut)
        PV_sag_cut = (np.max(sag_cut)-np.min(sag_cut))*1e9
        std_height_sag = np.std(sag_cut)*1e9
        std_slope_sag = np.std(sag_cut_slope)*1e6

        sag_errors = []
        for i in [-2, -1, 0, +1, +2]:
            idx = int(np.ceil((nL-1)/2.0)+1 + ((nL-1)/5.0)*i)
            sag_errors.append(calc_errors(new_mtx[1:,0], new_mtx[1:,idx], new_mtx[0,idx]))
        sag_errors = np.array(sag_errors)

        # === WRITES STATISTICS === #
        if(1):
            write_stats(filename, length_scan, mer_errors, sag_errors, new_mtx)

        #==================================================#
        # PLOTS MERIDIONAL
        #==================================================#
        fig_size = 5.5
        fsize = 14

        plt.figure(figsize=(2.5*fig_size,fig_size))

        plt.subplot(121)
        plt.plot(new_mtx[0][1:]*1e3, mer_cut*1e9, '.-', markersize=2.0)
        plt.title(filename+'\nL = {0} mm ; W = {1} mm'.format(realL*1e3, realW*1e3), fontsize=fsize)
        plt.xlabel('Mirror Length [mm]', fontsize=fsize)
        plt.ylabel('Height Error [nm]', fontsize=fsize)
        plt.grid(True)
        plt.text(0.95,0.95,"RMS = {0:.2f} nm".format(std_height)+'\n'+'PV = {0:.2f} nm'.format(PV_mer_cut), verticalalignment="top", horizontalalignment="right", transform=plt.gca().transAxes)
        plt.ylim(plt.ylim()[0], plt.ylim()[1]*1.15)
        set_ticks_size(fsize)

        plt.subplot(122)
        plt.plot(new_mtx[0][1:]*1e3, mer_cut_slope*1e6)
        plt.title(filename+'\nL = {0} mm ; W = {1} mm'.format(realL*1e3, realW*1e3), fontsize=fsize)
        plt.xlabel('Mirror Length [mm]', fontsize=fsize)
        plt.ylabel(r'Slope Error [$\mu rad$]', fontsize=fsize)
    #    plt.ylim(-2.0,2.0)
        plt.grid(True)
        plt.text(0.95,0.95,"RMS = {0:.3f} ".format(std_slope)+r'$\mu rad$', verticalalignment="top", horizontalalignment="right", transform=plt.gca().transAxes)
        set_ticks_size(fsize)
        #plt.show()

        plt.savefig(os.path.join(os.getcwd(), 'meridional', filename.split('.')[0]+'_mer.png'), dpi=300)

        #==================================================#
        # PLOTS SAGITTAL
        #==================================================#
        plt.figure(figsize=(2.5*fig_size,fig_size))

        plt.subplot(121)
        plt.plot(new_mtx[1:,0]*1e3, sag_cut*1e9, '.-', markersize=2.0)
        plt.title(filename+'\nL = {0} mm ; W = {1} mm'.format(realL*1e3, realW*1e3), fontsize=fsize)
        plt.xlabel('Mirror Width [mm]', fontsize=fsize)
        plt.ylabel('Height Error [nm]', fontsize=fsize)
        plt.grid(True)
        plt.text(0.95,0.95,"RMS = {0:.2f} nm".format(std_height_sag)+'\n'+'PV = {0:.2f} nm'.format(PV_sag_cut), verticalalignment="top", horizontalalignment="right", transform=plt.gca().transAxes)
        plt.ylim(plt.ylim()[0], plt.ylim()[1]*1.15)
        set_ticks_size(fsize)

        plt.subplot(122)
        plt.plot(new_mtx[1:,0]*1e3, sag_cut_slope*1e6)
        plt.title(filename+'\nL = {0} mm ; W = {1} mm'.format(realL*1e3, realW*1e3), fontsize=fsize)
        plt.xlabel('Mirror Width [mm]', fontsize=fsize)
        plt.ylabel(r'Slope Error [$\mu rad$]', fontsize=fsize)
    #    plt.ylim(-2.0,2.0)
        plt.grid(True)
        plt.text(0.95,0.95,"RMS = {0:.3f} ".format(std_slope_sag)+r'$\mu rad$', verticalalignment="top", horizontalalignment="right", transform=plt.gca().transAxes)
        set_ticks_size(fsize)
        #plt.show()

        plt.savefig(os.path.join(os.getcwd(), 'sagittal', filename.split('.')[0]+'_sag.png'), dpi=300)

        #==================================================#
        # PLOTS 2D SURFACE
        #==================================================#
        plt.figure(figsize=(12,3.0))
        #fig = plt.gcf()
        ax = plt.gca()
        #cbound = np.max([np.abs(np.floor(np.min(new_mtx[1:,1:]*1e9))),np.abs(np.ceil(np.max(new_mtx[1:,1:]*1e9)))])
        plt.imshow(new_mtx[1:,1:]*1e9, vmin=np.floor(np.min(new_mtx[1:,1:]*1e9)), vmax=np.ceil(np.max(new_mtx[1:,1:]*1e9)), # plot Z in nanometers
                   extent=[-realL/2.0*1e3, realL/2.0*1e3, -realW/2.0*1e3, realW/2.0*1e3], aspect='auto',
                   interpolation='nearest', origin='lower')
        cb = plt.colorbar(orientation="horizontal", fraction = 0.3, pad=0.21, shrink=0.5)
        cb.set_label('nm')
        ax.set_position([0.125, 0.6, 0.8, 0.3])
        ax.yaxis.set_major_locator(plt.MaxNLocator(3))
        plt.xlabel('Length [mm]', fontsize=fsize)
        plt.ylabel('Width [mm]', fontsize=fsize)
        set_ticks_size(fsize)
        #plt.show()

        plt.savefig(os.path.join(os.getcwd(), '2D', filename.split('.')[0]+'_2D.png'), dpi = 200)

        #==================================================#
        # PLOTS PSD
        #==================================================#
        #popt, pcov = curve_fit(linear_function, np.log10(frequencies[int(len(frequencies)*0.05): int(len(frequencies)*0.9)]), np.log10(PSD[int(len(frequencies)*0.05): int(len(frequencies)*0.9)]), maxfev=10000)
        popt = np.polyfit(np.log10(frequencies[int(len(frequencies)*0.05): int(len(frequencies)*0.9)]),
                          np.log10(PSD[int(len(frequencies)*0.05): int(len(frequencies)*0.9)]),1)
        x = frequencies[int(len(frequencies)*0.05): int(len(frequencies)*0.9)]
        #y = linear_function(np.log10(x), popt[0], popt[1])
        y = np.polyval(popt,
                       np.log10(frequencies[int(len(frequencies)*0.05): int(len(frequencies)*0.9)]))
        plt.figure()
        plt.loglog(frequencies, PSD)
        plt.loglog(x, 10**y, '-k')
        plt.title(filename+'\nPower Spectrum Density', fontsize=fsize)
        plt.xlabel('Spatial Frequency [$m^{-1}$]', fontsize=fsize)
        plt.ylabel('PSD [$m^{2}$]', fontsize=fsize)
        plt.grid(True)
        plt.text(0.95,0.95, r'$y = \alpha\ + \beta*x$'+'\n'+r'$\alpha= $'+'{0:.3f}'.format(popt[1])+'\n'+r'$\beta= $'+'{0:.3f}'.format(popt[0]), verticalalignment="top", horizontalalignment="right", transform=plt.gca().transAxes)
        set_ticks_size(fsize)
        plt.savefig(os.path.join(os.getcwd(), 'PSD', filename.split('.')[0]+'_PSD.png'), dpi=200)

        if (plotting):
            plt.show()

        else:
            plt.close('all')

def calc_errors(axis, heights, coordinate_value=0):

    from optlnls.math import derivate_keeping_size

    PV_cut = (np.max(heights)-np.min(heights))
    std_cut = np.std(heights)
    mean_heights = (np.max(heights)+np.min(heights))/2.0
    cut_slope = derivate_keeping_size(axis, heights)
    std_slope = np.std(cut_slope)
    return [coordinate_value, PV_cut, std_cut, mean_heights, std_slope]

def write_stats(filename, length_scan, mer_errors, sag_errors, new_mtx):

    import os

    with open('statistics/'+filename.split('.')[0]+'_stats.txt', 'w') as stats:
        stats.write('Figure Error Analysis'+ '\n' + 'File: ' + os.path.join(os.getcwd(), filename) +'\n\n' + '# *** Meridional Center Line - Different Lengths *** #\n' + 'L [mm]\tHeight-PV[nm]\tHeight-rms[nm]\tHeight-mean[nm]\tSlope-rms[urad]\n')
        for i in range(len(length_scan)):
            stats.write('{0:.1f}\t{1:.2f}\t{2:.2f}\t{3:.2f}\t{4:.3f}\n'.format(length_scan[i,0]*1e3, length_scan[i,1]*1e9, length_scan[i,2]*1e9, length_scan[i,3]*1e9, length_scan[i,4]*1e6))
        stats.write('\n'+'# *** Meridional Cuts Statistics *** # \n' + 'W [mm]\tHeight-PV[nm]\tHeight-rms[nm]\tHeight-mean[nm]\tSlope-rms[urad]\n')
        for i in range(len(mer_errors)):
            stats.write('{0:.1f}\t{1:.2f}\t{2:.2f}\t{3:.2f}\t{4:.3f}\n'.format(mer_errors[i,0]*1e3, mer_errors[i,1]*1e9, mer_errors[i,2]*1e9, mer_errors[i,3]*1e9, mer_errors[i,4]*1e6))
        stats.write('\n'+'# *** Sagittal Cuts Statistics *** # \n' + 'L [mm]\tHeight-PV[nm]\tHeight-rms[nm]\tHeight-mean[nm]\tSlope-rms[urad]\n')
        for i in range(len(sag_errors)):
            stats.write('{0:.1f}\t{1:.2f}\t{2:.2f}\t{3:.2f}\t{4:.3f}\n'.format(sag_errors[i,0]*1e3, sag_errors[i,1]*1e9, sag_errors[i,2]*1e9, sag_errors[i,3]*1e9, sag_errors[i,4]*1e6))
        stats.write('\n# *** Over all surface *** # \nPeak-to-Valley[nm]\tMinimum-Height[nm]\tMaximum-Height[nm]\n'+'{0:.3f}\t{1:.3f}\t{2:.3f}\n'.format(np.max(new_mtx[1:,1:]*1e9) - np.min(new_mtx[1:,1:]*1e9), np.min(new_mtx[1:,1:]*1e9), np.max(new_mtx[1:,1:]*1e9)))
    stats.close()

def gen_figure_error(L=400e-3, W=40e-3, stepL=1e-3, stepW=1e-3,
                     rmsL=1e-9, rmsW=1e-9, betaL=2.0, betaW=2.0,
                     typeL='h', typeW='h', seedL=8787, seedW=8454,
                     filename='', plot=False, print_out=False):

    from srxraylib.metrology import profiles_simulation

    # ==== Main Parameters ============================== #

    if(typeL == 'h'):
        typeL = 0 # (0) Normalize by Height RMS, (1) Normalize by Slope RMS
    elif(typeL == 's'):
        typeL = 1

    if(typeW == 'h'):
        typeW = 0 # (0) Normalize by Height RMS, (1) Normalize by Slope RMS
    elif(typeW == 's'):
        typeW = 1

    if(seedL == 0):
        fct = 1e4 if np.random.random() <= 0.5 else 1e3
        seedL = int(np.random.random()*fct)

    if(seedW == 0):
        fct = 1e4 if np.random.random() <= 0.5 else 1e3
        seedW = int(np.random.random()*fct)

    # ==== Creates 2D matrix ============================ #
    x, y, z  = profiles_simulation.simulate_profile_2D(combination='FF',
                         mirror_length=L, step_l=stepL, random_seed_l=seedL,
                         error_type_l=typeL, rms_l=rmsL,
                         power_law_exponent_beta_l=betaL,
                         mirror_width=W, step_w=stepW, random_seed_w=seedW,
                         error_type_w=typeW, rms_w=rmsW,
                         power_law_exponent_beta_w=betaW)

    # ==== Formats matrix to the required by function === #
    mtx_fmt = np.zeros((len(x)+1, len(y)+1))
    mtx_fmt[0, 1:] = y
    mtx_fmt[1:, 0] = x
    mtx_fmt[1:,1:] = np.transpose(z)

    if(print_out):
        print(filename)
        print('RMS = {0:.3f} nm'.format(np.std(z[:, 0]*1e9)))
        print('PV = {0:.3f} nm\n'.format(np.max(z[:, 0]*1e9)-np.min(z[:, 0]*1e9)))

    # # ==== Writes files for shadow input ================ #
    if(filename != ''):
        write_shadow_height_error(mtx_fmt, filename, 1e3, ' ') # [mm]

    # # ==== Preview Plots ================================ #
    if(plot):

        plt.figure(figsize=(6,1))
        plt.pcolormesh(y, x, np.transpose(z))
        plt.autoscale(enable=True, tight=True)

        plt.figure()
        plt.plot(y, z[:, 0]*1e9)
        plt.xlabel('L')
        plt.ylabel('height error')

        plt.show()

    return mtx_fmt

def minimize_slope_and_height(x, *args):

    from optlnls.math import derivate

    betaL = x

    inputs = args[0]

    mtx = gen_figure_error(L=inputs['L'], W=inputs['W'],
                           stepL=inputs['stepL'], stepW=inputs['stepW'],
                           rmsL=inputs['h_rms'], rmsW=inputs['rmsW'],
                           betaL=betaL, betaW=inputs['betaW'],
                           typeL='h', typeW=inputs['typeW'],
                           seedL=inputs['seedL'], seedW=inputs['seedW'])

    l = mtx[0, 1:]
    h_cut = mtx[1, 1:]
    s_cut = derivate(l, h_cut)
    s_rms = np.std(s_cut)
    #print('evaluating... ', s_rms*1e9, inputs['s_rms']*1e9)

    return np.abs(s_rms - inputs['s_rms'])*1e9

def gen_figure_error_multi(L=400e-3, W=40e-3, stepL=0.5e-3, stepW=0.5e-3,
                           betaW=2.0, rmsW=1e-9, typeW='h',
                           list_height_rms=[1.0e-9], list_slope_rms=[1.0e-9],
                           seedL=8787, seedW=8454, prefix='test', tolL=0.1):

    inputs = {
            "L":L,
            "W":W,
            "stepL":stepL,
            "stepW":stepW,
            "seedL":seedL,
            "seedW":seedW,
            "betaW":betaW,
            "rmsW":rmsW,
            "typeW":typeW
            }

    counter = 1

    for h_rms in list_height_rms:
        for s_rms in list_slope_rms:
            inputs["h_rms"] = h_rms
            inputs['s_rms'] = s_rms
            args = (inputs)
            res = minimize(minimize_slope_and_height, x0=np.array([2.0]), args=args,
                           method='L-BFGS-B', bounds=[(0.05, 15.0)])

            print('Minimization succesful: ', res.success)
            betaL = res.x
            inputs["betaL"] = betaL

            gen_figure_error(L=L, W=W, stepL=stepL, stepW=stepW,
                             rmsL=h_rms, rmsW=rmsW, betaL=betaL, betaW=betaW,
                             typeL='h', typeW=typeW, seedL=seedL, seedW=seedW,
                             filename=prefix+'_{0:d}.dat'.format(int(counter)))

            counter += 1

def spherical(x, R, x0, y0):
    return -numpy.sqrt(R**2 - (x-x0)**2) + y0

def func_ellipse_slopes(x, p, q, theta):
    #
    # returns y'(x), the slopes of an ellipse defined by p,q, and theta
    #

    a = (p + q) / 2
    b = numpy.sqrt( p * q) * numpy.sin(theta)
    c = numpy.sqrt(a*a - b*b)

    epsilon = c / a

    # (x0,y0) are the coordinates of the center of the mirror
    # x0 = (p*p - q*q) / 4 / c
    x0 = (p - q) / 2 / epsilon
    y0 = -b * numpy.sqrt(1 - ((x0/a)**2))

    # the versor normal to the surface at the mirror center is -grad(ellipse)
    xnor = -2 * x0 / a**2
    ynor = -2 * y0 / b**2
    modnor = numpy.sqrt(xnor**2 + ynor**2)
    xnor /= modnor
    ynor /= modnor
    # tangent  versor is perpendicular to normal versor
    xtan =  ynor
    ytan = -xnor

    A = 1/b**2
    B = 1/a**2
    C = A

    CCC = numpy.zeros(11)
    #CCC[1] = A
    CCC[2] = B*xtan**2 + C*ytan**2
    CCC[3] = B*xnor**2 + C*ynor**2
    #CCC[4] = .0
    CCC[5] = 2*(B*xnor*xtan+C*ynor*ytan)
    #CCC[6] = .0
    #CCC[7] = .0
    CCC[8] = .0
    CCC[9] = 2*(B*x0*xnor+C*y0*ynor)
    CCC[10]= .0

    # ellipse implicit eq is c2 x^2 + c3 y^2 + c5 x y + c8 x + c9 y + c10 = 0
    # AA y^2 + BB y + CC = 0
    AA = CCC[3]
    BB = CCC[5]*x + CCC[9]
    CC = CCC[2]*x*x + CCC[8]*x + CCC[10]
    DD = BB*BB-4*AA*CC
    #yell = (-BB - numpy.sqrt(DD) )/(2*AA)
    #yellp = numpy.gradient(yell,(x[1]-x[0]))

    #calculate derivatives (primes P)
    BBP = CCC[5]
    CCP = 2*CCC[2]*x+CCC[8]
    DDP = 2*BB*BBP -4*AA*CCP
    ells = (-1/2/AA) * (BBP + DDP/2/numpy.sqrt(DD))

    return ells

def func_ellipse(x, p, q, theta):
    #
    # returns y(x), the heights of an ellipse defined by p,q, and theta
    #

    a = (p + q) / 2
    b = numpy.sqrt( p * q) * numpy.sin(theta)
    c = numpy.sqrt(a*a - b*b)

    epsilon = c / a

    # (x0,y0) are the coordinates of the center of the mirror
    # x0 = (p*p - q*q) / 4 / c
    x0 = (p - q) / 2 / epsilon
    y0 = -b * numpy.sqrt(1 - ((x0/a)**2))

    # print(">>>> func_ellipse: a=%f, b=%f, c=%f"%(a,b,c))
    # print(">>>> func_ellipse: x0=%f, y0=%f"%(x0,y0))

    # the versor normal to the surface at the mirror center is -grad(ellipse)
    xnor = -2 * x0 / a**2
    ynor = -2 * y0 / b**2
    modnor = numpy.sqrt(xnor**2 + ynor**2)
    xnor /= modnor
    ynor /= modnor
    # tangent  versor is perpendicular to normal versor
    xtan =  ynor
    ytan = -xnor

    A = 1/b**2
    B = 1/a**2
    C = A

    CCC = numpy.zeros(11)
    #CCC[1] = A
    CCC[2] = B*xtan**2 + C*ytan**2
    CCC[3] = B*xnor**2 + C*ynor**2
    #CCC[4] = .0
    CCC[5] = 2*(B*xnor*xtan+C*ynor*ytan)
    #CCC[6] = .0
    #CCC[7] = .0
    CCC[8] = .0
    CCC[9] = 2*(B*x0*xnor+C*y0*ynor)
    CCC[10]= .0

    # ellipse implicit eq is c2 x^2 + c3 y^2 + c5 x y + c8 x + c9 y + c10 = 0
    # AA y^2 + BB y + CC = 0
    AA = CCC[3]
    BB = CCC[5]*x + CCC[9]
    CC = CCC[2]*x*x + CCC[8]*x + CCC[10]

    #yell1 = (-BB + numpy.sqrt(BB*BB-4*AA*CC) )/(2*AA)
    yell2 = (-BB - numpy.sqrt(BB*BB-4*AA*CC) )/(2*AA)

    return yell2

def calc_radius_of_curvature(x, y):
    """

    Parameters
    ----------
    x : TYPE
        DESCRIPTION.
    y : TYPE
        DESCRIPTION.

    Returns
    -------
    x2 : array_like
        new coordinates x.
    R : array_like
        Radius of curvature in x2.

    """

    x1, d1y = derivate(x, y)
    x2, d2y = derivate(x1, d1y)

    d1y_interp = interp1d(x1, d1y)
    d1y_i = d1y_interp(x2)

    R = (1 + d1y_i ** 2) ** (3 / 2) / np.abs(d2y)

    return x2, R

def radius_difference(x, *args):

    # for LTP analysis

    # get x_ell and radius_ell from args
    x_ell = args[0]
    radius_ell = args[1]
    R0 = args[2]

    radius_ell_interp = interp1d(x_ell, radius_ell)

    # manually chack if x is inside x_ell bounds
    if((x <= x_ell.min()) or (x >= x_ell.max())):
        return np.inf # return large value to fail minimization
    else:
        return radius_ell_interp(x) - R0


from nltk.tree import Tree

def get_lca_length(location1, location2):
    i = 0
    while i < len(location1) and i < len(location2) and location1[i] == location2[i]:
        i+=1
    return i

def get_labels_from_lca(ptree, lca_len, location):
    labels = []
    for i in range(lca_len, len(location)):
        labels.append(ptree[location[:i]].label())
    return labels

def findPath(ptree, text1, text2):
    leaf_values = ptree.leaves()
    leaf_index1 = leaf_values.index(text1)
    leaf_index2 = leaf_values.index(text2)

    location1 = ptree.leaf_treeposition(leaf_index1)
    location2 = ptree.leaf_treeposition(leaf_index2)

    #find length of least common ancestor (lca)
    lca_len = get_lca_length(location1, location2)

    #find path from the node1 to lca

    labels1 = get_labels_from_lca(ptree, lca_len, location1)
    #ignore the first element, because it will be counted in the second part of the path
    result = labels1[1:]
    #inverse, because we want to go from the node to least common ancestor
    result = result[::-1]

    #add path from lca to node2
    result = result + get_labels_from_lca(ptree, lca_len, location2)
    return result

ptree = Tree.fromstring("(VP (VERB saw) (NP (DET the) (NOUN dog)))")
print(ptree.pretty_print())
print(findPath(ptree, 'the', "dog"))


import math
import os
import random
import re
import sys
import string

#
# Complete the 'designerPdfViewer' function below.
#
# The function is expected to return an INTEGER.
# The function accepts following parameters:
#  1. INTEGER_ARRAY h
#  2. STRING word
#

def designerPdfViewer(h, word):
    '''
    max_h = 0

    for l in word:
        char_index = string.ascii_lowercase.index(l)
        current_l_h = h[char_index]
        if current_l_h > max_h:
            max_h = current_l_h

    return len(word) * max_h
    '''

    char_h_dict = {
        'a': h[0], 'b': h[1], 'c': h[2], 'd': h[3], 'e': h[4], 'f': h[5], 'g': h[6], 'h': h[7],
        'i': h[8], 'j': h[9], 'k': h[10], 'l': h[11], 'm': h[12], 'n': h[13], 'o': h[14], 'p': h[15],
        'q': h[16], 'r': h[17], 's': h[18], 't': h[19], 'u': h[20], 'v': h[21], 'w': h[22], 'x': h[23],
        'y': h[24], 'z': h[25]
    }

    max_h = 0

    for l in word:
        current_l_h = char_h_dict[l]
        if current_l_h > max_h:
            max_h = current_l_h

    return len(word) * max_h

if __name__ == '__main__':
    h = [1, 3, 1, 3, 1, 4, 1, 3, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7]
    word = 'zaba'

    result = designerPdfViewer(h, word)

    print(f'Selection rectangle area is: {result} mm2')


import numpy as np

# class RingBuffer copied from https://www.oreilly.com/library/view/python-cookbook/0596001673/ch05s19.html
class RingBuffer:
    """ class that implements a not-yet-full buffer """
    def __init__(self,size_max):
        self.max = size_max
        self.xdata = []
        self.ydata = []

    class __Full:
        """ class that implements a full buffer """
        def append(self, x, y):
            """ Append an element overwriting the oldest one. """
            self.xdata[self.cur] = x
            self.ydata[self.cur] = y
            self.cur = (self.cur+1) % self.max
        def get(self):
            """ return list of elements in correct order """
            return self.xdata[self.cur:]+self.xdata[:self.cur], self.ydata[self.cur:]+self.ydata[:self.cur]

    def append(self, x, y):
        """append an element at the end of the buffer"""
        self.xdata.append(x)
        self.ydata.append(y)
        if len(self.xdata) == self.max:
            self.cur = 0
            # Permanently change self's class from non-full to full
            self.__class__ = self.__Full

    def get(self):
        """ Return a list of elements from the oldest to the newest. """
        return self.xdata, self.ydata

class Line():
    def __init__(self):
        # was the line detected in the last iteration?
        self.detected = False
        # x values of the last n fits of the line
        self.recent_xfitted = []
        #average x values of the fitted line over the last n iterations
        self.bestx = None
        #polynomial coefficients averaged over the last n iterations
        self.best_fit = None
        #polynomial coefficients for the most recent fit
        self.current_fit = [np.array([False])]
        #radius of curvature of the line in some units
        self.radius_of_curvature = None
        #distance in meters of vehicle center from the line
        self.line_base_pos = None
        #difference in fit coefficients between last and new fits
        self.diffs = np.array([0,0,0], dtype='float')
        #x values for detected line pixels
        self.allx = None
        #y values for detected line pixels
        self.buffer = RingBuffer(5)
        self.notdetected = 0

    def update(self, x, y):
        self.allx = x
        self.ally = y
        if len(x) < 50:
            self.detected = False
            self.notdetected = self.notdetected + 1
        else:
            self.detected = True
            self.current_fit = np.polyfit(y, x, 2)
            self.notdetected = 0
        self.buffer.append(x,y)
        ax, ay = self.buffer.get()
        aax = np.concatenate(ax)
        aay = np.concatenate(ay)
        self.best_fit = np.polyfit(aay,aax,2)

    def update_force(self, x, y):
        self.allx = x
        self.ally = y
        self.detected = True
        self.current_fit = np.polyfit(y, x, 2)
        self.notdetected = 0
        self.buffer.append(x,y)
        ax, ay = self.buffer.get()
        aax = np.concatenate(ax)
        aay = np.concatenate(ay)
        self.best_fit = np.polyfit(aay,aax,2)

    def y_range(self):
        ax, ay = self.buffer.get()
        aay = np.concatenate(ay)
        return (min(aay),max(aay))

    def get_radius(self):
        fit = self.best_fit
        pos = max(self.ally)
        rad = (1+(2*fit[0]*pos + fit[1])**2)**(3/2)/abs(2*fit[0])
        return rad

    def get_vals(self, rang=None):
        if rang is None:
            rang = self.y_range()
        ploty = np.linspace(rang[0], rang[1], rang[1]-rang[0] )
        fit = self.best_fit
        fitx = fit[0]*ploty**2 + fit[1]*ploty + fit[2]

        return fitx, ploty

    def get_val(self, pos):
        fit = self.best_fit
        fitx = fit[0]*pos**2 + fit[1]*pos + fit[2]

        return fitx


import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES

BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-s[-1]]

class AESCipher:

    def __init__( self, key ):
        self.key = key

    def encrypt( self, raw ):
        raw = pad(raw)
        #iv = Random.new().read( AES.block_size )
        cipher = AES.new( self.key, AES.MODE_ECB)
        return base64.b64encode(cipher.encrypt( raw )).decode('utf-8')

    def decrypt( self, enc ):
        enc = base64.b64decode(enc)
        #v = enc[:16]
        cipher = AES.new(self.key, AES.MODE_ECB )
        return unpad(cipher.decrypt( enc)).decode('utf-8')


#!/home/programacion3/.linuxbrew/bin/python3

from math import sqrt,pow,atan
import numpy as np
import time
from PIL import Image
import argparse

ap= argparse.ArgumentParser()
ap.add_argument("-f", "--filepath", required=True)
args= ap.parse_args()

image= Image.open('/home/programacion3/JulianUribe/Images720x1280/'+args.filepath)
#image= Image.open('/home/programacion3/JulianUribe/WIN_20210528_18_18_45_Pro.jpg')
img= np.array(image)
img= img[:,:,0]

fila=720
columna=1280

halfWidth = fila / 2
halfHeight = columna / 2
strength = 2.3
correctionRadius = sqrt(pow(fila, 2) + pow(columna, 2)) / strength

dstImage=np.array(img)

start= time.time()

for i in range(fila):

    for j in range(columna):

        newX = i - halfWidth
        newY = j - halfHeight

        distance = sqrt(pow(newX, 2) + pow(newY, 2))
        r = distance / correctionRadius

        if r == 0.0:
            theta= 1
        else:
            theta = atan(r) / r

        sourceX = round(halfWidth + theta*newX);
        sourceY = round(halfHeight + theta * newY);

        dstImage[i][j] = img[sourceX][sourceY]

total= time.time() - start
print(total)

pil_image= Image.fromarray(dstImage)
pil_image.save('/home/programacion3/JulianUribe/Resultado/'+args.filepath)
#pil_image.save('/home/programacion3/JulianUribe/Result.png')


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a regex test module '

__author__ = 'Kevin deng'

import re

#正则匹配默认是贪婪匹配，也就是匹配尽可能多的字符。

# 1、识别:成功，则返回一个Match对象，否则返回None
print("\n====== match =====")

print(re.match(r'^\d{3}\-\d{3,8}$', '010-12345'))
print(re.match(r'^\d{3}\-\d{3,8}$', '010+12345'))

# 2、查找
m = re.search(r'\d{3}', "010+234 iow 456")
if m:
	print(m)
	print(m.group())

# 3、查找子串。成功则返回list，包含所有子串
m = re.findall(r'\d{3}', "010+234 iow 456")
if m:
	print(m)

# 4、切分，生成list
print("\n====== split =====")

fname = "The Legend of 1900.1998.海上钢琴师.双语字幕.HR-HDTV.AC3.1024X576.x264.mkv"
print(fname.split('.'))
print(re.split(r'[\.\s]', fname))

# 5、分组。返回tuple
print("\n====== group =====")

m = re.match(r'([\w\s.\']+).(\d{4}).([\w]+)', fname)
print(m.groups())
print(m.group(0))
print(m.group(1))
print(m.group(2))
print(m.group(3))

# 6、字符串替换
strall = "@123 023 中文@12345"
result = re.sub(r'\@\d{3}', "abc", strall)
print(result)

# 用regex对象
reobj = re.compile(r'\@\d{3}')
result = reobj.sub("aaa", strall)
print(result)

name = ["The Boat That Rocked.2009.海盗电台.BD中英双字.mp4",
        "All.Quiet.on.the.Western.Front.1930.西线无战事.BD.中英双字.rmvb",
        "12 monkey.2003.12只猴子.双语字幕.HR-HDTV.AC3.1024X576.x264.mkv",
        "12 monkey.12.2003.12只猴测试.双语字幕.HR-HDTV.AC3.1024X576.x264.mkv",
        "2012.2009.2012世界末日.双语字幕.HR-HDTV.AC3.1024X576.x264.mkv",
        "A.Bug's.Life.1998.虫虫特攻队.双语字幕.国粤英音轨.HR-HDTV.AC3.1024X576.X264-人人影视制作.mkv",
        "The Legend of 1900.1998.海上钢琴师.双语字幕.HR-HDTV.AC3.1024X576.x264-YYeTs人人影视.mkv",
        "2012.双语字幕.HR-HDTV.AC3.1024X576.x264.mkv"]

# 贪婪匹配，这个2012有问题，但海上钢琴师很好
print("\n====== greedy =====")

def test_pat(pat, msg):
	print(msg)
	m = pat.match(name[4])
	print(m.groups())
	m = pat.match(name[6])
	print(m.groups())

pat = re.compile(r'([\w\s.\']+).(\d{4}).([\w]+)')
test_pat(pat, "gredy:")
pat = re.compile(r'([\w\s.\']+?).(\d{4}).([\w]+)')
test_pat(pat, "shrink:")

# 非贪婪匹配 加?。
print("")
pat = re.compile(r'([\w\s.\']+?).(\d{4}).([\w]+)') #编译运行，提高效率
for val in name:
	m = pat.match(val)
	if m:
		print(m.groups())
	else:
		print("*:", val)

print("\n------ test @names in string --------")
msgs = ["@山长 清一 测试句子123","@0484吴庆可常州 测试句子123","@心灯不灭 测试句子123",
		 "@335王云兰河南 测试句子123","@0047 蒋有超 深圳 测试句子123","@0614张新 广州 测试句子123",
		 "@山长助理李菊英 测试句子123","@023+助李英+舞台上 测试句子123", "@汉中  吕丽 测试句子123"]
for sz in msgs:
	m = re.match(r'(@\d{0,4}[\+\s]?\w{2,10}[\+\s]{0,3}\w{0,6})\s(.+)', sz)
	if m:
		print(m.groups())
	else:
		print("err:", sz)

"""
萍子@爱旅行(44557248)
李冬（中天）(5458190)
御今学堂—李志强(108453172)
摩西<bing-.-xin@qq.com>
0224于凤菊威海<fly-02@qq.com>
ヤ_﹏.葉.(5354319)
La T'ao(757036415)
"""
print("\n------ test names/id --------")
names = ["(188799612)", "<tt6826@163.com>", "糖果~(190690962)",
         "0712 李卫贞 广州(649308223)","张芸溪<tt6826@163.com>", "0138王利利济南(279200540)",
         "萍子@爱旅行(44557248)","摩西<bing-.-xin@qq.com>", "ヤ_﹏.葉.(5354319)",
         "御今学堂—李志强(108453172)","La T'ao(757036415)", "李冬（中天）(5458190)"]
for name in names:
	m = re.match(r'([\d\w\+\s\!\-\~\@]{0,20})[\(\<]([\w\d\~\.\@\-]+[\>\)]$)', name)
	if m:
		print(m.groups())
	else:
		print("err:", name)


from pyglet.gl import *
import pyglet
from pyglet import app, graphics
from pyglet.window import Window, key
import numpy as np
d, d1, d2 = 5, 10, 15
wx, wy = 1.5 * d2, 1.5 * d2
width, height = int(30 * wx), int(30 * wy)
window = Window(visible = True, width = width, height = height, resizable = True)
glClearColor(0.4, 0.4, 0.4, 1.0)
glClear(GL_COLOR_BUFFER_BIT)

def texInit():

    iWidth = iHeight = 64
    n = 3 * iWidth * iHeight
    img = np.full((iWidth, iHeight, 3),255, dtype = 'uint8') #задаем белый цвет
    for i in range(iHeight):
     for j in range(iWidth):
        img[i, j, 0] = (i & 16 ^ j & 16)*255

    img = img.reshape(n)
    img = (GLubyte * n)(*img)

    p, r = GL_TEXTURE_2D, GL_RGB

    glTexParameterf(p, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameterf(p, GL_TEXTURE_WRAP_T, GL_REPEAT)
    glTexParameterf(p, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameterf(p, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
    glTexImage2D(p, 0, r, iWidth, iHeight, 0, r, GL_UNSIGNED_BYTE, img)
    glEnable(p)

texInit()
zv = -d2/2
v0, v1, v2, v3 = (-d2,d2,zv), (-d1,d1,0), (d1,d1,0), (d2,d2,zv)
@window.event
def on_draw():
    window.clear()
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    glOrtho(-wx, wx, -wy, wy, -20, 20)
    glRotatef(60, 1, 0, 0)
    graphics.draw(4, GL_QUADS,
        ('v3f', (v0 + v1 + v2 + v3)),
        ('t2f', (0,1, (d2-d1)/(2*d2),0, (d2+d1)/(2*d2),0, 1,1)))

app.run()


import sys
import os
import datetime
import gzip
import subprocess
from ftplib import FTP
from dateutil import parser
from classes.FtpCli import FtpCli

class CurlManager:
    ftps = {}

    def __init__(self, server):
        self.server = server

        if server in CurlManager.ftps:
            self.ftp = CurlManager.ftps[server]
        else:
            self.ftp = self.__login()
            CurlManager.ftps[server] = self.ftp

    def download(self, path, output_file):
        if not os.path.exists(output_file) or os.path.getsize(output_file) == 0:
            command = ['curl', '-o', output_file, self.server + path]

            log_file = output_file + '.log'
            err_file = output_file + '.err'

            log_fp = open(log_file, 'w')
            err_fp = open(err_file, 'w')

            log_fp.write(' '.join(command) + '\n')
            ret = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            log_fp.write(ret.stdout.decode())
            err_fp.write(ret.stderr.decode())

            log_fp.close()
            err_fp.close()

    def download_gz(self, path, output_file):
        unzip_file = output_file.replace('.gz', '')
        if not os.path.exists(unzip_file) or os.path.getsize(unzip_file) == 0:
            if os.path.exists(output_file):
                os.remove(output_file)
            if os.path.exists(unzip_file):
                os.remove(unzip_file)

            self.download(path, output_file)

            in_fp = gzip.open(output_file, 'rb')
            out_fp = open(unzip_file, 'wb')
            out_fp.write(in_fp.read())
            in_fp.close()
            out_fp.close()

    def __login(self):
        ftp = FTP(self.server)
        ftp.login('anonymous', '')
        return ftp

    def __write_line(fp, string):
        fp.write(string)
        fp.write('\n')


# -*- coding: UTF-8 -*-
import logging

import re
import random

from moneypenny.handler import HandlerType

log = logging.getLogger(__name__)

class Dice(metaclass=HandlerType):
    regex = re.compile(r'(\d*)(W|D)(\d+)', re.I)

    def __init__(self, event, conversation=None):
        match = self.regex.match(event['body'])
        if match.group(1) == '':
            n = 1
        else:
            n = int(match.group(1))
        result = ''
        r = int(match.group(3))
        if n > 10000:
            event.reply('Does not compute.').send()
        else:
            log.info('Throw %s %s-sided dice.' % (n, r))
            for i in range(n):
                result += '%s ' % random.randint(1, r)
            event.reply(result).send()


import os
import re
from tempfile import mkstemp
from osi import run_command
from services import systemctl
import shutil
import time
from datetime import (datetime, timedelta)
import requests
from django.conf import settings

YUM = '/usr/bin/yum'
RPM = '/usr/bin/rpm'
SYSTEMCTL = '/usr/bin/systemctl'
AT = '/usr/bin/at'
YCFILE = '/etc/yum/yum-cron.conf'

def install_pkg(name):
    return run_command([YUM, '--setopt=timeout=600', '-y', 'install', name])

def downgrade_pkgs(*packages):
    cmd = [YUM, '--setopt=timeout=600', '-y', 'downgrade', ]
    for p in packages:
        cmd.append(p)
    return run_command(cmd)

def auto_update(enable=True):
    service = 'yum-cron'
    fo, npath = mkstemp()
    updated = False
    with open(YCFILE) as ifo, open(npath, 'w') as tfo:
        for line in ifo.readlines():
            if (re.match('apply_updates = ', line) is not None):
                if (enable):
                    tfo.write('apply_updates = yes\n')
                else:
                    tfo.write('apply_updates = no\n')
                updated = True
            else:
                tfo.write(line)
    if (not updated):
        raise Exception('apply_updates directive missing in %s, assuming its '
                        'is corrupt. No change made.' % YCFILE)
    shutil.move(npath, YCFILE)
    if (enable):
        systemctl(service, 'enable')
        systemctl(service, 'start')
    else:
        systemctl(service, 'stop')
        systemctl(service, 'disable')

def auto_update_status():
    enabled = False
    with open(YCFILE) as ifo:
        for line in ifo.readlines():
            if (re.match('apply_updates = yes', line) is not None):
                enabled = True
                break
    if (enabled):
        systemctl('yum-cron', 'status')
    return enabled

def current_version():
    out, err, rc = run_command([RPM, '-qi', 'rockstor'], throw=False)
    if (rc != 0):
        return '0.0-0'
    return ('%s-%s' % (out[1].split(':')[-1].strip(),
                       out[2].split(':')[-1].strip()))

def rpm_build_info(pkg):
    version = None
    date = None
    o, e, rc = run_command([RPM, '-qi', pkg])
    for l in o:
        if (re.match('Build Date', l) is not None):
            # eg: Build Date  : Tue 11 Aug 2015 02:25:24 PM PDT
            # we return 2015-Aug-11
            dfields = l.strip().split()
            dstr = ' '.join(dfields[3:7])
            bdate = datetime.strptime(dstr, '%a %d %b %Y')
            bdate += timedelta(days=1)
            date = bdate.strftime('%Y-%b-%d')
        if (re.match('Version ', l) is not None):
            version = l.strip().split()[2]
        if (re.match('Release ', l) is not None):
            version = '%s-%s' % (version, l.strip().split()[2])
    return (version, date)

def switch_repo(subscription, on=True):
    yum_file = '/etc/yum.repos.d/Rockstor-%s.repo' % subscription.name
    if (on):
        with open(yum_file, 'w') as rfo:
            rfo.write('[Rockstor-%s]\n' % subscription.name)
            rfo.write('name=%s\n' % subscription.description)
            if (subscription.password is not None):
                rfo.write('baseurl=http://%s:%s@%s\n' %
                          (subscription.appliance.uuid, subscription.password,
                           subscription.url))
            else:
                rfo.write('baseurl=http://%s\n' % subscription.url)
            rfo.write('enabled=1\n')
            rfo.write('gpgcheck=1\n')
            rfo.write('gpgkey=file://%sconf/ROCKSTOR-GPG-KEY\n'
                      % settings.ROOT_DIR)
            rfo.write('metadata_expire=1m\n')
        os.chmod(yum_file, 600)
    else:
        if (os.path.exists(yum_file)):
            os.remove(yum_file)

def repo_status(subscription):
    if (subscription.password is None):
        return ('active', 'public repo')

    try:
        res = requests.get('http://%s' % subscription.url,
                           auth=(subscription.appliance.uuid,
                                 subscription.password))
        if (res.status_code == 401):
            return ('inactive', res.text)
        elif (res.status_code == 200):
            return ('active', res.text)
        return (res.status_code, res.text)
    except requests.ConnectionError as e:
        e_msg = ('Failed to connect to %s. Is the Rockstor system connected '
                 'to the internet?. Lower level exception: %s'
                 % (subscription.url, e.__str__()))
        raise Exception(e_msg)

def update_check(subscription=None):
    if (subscription is not None):
        switch_repo(subscription)

    pkg = 'rockstor'
    version, date = rpm_build_info(pkg)
    o, e, rc = run_command([YUM, 'changelog', date, pkg])
    log = False
    available = False
    new_version = None
    updates = []
    for l in o:
        if (re.search('Available Packages', l) is not None):
            available = True
        if (not available):
            continue
        if (new_version is None and (re.match('rockstor-', l) is not None)):
            new_version = l.split()[0].split(
                'rockstor-')[1].split('.x86_64')[0]
        if (log is True):
            updates.append(l)
            if (len(l.strip()) == 0):
                log = False
        if (re.match('\* ', l) is not None):
            log = True
    if (new_version is None):
        new_version = version
        # do a second check which is valid for updates without changelog
        # updates. eg: same day updates, testing updates.
        o, e, rc = run_command([YUM, 'update', pkg, '--assumeno'], throw=False)
        if (rc == 1):
            for l in o:
                if (re.search('will be an update', l) is not None):
                    if (re.search('rockstor.x86_64', l) is not None):
                        new_version = l.strip().split()[3].split(':')[1]

    return (version, new_version, updates)

def update_run(subscription=None):
    if (subscription is not None):
        switch_repo(subscription)

    run_command([SYSTEMCTL, 'start', 'atd'])
    fh, npath = mkstemp()
    with open(npath, 'w') as atfo:
        atfo.write('%s stop rockstor\n' % SYSTEMCTL)
        atfo.write('/usr/bin/find %s -name "*.pyc" -type f -delete\n'
                   % settings.ROOT_DIR)
        atfo.write('%s --setopt=timeout=600 -y update\n' % YUM)
        atfo.write('%s start rockstor\n' % SYSTEMCTL)
        atfo.write('/bin/rm -f %s\n' % npath)
    out, err, rc = run_command([AT, '-f', npath, 'now + 1 minutes'])
    time.sleep(120)

    return out, err, rc


import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

from scipy.stats import spearmanr, gaussian_kde, binned_statistic, binned_statistic_2d
from scipy.interpolate import interpn

from .utils import fix_aspect_ratio, bin_stat

def corner_scatter(x,y,ax,**kwargs):
    '''a simple scatter plot'''

    ax.scatter(x,y,**kwargs)

    return 0

def corner_binned_stat(x,y,ax,bins=10,color='black',**kwargs):
    '''a scatter plot with binned trends in y'''

    xlim = ax.get_xlim()
    #x,mean,std = bin_stat(x,y,bins=bins,range=xlim,statistic='median')

    # just ignore nan values
    x, y = x[np.isfinite(x) & np.isfinite(y)], y[np.isfinite(x) & np.isfinite(y)]
    mean, edges, _ = binned_statistic(x,y,statistic='median',bins=bins,range=xlim)
    x = (edges[1:]+edges[:-1])/2

    ax.errorbar(x,mean,yerr=None,fmt='-',color=color,**kwargs)

    return 0

def corner_density_scatter(x,y,ax,nbins=10,**kwargs):
    '''scatter plot with point density from histogram'''

    xlim=ax.get_xlim()
    ylim=ax.get_ylim()

    hist, x_e, y_e = np.histogram2d(x,y,bins=nbins,range=[xlim,ylim],density=True)
    z = interpn((0.5*(x_e[1:] + x_e[:-1]) , 0.5*(y_e[1:]+y_e[:-1]) ),hist,np.vstack([x,y]).T,method="linear",bounds_error=False)
    vmin,vmax=np.nanpercentile(z,[15,95])
    sc=ax.scatter(x,y,c=z,vmin=vmin,vmax=vmax,**kwargs)

    return 0

def corner_density_histogram(x,y,ax,nbins=10,**kwargs):
    '''2d histogram of point density

    https://stackoverflow.com/a/53865762
    '''

    xlim=ax.get_xlim()
    ylim=ax.get_ylim()

    hist, x_e, y_e = np.histogram2d(x,y,bins=nbins,range=[xlim,ylim],density=True)
    hist[hist==0]  = np.nan
    im = ax.imshow(hist.T,origin='lower',extent=[*xlim,*ylim],**kwargs)

    return 0

def corner_gaussian_kde_scatter(x,y,ax,**kwargs):
    '''use a gaussian kernel density estimate for the point density'''

    xy = np.vstack([x,y])
    z = gaussian_kde(xy)(xy)
    idx = z.argsort()
    x, y, z = x[idx], y[idx], z[idx]
    sc=ax.scatter(x,y,c=z,**kwargs)

    return 0

def corner_binned_stat2d(x,y,ax,z,nbins=10,**kwargs):
    '''make a scatter plot of x and y with the color based on binned stats of z'''

    xlim=ax.get_xlim()
    ylim=ax.get_ylim()

    x,y,z = x[~np.isnan(x) & ~np.isnan(y) & ~np.isnan(z)], y[~np.isnan(x) & ~np.isnan(y) & ~np.isnan(z)], z[~np.isnan(x) & ~np.isnan(y) & ~np.isnan(z)]

    stat ,x_e, y_e, _ = binned_statistic_2d(x,y,z,bins=nbins,range=[xlim,ylim])
    z = interpn((0.5*(x_e[1:] + x_e[:-1]) , 0.5*(y_e[1:]+y_e[:-1]) ),stat,np.vstack([x,y]).T,method="nearest",bounds_error=False)
    sc=ax.scatter(x,y,c=z,**kwargs)

    return 0

def corner_binned_stat2d_histogram(x,y,ax,z,nbins=10,**kwargs):
    '''show z binned based on x and y'''

    xlim=ax.get_xlim()
    ylim=ax.get_ylim()

    x,y,z = x[~np.isnan(x) & ~np.isnan(y) & ~np.isnan(z)], y[~np.isnan(x) & ~np.isnan(y) & ~np.isnan(z)], z[~np.isnan(x) & ~np.isnan(y) & ~np.isnan(z)]

    stat ,x_e, y_e, _ = binned_statistic_2d(x,y,z,bins=nbins,range=[xlim,ylim])
    im = ax.imshow(stat.T,origin='lower',extent=[*xlim,*ylim],**kwargs)

    return 0

def corner_binned_percentile(x,y,ax,nbins=10,range=None,n=68,**kwargs):
    '''plot the n percentile of y along the xaxis'''

    x,y = x[~np.isnan(x) & ~np.isnan(y)], y[~np.isnan(x) & ~np.isnan(y)]

    pl, edges, _ = binned_statistic(x,y,statistic=lambda data: np.percentile(data,50+n/2),bins=nbins,range=range)
    pu, edges, _ = binned_statistic(x,y,statistic=lambda data: np.percentile(data,50-n/2),bins=nbins,range=range)
    xp = (edges[1:]+edges[:-1])/2
    ax.fill_between(xp,y1=pl,y2=pu,**kwargs)

def corner_violin(x,y,ax,positions,**kwargs):
    '''create violin plots for the data'''

    #x,y = x[~np.isnan(y)],y[~np.isnan(y)]

    bins = (positions[1:]+positions[:-1])/2
    binned_data = [y[(bins[i]<x) & (x<bins[i+1])] for i in range(len(bins)-1)]

    ax.violinplot(binned_data,positions=positions[1:-1],**kwargs)

    return binned_data

def corner_spearmanr(x,y,ax,position=(0.93,0.93),pvalue=False,**kwargs):
    '''calculate Spearman correlation coefficient'''

    not_nan = ~np.isnan(x) & ~np.isnan(y)
    r,p = spearmanr(x[not_nan],y[not_nan])

    label = r'$\rho'+f'={r:.2f}$'
    if pvalue: label+=f', p={100*p:.3f}\%'
    t = ax.text(*position,label,transform=ax.transAxes,ha='right',va='top',**kwargs)
    t.set_bbox(dict(facecolor='white', alpha=1, ec='white'))

def corner(table,columns,function=None,histogram=False,limits={},labels={},scale={},filename=None,figsize=10,aspect_ratio=1,**kwargs):
    '''Create a pairwise plot for all names in columns

    Parameters
    ----------

    table : table with the data

    columns : list of the columns that should be used

    limits : dictionary with the limits (tuple) for the columns

    labels : dictionary with the axis labels

    filename : name of the file to save to
    '''

    # create a figure with the correct proportions
    nrows, ncols = len(columns)-1, len(columns)-1

    if histogram:
        nrows+=1
        ncols+=1

    fig, axes = plt.subplots(nrows=nrows,ncols=ncols,figsize=(figsize,figsize*aspect_ratio*ncols/nrows))
    for i,row in enumerate(columns[1:]):
        for j,col in enumerate(columns[:-1]):
            ax=axes[i,j]
            if j>i:
                ax.remove()
            else:
                # we need to set the scale before
                ax.set(xscale=scale.get(col,'linear'),yscale=scale.get(row,'linear'))
                if j==0:
                    ax.set_ylabel(labels.get(row,row.replace("_","")))
                else:
                    ax.set_yticklabels([])
                if i==len(columns)-2:
                    ax.set_xlabel(labels.get(col,col.replace("_","")))
                else:
                    ax.set_xticklabels([])

                # we set the axis limit before we plot the data (some functions might need the limits)
                xlim = limits.get(col,[np.min(table[col]),np.max(table[col])])
                ylim = limits.get(row,[np.min(table[row]),np.max(table[row])])
                ax.set(xlim=xlim,ylim=ylim)

                # make sure there are no NaN in the data points
                x,y = table[col],table[row]
                x,y = x[np.isfinite(x) & np.isfinite(y)], y[np.isfinite(x) & np.isfinite(y)]
                #x,y = x[(xlim[0]<=x) & (x<=xlim[1]) & (ylim[0]<=y) & (y<=ylim[1])],y[(xlim[0]<=x) & (x<=xlim[1]) & (ylim[0]<=y) & (y<=ylim[1])]

                # either use a special function for the plot or just use a scatter plot
                if function:
                    function(x,y,ax,xlim=xlim)
                else:
                    ax.scatter(x,y,**kwargs)
            fix_aspect_ratio(ax,aspect_ratio=aspect_ratio)

    plt.subplots_adjust(wspace=0.12, hspace=0.12)

    if filename:
        plt.savefig(filename,dpi=600)

    plt.show()

def corner_by_galaxy(table,sample_table,columns,limits={},labels={},nbins=7,filename=None,figsize=10,aspect_ratio=1,**kwargs):
    '''Create a pairwise plot for all names in columns

    Parameters
    ----------

    table : table with the data

    columns : list of the columns that should be used

    limits : dictionary with the limits (tuple) for the columns

    labels : dictionary with the axis labels

    filename : name of the file to save to
    '''

    cmap = mpl.cm.get_cmap('plasma')
    norm = mpl.colors.Normalize(vmin=9.4,vmax=11)

    # create a figure with the correct proportions
    nrows, ncols = len(columns)-1, len(columns)-1

    fig, axes = plt.subplots(nrows=nrows,ncols=ncols,figsize=(figsize,figsize*aspect_ratio*ncols/nrows))
    for i,row in enumerate(columns[1:]):
        for j,col in enumerate(columns[:-1]):
            ax=axes[i,j]
            if j>i:
                ax.remove()
            else:
                if j==0:
                    ax.set_ylabel(labels.get(row,row.replace("_","")))
                else:
                    ax.set_yticklabels([])
                if i==len(columns)-2:
                    ax.set_xlabel(labels.get(col,col.replace("_","")))
                else:
                    ax.set_xticklabels([])

                # we set the axis limit before we plot the data (some functions might need the limits)
                xlim = limits.get(col,[np.min(table[col]),np.max(table[col])])
                ylim = limits.get(row,[np.min(table[row]),np.max(table[row])])
                ax.set(xlim=xlim,ylim=ylim)

                for k,gal_name in enumerate(np.unique(table['gal_name'])):

                    tmp = table[table['gal_name']==gal_name]

                    # make sure there are no NaN in the data points
                    x,y = tmp[col],tmp[row]
                    x,y = x[(xlim[0]<=x) & (x<=xlim[1]) & (ylim[0]<=y) & (y<=ylim[1])],y[(xlim[0]<=x) & (x<=xlim[1]) & (ylim[0]<=y) & (y<=ylim[1])]
                    x, y = x[~np.isnan(y) & np.isfinite(y)], y[~np.isnan(y) & np.isfinite(y)]

                    if len(x)>0:
                        mean, edges, _ = binned_statistic(x,y,statistic='median',bins=nbins,range=xlim)
                        std, _, _ = binned_statistic(x,y,statistic='std',bins=nbins,range=xlim)
                        x,mean,std = (edges[1:]+edges[:-1])/2,mean,std
                        ax.errorbar(x,mean,fmt='o-',ms=1.5,color=cmap(norm(sample_table.loc[gal_name]['mass'])),label=gal_name)

                x,y = table[col],table[row]
                x,y = x[(xlim[0]<=x) & (x<=xlim[1]) & (ylim[0]<=y) & (y<=ylim[1])],y[(xlim[0]<=x) & (x<=xlim[1]) & (ylim[0]<=y) & (y<=ylim[1])]
                x, y = x[~np.isnan(y) & np.isfinite(y)], y[~np.isnan(y) & np.isfinite(y)]

                mean, edges, _ = binned_statistic(x,y,statistic='median',bins=7,range=xlim)
                std, _, _ = binned_statistic(x,y,statistic='std',bins=7,range=xlim)
                x,mean,std = (edges[1:]+edges[:-1])/2,mean,std
                ax.errorbar(x,mean,fmt='o-',ms=1.5,color='black',label=gal_name)

            fix_aspect_ratio(ax,aspect_ratio=aspect_ratio)

    plt.subplots_adjust(wspace=0.12, hspace=0.12)

    if filename:
        plt.savefig(filename,dpi=600)

    plt.show()


from Sandbox.Libraries.DEV import GITLibrary
import git
import os
import time
from git import *
import shutil

LocalRepositoryName = "Local"
LocalDirectoryPath = 'C:\\Devworkspace\\GitWorkspace\\new\\RepOne'
LocalGITRepo = 'C:\\Users\\SANUMOLU'
giturl = 'https://github.com/seshikanth/RepoOne.git'
sourcepath = 'C:\\Devworkspace\\GitWorkspace\\files\\'
LocalRepoPath = 'C:\\Devworkspace\\GitWorkspace\\new\\RepOne'
DirectoryName = "Hello9920"
destinationpath = LocalDirectoryPath + '\\' +  DirectoryName
RepositoryName = "RepOne"

print("LocalDirectoryPath "  +  str(os.path.isdir(destinationpath)))
print("LocalDirectoryPath1 "  +  str(os.path.exists(destinationpath)))
print("LocalDirectoryPath2 "  +  str(os.path.abspath(destinationpath)))
print("LocalDirectoryPath3 "  +  str(os.path.isdir(os.path.abspath(destinationpath))))
print("LocalDirectoryPath4  "  +  str(os.path.exists(os.path.abspath(destinationpath))))

if os.path.exists(destinationpath):
    print("directory exists")
else:
    os.mkdir(LocalDirectoryPath + '\\' + DirectoryName)


import tkinter as tk
import customtkinter as ctk

from PIL.Image import open as OpenImage

from settings import StyleElements as style
from settings import Paths as paths

from CTkMessagebox import CTkMessagebox

class DataView(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent, bg=style.CLR_BACKGROUND)

        self.controller = controller

        if self.controller.options['n_users'] == 1:
            self.populate_UI_1user()
        else:
            self.populate_UI_2users()
        return

    def back(self):
        self.controller.stop_measurement_thread()
        self.controller.back()
        return

    def end(self):
        self.controller.stop_measurement()
        return

    def recalibrate(self):
        print('recalibrate')
        self.controller.update_calibration_factor()
        return

    def populate_navigation_bar(self):
        # ------------------
        #   IMAGES USED
        # ------------------
        IC_BACK = ctk.CTkImage(OpenImage(paths.PATH_IC_BACK), size=(40,40))
        IC_CALIBRATE = ctk.CTkImage(OpenImage(paths.PATH_IC_CALIBRATE), size=(64,51))
        IC_END = ctk.CTkImage(OpenImage(paths.PATH_IC_END), size=(40,51))

        # ------------------
        #   NAVIGATION BAR
        # ------------------
        btn_back = ctk.CTkButton(master=self,
                                 width=40,
                                 height=51,
                                 text="",
                                 bg_color=style.CLR_BACKGROUND,
                                 fg_color=style.CLR_BACKGROUND,
                                 command=self.back,
                                 image=IC_BACK)
        btn_back.place(x=16, y=4)

        btn_recalibrate = ctk.CTkButton(master=self,
                                 width=64,
                                 height=51,
                                 text="",
                                 bg_color=style.CLR_BACKGROUND,
                                 fg_color=style.CLR_BACKGROUND,
                                 command=self.recalibrate,
                                 image=IC_CALIBRATE)
        btn_recalibrate.place(x=646, y=4)

        btn_end = ctk.CTkButton(master=self,
                                 width=40,
                                 height=51,
                                 text="",
                                 bg_color=style.CLR_BACKGROUND,
                                 fg_color=style.CLR_BACKGROUND,
                                 command=self.end,
                                 image=IC_END)
        btn_end.place(x=726, y=4)
        return

    def populate_UI_1user(self):
        # Navigation bar
        self.populate_navigation_bar()

        # Frame Top Left
        tl_frame = tk.Frame(self, width=368, height=130, bg= style.CLR_PRIMARY,
                            highlightthickness=1, highlightcolor=style.CLR_ONBACKGROUND)
        tl_frame.place(anchor='nw', x=24, y=64)

        time_txt = tk.Label(tl_frame, text="TIME [s]", font= style.FNT_CAPTION,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height = 16)
        time_txt.place(anchor='ne', x=364, y=0, height=16)

        self.time_var = tk.DoubleVar()
        time_data = tk.Label(tl_frame, textvariable=self.time_var, font= style.FNT_DATA,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height=106)
        time_data.place(anchor='ne', x=364, y=20, height=106)

        # Frame Top Right
        tr_frame = tk.Frame(self, width=368, height=130, bg= style.CLR_PRIMARY,
                            highlightthickness=1, highlightcolor=style.CLR_ONBACKGROUND)
        tr_frame.place(anchor='nw', x=384+24, y=0+64)

        pf_txt = tk.Label(tr_frame, text="PEAK FORCE [kg]", font= style.FNT_CAPTION,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height = 16)
        pf_txt.place(anchor='ne', x=364, y=0, height=16)

        self.pf_var = tk.DoubleVar()
        pf_data = tk.Label(tr_frame, textvariable=self.pf_var, font= style.FNT_DATA,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height=106)
        pf_data.place(anchor='ne', x=364, y=20, height=106)

        # Frame Mid Right
        mr_frame = tk.Frame(self, width=368, height=130, bg= style.CLR_PRIMARY,
                            highlightthickness=1, highlightcolor= style.CLR_ONBACKGROUND)
        mr_frame.place(anchor='nw', x=384+24, y=138+64)

        watt_txt = tk.Label(mr_frame, text="POWER [W]", font= style.FNT_CAPTION,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height = 16)
        watt_txt.place(anchor='ne', x=364, y=0, height=16)

        self.power_var = tk.DoubleVar()
        watt_data = tk.Label(mr_frame, textvariable=self.power_var, font= style.FNT_DATA,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height=106)
        watt_data.place(anchor='ne', x=364, y=20, height=106)

        # Frame Mid Left
        ml_frame = tk.Frame(self, width=368, height=130, bg= style.CLR_PRIMARY,
                            highlightthickness=1, highlightcolor= style.CLR_ONBACKGROUND)
        ml_frame.place(anchor='nw', x=0+24, y=138+64)

        mf_txt = tk.Label(ml_frame, text="MEAN FORCE [kg]", font= style.FNT_CAPTION,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height = 16)
        mf_txt.place(anchor='ne', x=364, y=0, height=16)

        self.mf_var = tk.DoubleVar()
        mf_data = tk.Label(ml_frame, textvariable=self.mf_var, font= style.FNT_DATA,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height=106)
        mf_data.place(anchor='ne', x=364, y=20, height=106)

        # Frame Bottom
        b_frame = tk.Frame(self, width=750, height= 138, bg=style.CLR_SECONDARY,
                                highlightthickness=1, highlightcolor= style.CLR_ONBACKGROUND)
        b_frame.place(anchor='nw', x=0+24 , y=276+64)

        mf_txt = tk.Label(b_frame, text="FORCE [kg]", font= style.FNT_CAPTION,
                            bg= style.CLR_PRIMARY, fg= style.CLR_ONBACKGROUND, height = 16)
        mf_txt.place(anchor='ne', x=746, y=0, height=16)
        return

    def populate_UI_2users(self):
        # Navigation bar
        self.populate_navigation_bar()

        # Frame Top Left
        clr_tl = style.CLR_GREY75
        tl_frame = tk.Frame(self, width=368, height=130, bg=clr_tl,
                            highlightthickness=0, highlightcolor=style.CLR_BLACK)
        tl_frame.place(anchor='nw', x=24, y=64)

        time_txt = tk.Label(tl_frame, text="TIME [s]", font= style.FNT_CAPTION,
                            bg=clr_tl, fg= style.CLR_ONBACKGROUND, height = 16)
        time_txt.place(anchor='nw', x=4, y=0, height=16)

        self.time_var = tk.DoubleVar()
        time_data = tk.Label(tl_frame, textvariable=self.time_var, font= style.FNT_DATA,
                            bg=clr_tl, fg= style.CLR_ONBACKGROUND, height=106)
        time_data.place(anchor='nw', x=4, y=20, height=106)

        # Frame Top Right
        clr_tr = style.CLR_GREY75
        tr_frame = tk.Frame(self, width=368, height=130, bg=clr_tr,
                            highlightthickness=0, highlightcolor=style.CLR_BLACK)
        tr_frame.place(anchor='nw', x=384+24, y=0+64)

        velocity_txt = tk.Label(tr_frame, text="VELOCITY [m/s]", font= style.FNT_CAPTION,
                            bg=clr_tr, fg= style.CLR_ONBACKGROUND, height = 16)
        velocity_txt.place(anchor='ne', x=364, y=0, height=16)

        self.velocity_var = tk.DoubleVar()
        velocity_data = tk.Label(tr_frame, textvariable=self.velocity_var, font= style.FNT_DATA,
                            bg=clr_tr, fg= style.CLR_ONBACKGROUND, height=106)
        velocity_data.place(anchor='ne', x=364, y=20, height=106)

        # Frame Mid Right
        clr_user2 = style.CLR_PRIMARY
        mr_frame = tk.Frame(self, width=368, height=130, bg=clr_user2,
                            highlightthickness=1, highlightcolor= style.CLR_ONBACKGROUND)
        mr_frame.place(anchor='nw', x=384+24, y=138+64)

        force2_txt = tk.Label(mr_frame, text="FORCE 2 [kg]", font= style.FNT_CAPTION,
                            bg=clr_user2, fg= style.CLR_ONBACKGROUND, height = 16)
        force2_txt.place(anchor='ne', x=364, y=0, height=16)

        self.force2_var = tk.DoubleVar()
        force2_data = tk.Label(mr_frame, textvariable=self.force2_var, font= style.FNT_DATA,
                            bg=clr_user2, fg= style.CLR_ONBACKGROUND, height=106)
        force2_data.place(anchor='ne', x=364, y=20, height=106)

        # Frame Mid Left
        clr_user1 = style.CLR_SECONDARY
        ml_frame = tk.Frame(self, width=368, height=130, bg=clr_user1,
                            highlightthickness=1, highlightcolor= style.CLR_ONBACKGROUND)
        ml_frame.place(anchor='nw', x=0+24, y=138+64)

        force1_txt = tk.Label(ml_frame, text="FORCE 1 [kg]", font= style.FNT_CAPTION,
                            bg=clr_user1, fg= style.CLR_ONBACKGROUND, height = 16)
        force1_txt.place(anchor='nw', x=4, y=0, height=16)

        self.force1_var = tk.DoubleVar()
        force1_data = tk.Label(ml_frame, textvariable=self.force1_var, font= style.FNT_DATA,
                            bg=clr_user1, fg= style.CLR_ONBACKGROUND, height=106)
        force1_data.place(anchor='nw', x=4, y=20, height=106)

        # Frame Bottom Left
        bl_frame = tk.Frame(self, width=368, height=138, bg=clr_user1,
                            highlightthickness=1, highlightcolor=style.CLR_ONBACKGROUND)
        bl_frame.place(anchor='nw', x=0+24, y=276+64)

        pf1_txt = tk.Label(bl_frame, text="PEAKFORCE 1 [kg]", font= style.FNT_CAPTION,
                            bg=clr_user1, fg= style.CLR_ONBACKGROUND, height = 16)
        pf1_txt.place(anchor='nw', x=4, y=0, height=16)

        self.pf1_var = tk.DoubleVar()
        pf1_data = tk.Label(bl_frame, textvariable=self.pf1_var, font= style.FNT_DATA,
                            bg=clr_user1, fg= style.CLR_ONBACKGROUND, height=106)
        pf1_data.place(anchor='nw', x=4, y=20, height=106)

        # Frame Bottom Right
        br_frame = tk.Frame(self, width=368, height=138, bg=clr_user2,
                            highlightthickness=1, highlightcolor= style.CLR_ONBACKGROUND)
        br_frame.place(anchor='nw', x=384+24, y=276+64)

        pf2_txt = tk.Label(br_frame, text="PEAKFORCE 2 [kg]", font= style.FNT_CAPTION,
                            bg=clr_user2, fg= style.CLR_ONBACKGROUND, height = 16)
        pf2_txt.place(anchor='ne', x=364, y=0, height=16)

        self.pf2_var = tk.DoubleVar()
        pf2_data = tk.Label(br_frame, textvariable=self.pf2_var, font= style.FNT_DATA,
                            bg=clr_user2, fg= style.CLR_ONBACKGROUND, height=106)
        pf2_data.place(anchor='ne', x=364, y=20, height=106)
        return

    def display_time(self, time):
        self.time_var.set(round(time,1))

    def display_data_1p(self, data):
        self.force = data[0]
        self.power_var.set(round(data[1], 0))
        self.mf_var.set(round(data[2], 0))
        self.pf_var.set(round(data[3], 0))

    def display_data_2p(self, data):
        self.force1_var.set(round(data[0], 0))
        self.force2_var.set(round(data[1], 0))
        self.velocity_var.set(round(data[2], 2))
        self.pf1_var.set(round(data[3], 0))
        self.pf2_var.set(round(data[4], 0))

    def display_msg(self):
        # Display messagebox
        ctk.set_appearance_mode('light')
        CTkMessagebox(title="Data is being processed",
                          message="Your data is being processed, and can be found in the user profiles once its done",
                          icon='check', option_1='OK')

    def display_errormsg(self):
        # Display messagebox
        ctk.set_appearance_mode('light')
        CTkMessagebox(title="Error",
                          message="No data from peripherals was found, check that the sensors are connected properly and that the peripherals are running",
                          icon='check', option_1='OK')


import random
import numpy as np
import matplotlib.pyplot as plt

n=3 #Количество циклов
X=[]
Y=[]
q=1000 #Количество точек на каждом отрезке

for i in range(n):

    x=np.linspace(0, 10, q)
    y=(np.exp(-5+x))/(1+np.exp(-5+x)) #Логарифмический закон. Взято из интернета, возможна замена
    Y.extend(y)

    y=np.exp(-1/2*x) #Экспоненциальный закон. Подобрал для красоты, требуется замена
    y[0]=Y[-1]  #Убирает пики при переходе  одного графика в другой (Бесило пзд)
    y[1]=Y[-2]

    Y.extend(y)

    x=np.linspace(i*10*2, (i+1)*20, 2*q)

    X.extend(x)

plt.plot(X,Y)
plt.show()


import torch
from torch import nn
from torch.nn import init

# JK
def init_weights(net, init_type='normal', init_gain=0.02):
    """Initialize network weights.

    Parameters:
        net (network)   -- network to be initialized
        init_type (str) -- the name of an initialization method: normal | xavier | kaiming | orthogonal
        init_gain (float)    -- scaling factor for normal, xavier and orthogonal.

    We use 'normal' in the original pix2pix and CycleGAN paper. But xavier and kaiming might
    work better for some applications. Feel free to try yourself.
    """

    def init_func(m):  # define the initialization function
        classname = m.__class__.__name__
        if hasattr(m, 'weight') and (classname.find('Conv') != -1 or classname.find('Linear') != -1):
            if init_type == 'normal':
                init.normal_(m.weight.data, 0.0, init_gain)
            elif init_type == 'xavier':
                init.xavier_normal_(m.weight.data, gain=init_gain)
            elif init_type == 'kaiming_uniform':
                init.kaiming_uniform(m.weight.data, a=0, mode='fan_in')
                init.kaiming_uniform(m.bias.data, a=0, mode='fan_in')
            elif init_type == 'orthogonal':
                init.orthogonal_(m.weight.data, gain=init_gain)
                init.orthogonal_(m.bias.data, gain=init_gain)
            else:
                raise NotImplementedError('initialization method [%s] is not implemented' % init_type)
            if hasattr(m, 'bias') and m.bias is not None:
                init.constant_(m.bias.data, 0.0)
        elif classname.find(
                'BatchNorm2d') != -1:  # BatchNorm Layer's weight is not a matrix; only normal distribution applies.
            init.normal_(m.weight.data, 1.0, init_gain)
            init.constant_(m.bias.data, 0.0)

    print('initialize network with %s' % init_type)
    net.apply(init_func)

class ScoreModel(nn.Module):
    def __init__(self, clamp=False, masked_feat_id=None):
        super(ScoreModel, self).__init__()
        self.clamp = clamp
        self.masked_feat_id = masked_feat_id

    def compute_score(self, x):
        raise NotImplementedError

    def forward(self, x):
        if self.masked_feat_id is not None:
            x = x.index_fill(-1, torch.tensor(self.masked_feat_id, dtype=torch.long, device=x.device), 0.0)
        score = self.compute_score(x)
        if self.clamp:
            score = torch.clamp(score, -10, 10)
        return score

class LinearModel(ScoreModel):

    #One layer simple linear model

    def __init__(self, input_dim=2, **kwargs):
        super(LinearModel, self).__init__(**kwargs)
        self.input_dim = input_dim
        self.w = nn.Linear(input_dim, 1, bias=True)

    def compute_score(self, x):
        h = self.w(x)
        return h

"""   original
class MLP(ScoreModel):
    def __init__(self, input_dim: int, hidden_layer: int, dropout: float = 0.0, **kwargs):
        super(MLP, self).__init__(**kwargs)
        self.input_size = input_dim
        fcs = []
        last_size = self.input_size
        for _ in range(hidden_layer):
            size = last_size // 2
            linear = nn.Linear(last_size, size)
            linear.bias.data.fill_(0.0)
            fcs.append(linear)
            last_size = size
            fcs.append(nn.ReLU())
            if dropout > 0.0:
                fcs.append(nn.Dropout(dropout))
        self.fc = nn.Sequential(*fcs)
        linear = nn.Linear(last_size, 1)
        linear.bias.data.fill_(0.0)
        self.final_layer = linear

    def compute_score(self, x):
        out = self.fc(x)
        out = self.final_layer(out)
        return out
"""

# Batch Norm version
class MLP(ScoreModel):
    def __init__(self, input_dim: int, hidden_layer: int, dropout: float = 0.0, **kwargs):
        super(MLP, self).__init__(**kwargs)
        self.input_size = input_dim
        fcs = []
        last_size = self.input_size
        for _ in range(hidden_layer):
            size = last_size // 2
            linear = nn.Linear(last_size, size)
            bn = nn.BatchNorm1d(20)  #JK

            #bn = nn.LayerNorm(size)  #JK
            linear.bias.data.fill_(0.0)
            fcs.append(linear)
            use_bn = False #JK
            if use_bn:    #JK
                fcs.append(bn) #JK
            last_size = size
            fcs.append(nn.ReLU())
            if dropout > 0.0:
                fcs.append(nn.Dropout(dropout))
        self.fc = nn.Sequential(*fcs)
        linear = nn.Linear(last_size, 1)
        linear.bias.data.fill_(0.0)
        self.final_layer = linear

    def compute_score(self, x):

        out = self.fc(x)
        out = self.final_layer(out)
        return out

# MLP for predicting an embedding of the group composition.
# Differs from the main MLP in that each item does not get its own score
# Items are aggregated to a group composition embedding to help predict accurate
class MLPGroupEmbedding(nn.Module):
    def __init__(self, input_dim: int, hidden_layer: int, dropout: float = 0.0, clamp = False):
        super(MLPGroupEmbedding, self).__init__()
        self.input_size = input_dim
        self.clamp = clamp
        fcs = []
        input_size = self.input_size
        for _ in range(hidden_layer):
            #size = last_size // 2
            #linear = nn.Linear(last_size, size)
            linear = nn.Linear(input_size, input_size)
            linear.bias.data.fill_(0.0)
            fcs.append(linear)
            bn = nn.BatchNorm1d(20)    # JK 1005
            #last_size = size
            fcs.append( bn )           # JK 1005
            fcs.append(nn.ReLU())
            if dropout > 0.0:
                fcs.append(nn.Dropout(dropout))
        self.fc = nn.Sequential(*fcs)
        linear = nn.Linear(input_size, input_size)
        linear.bias.data.fill_(0.0)
        self.final_layer = linear

    def forward(self, x):
        out = self.fc(x)
        out = self.final_layer(out)
        if self.clamp:
            out = torch.clamp(out, -10, 10)
        return out

# JK
class SiameseMLP(nn.Module):
    def __init__(self, ScoreModel, GroupCompModel):
        super(SiameseMLP, self).__init__()
        self.gcm = GroupCompModel
        self.sm  = ScoreModel

    def forward(self,x,gid):

        out_sm  = self.sm(x)
        out_gcm = self.gcm(gid)

        return out_sm, out_gcm

# This version concatenates item scores with group id vector, and
# aggregates to size-N^2 score embedding for the QP
class MLPQuadScore(nn.Module):
    def __init__(self, ScoreModel, list_len):
        super(MLPQuadScore, self).__init__()
        self.sm  = ScoreModel
        self.fc1 = nn.Linear(2*list_len,list_len**2)
        self.fc2 = nn.Linear(list_len**2,list_len**2)
        self.fc1.bias.data.fill_(0.0)
        self.fc2.bias.data.fill_(0.0)
        self.bn  = nn.BatchNorm1d(400)

    def forward(self,x,gid):

        out_sm  = self.sm(x)

        #out = torch.cat( (out_sm.squeeze(-1),gid),1 )
        out = torch.cat( (out_sm.view(gid.shape),gid),1 )
        out = self.fc1(out)
        out = self.bn(out)
        out = self.fc2(out)

        return out

def convert_vars_to_gpu(varlist):
    return [var.cuda() for var in varlist]

# for Zehlike baseline 11/8

class NNModel(nn.Module):
    """
    Neural network model
    """

    def __init__(self,
                 hidden_layer=64,
                 D=2,
                 dropout=0.0,
                 init_weight1=None,
                 init_weight2=None,
                 pooling=False,
                 clamp=False):
        self.input_dim = D
        super(NNModel, self).__init__()
        self.fc = nn.Linear(D, hidden_layer, bias=True)
        self.fc_drop = nn.Dropout(p=dropout)
        # self.activation = nn.ReLU()
        self.activation = nn.ReLU()
        if pooling == "concat_avg":
            self.fc2 = nn.Linear(2 * hidden_layer, hidden_layer, bias=True)
            self.fc3 = nn.Linear(hidden_layer, 1, bias=True)
        elif pooling is not False:
            self.fc2 = nn.Linear(hidden_layer, hidden_layer, bias=True)
            self.fc3 = nn.Linear(hidden_layer, 1, bias=True)
        else:
            self.fc2 = nn.Linear(hidden_layer, 1, bias=True)
        self.softmax = nn.Softmax(dim=1)
        if init_weight1 is not None:
            self.fc.weight = torch.nn.Parameter(init_weight1)
        if init_weight2 is not None:
            self.fc2.weight = torch.nn.Parameter(init_weight2)
        self.pooling_layer = pooling
        self.clamp = clamp

    def forward(self, x):
        h = self.activation(self.fc(x))
        h = self.fc_drop(h)
        if self.pooling_layer:
            if self.pooling_layer == "average":
                h1 = h - torch.mean(h, dim=0, keepdim=True)
            elif self.pooling_layer == "max":
                h1 = h - torch.max(h, dim=0, keepdim=True)
            elif self.pooling_layer == "concat_avg":
                h1 = torch.cat(
                    (h, torch.mean(h, dim=0, keepdim=True).repeat(
                        x.size()[0], 1)),
                    dim=1)
        else:
            h1 = h
        h2 = self.fc2(h1)
        if self.pooling_layer:
            h3 = self.fc3(self.activation(h2))
            return h3 if not self.clamp else torch.clamp(h3, -10, 10)

        else:
            return h2 if not self.clamp else torch.clamp(h2, -10, 10)


from A import *
from hypothesys import *

from scipy.stats import entropy
import numpy as np
import matplotlib.pyplot as plt

def get_probabilities_digitized(activations_list, max, nbins):
    activations = np.array(activations_list)
    (probs, bins, _) = plt.hist(activations, bins=nbins,
                                weights=np.ones_like(activations) / len(activations), range=(0, max))
    plt.show()
    return probs

def measure_discrim_power_of_hypo_onto_descr(pics, hypothesys, descriptor, nbins):
    Afields = []
    ABfields = []
    for pic in pics:
        Afield = descriptor.apply_to_pic(pic)
        Bfield = hypothesys.apply_to_all_pic(pic)
        ABfield = Afield * Bfield
        Afields = Afields + Afield.flatten().tolist()
        ABfields = ABfields + ABfield.flatten().tolist()
    Afields_hist = get_probabilities_digitized(Afields, 1, nbins)
    AB_fields_hist = get_probabilities_digitized(ABfields, 1, nbins)
    discrim_power = measure_discrim_power(Afields_hist, AB_fields_hist)
    return discrim_power

def measure_discrim_power(Afields_hist, AB_fields_hist):
    w=1
    sum=0 #чем больше тем лучше
    for i in range(len(Afields_hist)-1,-1,-1):
        print(i)
        before = Afields_hist[i]
        after = AB_fields_hist[i]
        increase = before - after
        sum+=increase * w
        w=w/1.5

    return sum

def eval_many_hypos_for_descriptor(pics, hypotheses_list, descriptor, nbins):
    evaluations=[]
    for hypo in hypotheses_list:
        evaluation = measure_discrim_power_of_hypo_onto_descr(pics, hypo, descriptor, nbins)
        evaluations.append(evaluation)
    return evaluations


import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import argrelextrema
from pandas import ExcelWriter
import os
import openpyxl

choice = input("Would you like to just do computations on one file (1) or combine multiple .csv files into an .xlsx file (2): ")

if choice == '2':
    csv_files = [] # Initialize an empty list

    while True: # Continue prompting the user until they choose to stop
        csv_file = input("Enter the full path of a .csv file (or 'quit' to finish): ")

        if csv_file.lower() == 'quit':
            break

        # Checks to see if file exists
        if not os.path.isfile(csv_file):
            print("The file does not exist. Please check the path and try again.")
        else:
            # Append entered file to the list
            csv_files.append(csv_file)

    # Prompts user to enter the output file name with .xlsx
    outputfileName = input("What would you like the output file to be named (with .xlsx): ")
    # Create a Pandas Excel writer using XlsxWriter as the engine
    with ExcelWriter(outputfileName) as writer:
        # Iterate through all the .csv files and add each one to a separate worksheet
        for csv_file in csv_files:
            df = pd.read_csv(csv_file)
            # Use the .csv file name (without extension) as the name of the worksheet
            df.to_excel(writer, sheet_name=os.path.splitext(os.path.basename(csv_file))[0], index=False)
        writer.save()

        # Input which worksheet to work with
    looper = 1
    while looper == 1:
        selectSheet = input("Enter which worksheet you would like to work with (for list, enter 'GiveList'): ")
        if selectSheet == 'GiveList':
            #Lists all the worksheets inside .xlsx file
            xls = pd.ExcelFile(outputfileName)
            sheet_list = xls.sheet_names
            print(sheet_list)
        else:
            # Reads the new Excel file skipping the first 17 lines inside the respective worksheet
            df = pd.read_excel(outputfileName, header=None, skiprows = 17, sheet_name = selectSheet)
            looper = 0

elif choice == '1':
    filename = input("Please input filename with respective path and type: ")
    #filename = r"C:\Users\Jakey\Desktop\Summer_2023_Research\Python\780-AuNS-3_0.5-S1.1" + '.xlsx'
    #filename = r"C:\Users\movaq\OneDrive\Spring 2023\Gold Nano-star\data\python_data" + '.xlsx'

    # If the filetype is .xlsx which includes multiple worksheets
    if filename.endswith('.xlsx'):
        # Input which worksheet to work with
        looper = 1
        while looper == 1:
            selectSheet = input("Enter which worksheet you would like to work with (for list, enter 'GiveList'): ")
            if selectSheet == 'GiveList':
                #Lists all the worksheets inside .xlsx file
                xls = pd.ExcelFile(filename)
                sheet_list = xls.sheet_names
                print(sheet_list)
            else:
                # Reads the new Excel file skipping the first 17 lines inside the respective worksheet
                df = pd.read_excel(filename, header=None, skiprows = 17, sheet_name = selectSheet)
                looper = 0

    # If the filetype is .csv which only has 1 worksheet
    elif filename.endswith('.csv'):
        # Reads the new .csv file skipping the first 17 lines
        df = pd.read_csv(filename, header=None, skiprows = 17)
    else:
        raise ValueError('Invalid file type. Please use .xlsx or .csv file')

else:
    raise ValueError('Invalid choice')

# Create a sample dataframe with header names
header_names = input("Enter the header names separated by commas: ").split(',')

df.columns = header_names

# display the dataframeg
print(df)

# Get the header name of the first column
x_col = df.columns[0]

# Filter the data between 450 and 1600
df = df[(df[x_col] >= 450) & (df[x_col] <= 1600)]

# Plot all the spectra with the first column as the x-axis
for i in range(1, df.shape[1]):
    # Fit a polynomial to the data
    z = np.polyfit(df[x_col], df.iloc[:, i], 30)
    p = np.poly1d(z)

    # Subtract the minimum value of the polynomial fit from the data to get the baseline-corrected data
    baseline_corrected_data = df.iloc[:, i] - p(df[x_col])
    min_value = np.min(baseline_corrected_data)
    baseline_corrected_data += abs(min_value)

    # Bring all the negative values on the curve to position one
    if np.min(baseline_corrected_data) < 0:
        negative_values = baseline_corrected_data[baseline_corrected_data < 0]
        baseline_corrected_data[baseline_corrected_data < 0] = 1
        baseline_corrected_data[0:len(negative_values)] = negative_values

    # Plot the baseline-corrected data
    plt.plot(df[x_col], baseline_corrected_data, label=df.columns[i])

    # Find the indices of the local maxima of the data
    maxima_indices = argrelextrema(baseline_corrected_data.values, np.greater)

    # Sort the indices based on the value of the data at the indices
    maxima_indices = maxima_indices[0][np.argsort(-baseline_corrected_data.values[maxima_indices])]

    # Mark the top three highest peaks with markers on the plot
    #for j in range(min(3, len(maxima_indices))):
     #   plt.plot(df[x_col].values[maxima_indices[j]], baseline_corrected_data.values[maxima_indices[j]], 'ro')

# Add a straight line
plt.axvspan(1186, 1226, color='blue', alpha=0.3)
plt.axvspan(1250, 1290, color='black', alpha=0.3)
plt.axvspan(1447, 1487, color='yellow', alpha=0.3)
plt.axvspan(1495, 1535, color='red', alpha=0.3)

plt.text(1206 -50, 18000, '[1206] \nC-H \nrocking of \ncyclohexene ring', ha='right')
plt.text(1270 + 10, 15000, '[1270] \nC-H \nrocking of \nphenyl ring', ha='left')
plt.text(1467 -10, 5000, '[1467] \nC=C \nbending of \nphenyl', ha='right')

plt.text(1515 + 50, 4500, '[1515] \nC-H \nstretching \nof phenyl', ha='left')

# add a title and labels to the axes
#plt.title('SERS measurement of silica coated, IR780 encapsulated GNS')
plt.xlabel('Wavenumber (cm$^{-1}$)', fontsize=12)
plt.ylabel('Raman Intensity (counts)', fontsize=12)

# Set the minimum value of the y-axis to be lower than the minimum value of the data
plt.ylim(bottom=min(df.iloc[:, 1:].min().min(), 0))

# Add a legend
plt.legend(loc='upper left')

# display the plot
plt.show()

# Plot all the spectra with the first column as the x-axis
fig, ax = plt.subplots()
fig.set_size_inches(6, 4)
x = df[x_col].values
y_offset = 1

for i in range(1, df.shape[1]):
    # Fit a polynomial to the data
    z = np.polyfit(x, df.iloc[:, i], 3)
    p = np.poly1d(z)

    # Subtract the polynomial fit from the data to get the baseline-corrected data
    baseline_corrected_data = df.iloc[:, i] - p(x) + y_offset
    min_value = np.min(baseline_corrected_data)
    baseline_corrected_data += abs(min_value)

    # Plot the baseline-corrected data with an offset in the y-axis
    ax.plot(x, baseline_corrected_data, label=df.columns[i])

    # Find the indices of the local maxima of the data
    maxima_indices = argrelextrema(baseline_corrected_data.values, np.greater)

    # Sort the indices based on the value of the data at the indices
    maxima_indices = maxima_indices[0][np.argsort(-baseline_corrected_data.values[maxima_indices])]

    # Mark the top three highest peaks with markers on the plot
    for j in range(min(4, len(maxima_indices))):
        ax.plot(x[maxima_indices[j]], baseline_corrected_data.values[maxima_indices[j]], 'ro')

    # Increment the y offset for the next plot
    y_offset += 25000

# Add labels to the axes
#ax.set_title('Baseline-corrected spectra', fontsize=14, loc="center")
ax.set_xlabel('Wavenumber (cm$^{-1}$)', fontsize=12)
ax.set_ylabel('Raman intensity', fontsize=12)

# Display the legend
ax.legend(loc = 'upper right')

# Display the plot
plt.show()

# Create empty lists to store the highest peak, second highest peak, and third highest peak of each spectrum and its corresponding sample name
highest_peaks = []
second_highest_peaks = []
third_highest_peaks = []

sample_names = []

highest_wavenumbers = []
second_highest_wavenumbers = []
third_highest_wavenumbers = []

# Loop through each spectrum in the DataFrame
for i in range(1, df.shape[1]):
    # Fit a polynomial to the data
    z = np.polyfit(df[x_col], df.iloc[:, i], 3)
    p = np.poly1d(z)

    # Subtract the minimum value of the polynomial fit from the data to get the baseline-corrected data
    baseline_corrected_data = df.iloc[:, i] - p(df[x_col])
    min_value = np.min(baseline_corrected_data)
    baseline_corrected_data += abs(min_value)

    # Find the indices of the local maxima of the data
    maxima_indices = argrelextrema(baseline_corrected_data.values, np.greater)

    # Sort the indices based on the value of the data at the indices
    sorted_maxima_indices = maxima_indices[0][np.argsort(-baseline_corrected_data.values[maxima_indices])]

    # Add the highest peak, second highest peak, third highest peak value and sample name to the corresponding lists
    highest_peaks.append(baseline_corrected_data.values[sorted_maxima_indices[0]])
    second_highest_peaks.append(baseline_corrected_data.values[sorted_maxima_indices[1]])
    third_highest_peaks.append(baseline_corrected_data.values[sorted_maxima_indices[2]])

    highest_wavenumbers.append(df[x_col].values[sorted_maxima_indices[0]])
    second_highest_wavenumbers.append(df[x_col].values[sorted_maxima_indices[1]])
    third_highest_wavenumbers.append(df[x_col].values[sorted_maxima_indices[2]])

    sample_names.append(df.columns[i])

# Compute the mean and standard deviation of the highest peaks
mean_highest_peak = np.mean(highest_peaks)
std_highest_peak = np.std(highest_peaks)

mean_second_highest_peak = np.mean(second_highest_peaks)
std_second_highest_peak = np.std(second_highest_peaks)

mean_third_highest_peak = np.mean(third_highest_peaks)
std_third_highest_peak = np.std(third_highest_peaks)

# Compute the RSD of the highest peaks
rsd_highest_peak = (std_highest_peak / mean_highest_peak) * 100
rsd_second_highest_peak = (std_second_highest_peak / mean_second_highest_peak) * 100
rsd_third_highest_peak = (std_third_highest_peak / mean_third_highest_peak) * 100

# Create labels for the mean and standard deviation
mean_label_highest = f"Mean: {mean_highest_peak:.2f}\n+/- {std_highest_peak:.2f}\nRSD: {rsd_highest_peak:.2f}%"
mean_label_second_highest = f"Mean: {mean_second_highest_peak:.2f}\n+/- {std_second_highest_peak:.2f}\nRSD: {rsd_second_highest_peak:.2f}%"
mean_label_third_highest = f"Mean: {mean_third_highest_peak:.2f}\n+/- {std_third_highest_peak:.2f}\nRSD: {rsd_third_highest_peak:.2f}%"

# Create a bar chart of the highest peak of each spectrum after baseline correction
fig, ax = plt.subplots(figsize=(10, 5))
x = np.arange(len(sample_names))

ax.bar(x, highest_peaks, width=0.8, label='Highest Peak', tick_label=sample_names)
#ax.errorbar(x, highest_peaks, yerr=std_highest_peak, fmt='none', ecolor='black', capsize=5)
ax.text(3,-5000, mean_label_highest, ha='right')

ax.bar(x + 20, second_highest_peaks, width=0.8, label='Second Highest Peak', tick_label=sample_names)
#ax.errorbar(x + 20, second_highest_peaks, yerr=std_second_highest_peak, fmt='none', ecolor='black', capsize=5)
ax.text(23,-5000, mean_label_second_highest, ha='right')

ax.bar(x + 10, third_highest_peaks, width=0.8, label='Third Highest Peak', tick_label=sample_names)
#ax.errorbar(x + 10, third_highest_peaks, yerr=std_third_highest_peak, fmt='none', ecolor='black', capsize=5)
ax.text(13,-5000, mean_label_third_highest, ha='right')

width = 10
xticks = np.tile(x, 3) + np.repeat([0, width, 2*width], len(sample_names))
ax.set_xticks(xticks)
ax.set_xticklabels(np.tile(sample_names, 3), rotation=90)
ax.tick_params(axis='x', pad=10, which='minor')
ax.set_ylabel('Raman Intensity', fontsize=12)
ax.set_xlabel('Samples', fontsize=12)

ax2 = ax.twiny()
ax2.set_xticks([4, 15, 26, 30])
ax2.set_xticklabels([round(highest_wavenumbers[0],0), round(second_highest_wavenumbers[0],0), round(third_highest_wavenumbers[0],0), ""])
ax2.set_xlabel('Wavenumber (cm$^{-1}$)', fontsize=12)

fig.tight_layout()
plt.show()

# ##########################################################
# # Inside these comments is the combining files code
# # If you just want the skip 17 lines and select the worksheet code, comment this out and uncomment all above

# # Function to handle different files
# def process_file(filename, selectSheet=None):
#     if filename.endswith('.xlsx'):
#         df = pd.read_excel(filename, header=None, skiprows = 17, sheet_name = selectSheet)
#     elif filename.endswith('.csv'):
#         df = pd.read_csv(filename, header=None, skiprows = 17)
#     else:
#         raise ValueError('Invalid file type. Please use .xlsx or .csv file')
#     return df

# # Function to combine files
# def combine_files(filenames):
#     combined_data = None
#     for filename in filenames:
#         filename = filename.strip()  # Remove any leading/trailing spaces
#         if filename.endswith('.xlsx'):
#             selectSheet = input(f"Enter which worksheet you would like to work with for file {filename}: ")
#             df = process_file(filename, selectSheet)
#         else:
#             df = process_file(filename)

#         if combined_data is None:
#             combined_data = df
#         else:
#             combined_data = pd.concat([combined_data, df], axis=1)

#     # Write the combined data to a new Excel file
#     combined_filename = "combined.xlsx"
#     write_to_excel(combined_data, combined_filename)
#     print(f"All files combined into {combined_filename}. Now proceeding with the data analysis.")

#     return combined_filename

# def write_to_excel(df, filename):
#     writer = ExcelWriter(filename)
#     df.to_excel(writer, 'Sheet1', index=False)
#     writer.save()

# choice = input("Do you want to (1) process a single file or (2) combine multiple files? ")

# if choice == '1':
#     filename = input("Please input the full file path and filename: ")
#     if filename.endswith('.xlsx'):
#         selectSheet = input("Enter which worksheet you would like to work with: ")
#         df = process_file(filename, selectSheet)
#     else:
#         df = process_file(filename)
# elif choice == '2':
#     filenames = input("Please input the full file paths and filenames, separated by commas: ").split(',')
#     filename = combine_files(filenames)
#     df = process_file(filename)
# else:
#     raise ValueError('Invalid choice. Please enter 1 or 2.')

# # Read the file (either the single file or the combined file)
# df = pd.read_excel(filename, header=None, skiprows=17)

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