#!/usr/bin/python
# -*- coding: utf-8 -*-
# vi: ts=4 sw=4

import pickle
from ..Protocols import *

from scipy.spatial.distance import cdist
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, MeanShift, estimate_bandwidth, AffinityPropagation, SpectralClustering # Clustering methods
from sklearn.decomposition import PCA

import skimage

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.patches as patches

class cluster(ProtocolMultiple):

    def __init__(self, name='cluster', **kwargs):

        self.name = self.__class__.__name__ if name is None else name

        self.default_ext = '.pkl'
        self.run_args = {
                        'file_extension' : '.pkl',
                        'force' : False,
                        'verbosity' : 3,
                        'num_jobs' : None,
                        'num_clusters' : 20,
                        'cluster_method' : 'kmeans', # 'affinity', 'kmeans', 'meanshift'
                        'features' : 'all', # 'shape', 'color', 'all'
                        'feature_normed_range' : [-2, +2], # range for plotting the normed features
                        'bbox_pad' : 0.5,
                        'image_contrast' : (0, 1),
                        'image_contrast_trim' : None,
                        'overlays' : 3,
                        }
        self.run_args.update(kwargs)

        # WARNING: This association of features names is hard-coded, and is thus contingent
        # on the current implementation of Protocols.py>flake_analysis
        self.feature_names_color = [
            'g contrast',
            'v contrast',
            'gray',
            'gray std',
            'H',
            'S',
            'V',
            'H std',
            'S std',
            'V std',
            'R',
            'G',
            'B',
            'R std',
            'G std',
            'B std',
            'entropy'
            ]
        self.feature_names_color = self.feature_names_color + ['{}_inner'.format(f) for f in self.feature_names_color]

        self.feature_names_shape = ['P/A'] + ['hist {}'.format(i) for i in range(15)] + ['fractal dimension']

    def load_flakes(self, datas, **run_args):
        flakes = []
        for data in datas:
            with open(data.infile, 'rb') as fin:
                saved = pickle.load(fin) # 'res_map', 'image_labelmap', 'flakes'
                if len(flakes)==0:
                    h, w = saved['res_map'].shape

                for flake in saved['flakes']:
                    flakes.append(flake)
                    #self.print_structure(flake)

                if run_args['verbosity']>=5:
                    print('      {} flakes added from image {}'.format(len(saved['flakes']), data.infile))

        return flakes

    def load_flakes_parallel(self, datas, **run_args):
        # Parallelize loading
        # Doesn't seem to actually run faster (likely I/O limited)
        from joblib import Parallel, delayed
        import itertools

        flakes = Parallel(n_jobs=run_args['num_jobs'])(delayed(self.load_flake_pkl)(data.infile) for data in datas)
        flakes = list(itertools.chain.from_iterable(flakes))

        return flakes

    def load_flake_pkl(self, infile):
        with open(infile, 'rb') as fin:
            flakes = pickle.load(fin)['flakes']
        return flakes

    def load_features(self, flakes, **run_args):

        if run_args['features']=='all':
            features = [ np.concatenate([flake['flake_color_fea'], flake['flake_shape_fea']]) for flake in flakes ]

            if 'flake_color_fea_names' in flakes[0]:
                self.feature_names_color = flakes[0]['flake_color_fea_names']
            if 'flake_shape_fea_names' in flakes[0]:
                self.feature_names_shape = flakes[0]['flake_shape_fea_names']

            self.feature_names = self.feature_names_color + self.feature_names_shape

        else:
            features = [ flake['flake_{}_fea'.format(run_args['features'])] for flake in flakes ]

            if run_args['features']=='color':
                if 'flake_color_fea_names' in flakes[0]:
                    self.feature_names = flakes[0]['flake_color_fea_names']
                else:
                    self.feature_names = self.feature_names_color

            elif run_args['features']=='shape':
                if 'flake_shape_fea_names' in flakes[0]:
                    self.feature_names = flakes[0]['flake_shape_fea_names']
                else:
                    self.feature_names = self.feature_names_shape

            else:
                if 'flake_{}_fea_names'.format(run_args['features']) in flakes[0]:
                    self.feature_names = flakes[0]['flake_{}_fea_names'.format(run_args['features'])]
                else:
                    self.feature_names = []

        return np.asarray(features)

    def load_clustering(self, basename, output_dir='./', features_rescaled=None, **run_args):
        # Load data aggregated from the "cluster" protocol into a cluster.pkl file
        savefile = self.get_outfile(basename, output_dir, ext=run_args['file_extension'])
        if os.path.exists(savefile):
            with open(savefile, 'rb') as fin:
                clustering = pickle.load(fin)
        else:
            savefile = self.get_outfile(basename, output_dir+'/../cluster/', ext=run_args['file_extension'])
            if os.path.exists(savefile):
                with open(savefile, 'rb') as fin:
                    clustering = pickle.load(fin)
            elif features_rescaled is not None:
                # Manually recompute some minimal aspects of clustering
                # Note: This mostly exists so that select_flakes.run has access to this information
                # even if cluster.run has never been run (and thus cluster.pkl doesn't exist).
                clustering = {}
                vmin, vmax = run_args['feature_normed_range']
                distributions, dist_bin_edges = np.apply_along_axis(lambda x: np.histogram(x, bins=50, range=[vmin,vmax], density=True), 0, features_rescaled)
                clustering['distributions'] = distributions
                clustering['dist_bin_edges'] = dist_bin_edges

            else:
                print("Error in cluster.load_clustering: we don't have access to clustering information.")

        return clustering

    @run_default
    def run(self, datas, output_dir, basename, **run_args):

        results = {}
        clustering = {} # Save results of clustering operation

        # Aggregate results
        ########################################
        flakes = self.load_flakes(datas, **run_args)
        if run_args['verbosity']>=4:
            print('  {:,d} flakes identified in {:d} images'.format(len(flakes), len(datas)))

        features_orig = self.load_features(flakes, **run_args)

        # Clustering
        ########################################
        rescale = StandardScaler()
        features = rescale.fit_transform(features_orig)

        if run_args['verbosity']>=4:
            print("  Clustering {:,d} flakes using '{}'".format(len(flakes), run_args['cluster_method']))

        start = time.time()

        n_jobs = run_args['num_jobs'] if 'num_jobs' in run_args else -1

        if run_args['cluster_method']=='kmeans':
            cluster_result = KMeans(n_clusters=run_args['num_clusters'], random_state=0, n_jobs=n_jobs).fit(features)

        elif run_args['cluster_method']=='meanshift':
            bandwidth = estimate_bandwidth(features, quantile=0.1)#, n_samples=int(features.shape[0]/10))
            cluster_result = MeanShift(bandwidth=bandwidth, bin_seeding=True, n_jobs=n_jobs).fit(features)

        elif run_args['cluster_method']=='affinity':
            cluster_result = AffinityPropagation().fit(features)

        elif run_args['cluster_method']=='spectral':
            cluster_result = SpectralClustering(n_clusters=run_args['num_clusters'], n_jobs=n_jobs).fit(features)

        else:
            print("ERROR: clustering method '{}' not recognized.".format(run_args['cluster_method']))
            raise NotImplementedError

        clustering['cluster_result'] = cluster_result
        results['cluster_runtime'] = time.time()-start
        results['cluster_method'] = run_args['cluster_method']

        # Assignments are unsorted by default
        assignment = cluster_result.labels_
        results['num_clusters'] = len(np.unique(assignment))
        clustering['assignment'] = assignment # Label ids for each flake, saying what cluster it belongs to [unsorted indexing]

        if run_args['verbosity']>=4:
            print("    clustering took {:.1f}s ({:d} clusters)".format(results['cluster_runtime'], results['num_clusters']))

        # Sort clusters into a sensible order
        consider_features = np.asarray([flake['flake_color_fea'][:2] for flake in flakes]) # Grayscale and V contrast

        # The average for each cluster gives the position for the center of that cluster (in the feature space)
        central_features = np.zeros([results['num_clusters'], consider_features.shape[1]])
        for i in range(results['num_clusters']):
             cluster_i = np.nonzero(assignment==i)[0]
             central_features[i,:] = np.mean(consider_features[cluster_i, :])
        clustering['sort_indices'] = np.argsort(np.abs(central_features).sum(1))
        clustering['unsort2sort'] = np.unique(clustering['sort_indices'], return_index=True)[1]

        clustering['cluster_centers'] = cluster_result.cluster_centers_[clustering['sort_indices']] # in (normed) feature space coordinates [sorted indexing]
        clustering['cluster_centers_orig'] = rescale.inverse_transform(clustering['cluster_centers']) # in (original) feature space coordinates [sorted indexing]
        clustering['cluster_center_distances'] = cdist(clustering['cluster_centers'], clustering['cluster_centers']) # in (normed) feature space coordinates [sorted indexing]

        # Compute additional things
        ########################################
        # The distribution (histogram) for each feature dimension
        # Since these are normed they should look somewhat Gaussian
        vmin, vmax = run_args['feature_normed_range']
        distributions, dist_bin_edges = np.apply_along_axis(lambda x: np.histogram(x, bins=50, range=[vmin,vmax], density=True), 0, features)
        clustering['distributions'] = distributions
        clustering['dist_bin_edges'] = dist_bin_edges

        # Output results
        ########################################

        # Save cluster results
        outfile = self.get_outfile(basename, output_dir, ext=run_args['file_extension'])
        results['files_saved'] = [
            { 'filename': '{}'.format(outfile) ,
             'description' : 'results of cluster analysis' ,
             'type' : 'data'
            } ,
            ]
        with open(outfile, 'wb') as fout:
            pickle.dump(clustering, fout)

        # Output images
        if run_args['verbosity']>=4:
            print('  Generating PCA 3D projection')

        # Pick a color for each cluster
        norm = mpl.colors.Normalize(vmin=0, vmax=results['num_clusters']-1)
        cmap = mpl.cm.jet
        #cmap = cmap_vge
        m = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
        cluster_colors = [m.to_rgba(index) for index in range(results['num_clusters'])]

        pca = PCA(n_components=3)
        coordinates = pca.fit_transform(features)
        outfile = self.get_outfile(basename, output_dir, ext='-{}.png'.format(run_args['cluster_method']))
        self.plot_pca(outfile, coordinates, assignment, cluster_colors, **run_args)

        if run_args['verbosity']>=4:
            print('  Generating map of distances')

        outfile = self.get_outfile('distances', output_dir, ext='-{}.png'.format(run_args['cluster_method']))
        self.plot_distances(outfile, clustering['cluster_center_distances'], cluster_colors, **run_args)

        if run_args['verbosity']>=4:
            print('  Generating cluster images')

        self.plot_clusters(output_dir, clustering['cluster_centers'], clustering['cluster_centers_orig'], clustering['sort_indices'], distributions, dist_bin_edges, flakes, features, assignment, rescale=rescale, **run_args)

        return results

    def plot_pca(self, outfile, coordinates, assignment, cluster_colors, **run_args):

        flake_colors = [cluster_colors[index] for index in assignment]

        # Centroid of each cluster (in PCA coordinates)
        num_clusters = np.max(assignment)+1
        cluster_coordinates = np.zeros([num_clusters, coordinates.shape[1]])
        for i in range(num_clusters):
             cluster_i = np.nonzero(assignment==i)[0]
             cluster_coordinates[i,:] = np.mean(coordinates[cluster_i,:], axis=0)
        cluster_index = range(cluster_coordinates.shape[0])

        plt.rcParams['xtick.labelsize'] = 8
        plt.rcParams['ytick.labelsize'] = 8
        plt.rcParams['axes.labelsize'] = 12
        plt.rcParams['lines.markersize'] = 5

        cmap = run_args['cmap'] if 'cmap' in run_args else 'jet'
        alpha = 0.12

        self.fig = plt.figure(figsize=(10,10))
        self.fig.subplots_adjust(left=0.08, right=0.95, bottom=0.08, top=0.95, hspace=0.15, wspace=0.15)

        self.ax = self.fig.add_subplot(2,2,2 , projection='3d')
        self.ax.scatter(coordinates[:,0], coordinates[:,1], coordinates[:,2], c=flake_colors, alpha=0.3)
        self.ax.set_xlabel('$\mathrm{PCA}_1$', labelpad=-4)
        self.ax.set_ylabel('$\mathrm{PCA}_2$', labelpad=-4)
        self.ax.set_zlabel('$\mathrm{PCA}_3$', labelpad=-2)
        self.ax.tick_params(axis='both', which='major', pad=-1)
        self.ax.view_init(elev=30, azim=45-90)

        self.ax = self.fig.add_subplot(2,2,1)
        self.ax.scatter(coordinates[:,0], coordinates[:,2], c=flake_colors, edgecolors=None, alpha=alpha)
        self.ax.set_xlabel('$\mathrm{PCA}_1$')
        self.ax.set_ylabel('$\mathrm{PCA}_3$')
        self.overlay_cluster_number(cluster_coordinates, cluster_index, 0, 2, cluster_colors)
        xi, xf, yi, yf = self.ax.axis()
        self.ax.text(xi,yf, '{:,d} flakes in {} clusters'.format(len(assignment), len(cluster_colors)), size=10, verticalalignment='top', horizontalalignment='left', alpha=0.5)

        self.ax = self.fig.add_subplot(2,2,3)
        self.ax.scatter(coordinates[:,0], coordinates[:,1], c=flake_colors, cmap=cmap, edgecolors=None, alpha=alpha)
        self.ax.set_xlabel('$\mathrm{PCA}_1$')
        self.ax.set_ylabel('$\mathrm{PCA}_2$')
        self.overlay_cluster_number(cluster_coordinates, cluster_index, 0, 1, cluster_colors)

        self.ax = self.fig.add_subplot(2,2,4)
        self.ax.scatter(coordinates[:,2], coordinates[:,1], c=flake_colors, cmap=cmap, edgecolors=None, alpha=alpha)
        self.ax.set_xlabel('$\mathrm{PCA}_3$')
        self.ax.set_ylabel('$\mathrm{PCA}_2$')
        self.overlay_cluster_number(cluster_coordinates, cluster_index, 2, 1, cluster_colors)

        plt.savefig(outfile, dpi=300)
        plt.close()

    def overlay_cluster_number(self, cluster_coordinates, cluster_index, coord1, coord2, cluster_colors):

        r = 0.3 # r=1 means no fade (strong color), r=0 means fully faded (appears white)
        cluster_colors_a = [ [ 1-(1-c[0])*r, 1-(1-c[1])*r, 1-(1-c[2])*r, c[3]] for c in cluster_colors]
        self.ax.scatter(cluster_coordinates[:,coord1], cluster_coordinates[:,coord2], s=25, c=cluster_colors_a, edgecolor=cluster_colors, alpha=1)

        for i in range(cluster_coordinates.shape[0]):
            self.ax.text(cluster_coordinates[i, coord1], cluster_coordinates[i, coord2], '{}'.format(i), size=3, horizontalalignment='center', verticalalignment='center')

    def plot_distances(self, outfile, cluster_center_distances, cluster_colors, plot_buffers=[0.15,0.05,0.15,0.05], **run_args):

        plt.rcParams['xtick.labelsize'] = 15
        plt.rcParams['ytick.labelsize'] = 15
        plt.rcParams['axes.labelsize'] = 20
        plt.rcParams['lines.markersize'] = 5

        self.fig = plt.figure( figsize=(8,8), facecolor='white' )
        left_buf, right_buf, bottom_buf, top_buf = plot_buffers
        fig_width = 1.0-right_buf-left_buf
        fig_height = 1.0-top_buf-bottom_buf
        self.ax = self.fig.add_axes( [left_buf, bottom_buf, fig_width, fig_height] )

        #plt.figtext(0,1, 'distances between clusters (in the feature space)', size=15, verticalalignment='top', horizontalalignment='left')
        self.ax.imshow(cluster_center_distances, cmap='viridis')

        self.ax.set_xlabel('cluster index')
        self.ax.set_ylabel('cluster index')

        xi, xf, yi, yf = self.ax.axis()

        s = 0.02
        n = len(cluster_colors)
        self.axt = self.fig.add_axes( [left_buf, bottom_buf+fig_height, fig_width, s] )
        self.axt.scatter(range(n), np.ones(n), c=cluster_colors)
        if n<160:
            for i in range(n):
                self.axt.text(i, 1, '{}'.format(i), size=4, horizontalalignment='center', verticalalignment='center')
        self.axt.axis([xi, xf, 0, 2])

        self.axt.axes.get_xaxis().set_visible(False)
        self.axt.axes.get_yaxis().set_visible(False)

        self.axr = self.fig.add_axes( [left_buf+fig_width, bottom_buf, s, fig_height] )
        self.axr.scatter(np.ones(n), range(n), c=cluster_colors)
        if n<80:
            for i in range(n):
                self.axr.text(1, i, '{}'.format(i), size=4, horizontalalignment='center', verticalalignment='center')
        self.axr.axis([0, 2, yi, yf])

        self.axr.axes.get_xaxis().set_visible(False)
        self.axr.axes.get_yaxis().set_visible(False)

        plt.savefig(outfile, dpi=300)

    def plot_clusters(self, output_dir, cluster_centers, cluster_centers_orig, sort_indices, distributions, dist_bin_edges, flakes, flake_features, assignment, rescale=None, plot_buffers=[0.01,0.0,0.0,0.045], **run_args):

        plt.rcParams['xtick.labelsize'] = 15
        plt.rcParams['ytick.labelsize'] = 15
        plt.rcParams['axes.labelsize'] = 20
        plt.rcParams['lines.markersize'] = 5

        #for i, feature_vector in enumerate(cluster_centers[:1]): # for testing
        for i, feature_vector in enumerate(cluster_centers):

            # i # [sorted indexing]
            # feature_vector # in (normed) feature space coordinates [sorted indexing]
            feature_vector_orig = cluster_centers_orig[i] # in (original) feature space coordinates [sorted indexing]

            i_before_sort = sort_indices[i] # [unsorted indexing]

            cluster_i = np.nonzero(assignment==i_before_sort)[0] # indices [in unsorted indexing] of all flakes matching this cluster
            flakes_cluster = np.asarray(flakes)[cluster_i] # flakes matching this cluster
            features_cluster = flake_features[cluster_i] # feature vectors matching this cluster

            self.plot_cluster(output_dir, '{:03d}'.format(i), feature_vector, feature_vector_orig, flakes_cluster, features_cluster, distributions, dist_bin_edges, rescale=rescale, plot_buffers=plot_buffers, **run_args)

    def plot_cluster(self, output_dir, cluster_name, feature_vector, feature_vector_orig, flakes_cluster, features_cluster, distributions, dist_bin_edges, rescale=None, plot_buffers=[0.01,0.0,0.0,0.045], **run_args):
        ''' Outputs an image showing representative flakes for this cluster.

        flakes_cluster, features_cluster : The subset of flakes (and their features) for this cluster.
        feature_vector, feature_vector_orig : The centroid of this cluster (average of features).
        distributions, dist_bin_edges : The feature distributions (for all flakes).
        '''

        num_flakes = len(flakes_cluster)

        # Sort flakes by their distance from the cluster centroid (which is located at position "feature_vector")
        distances = cdist(features_cluster, [feature_vector], metric='euclidean')[:,0]
        sort_indices = np.argsort(distances)
        flakes_cluster = flakes_cluster[sort_indices]
        features_cluster = features_cluster[sort_indices]
        distances = distances[sort_indices]

        if run_args['verbosity']>=5:
            print('    image for cluster {} ({:,d} flakes)'.format(cluster_name, num_flakes))

        # Output a summary (central, generic, peripheral)
        ########################################
        self.fig = plt.figure( figsize=(8,8), facecolor='white' )
        fea_w, fea_h = 0.04, 0.95 # Size of features graphs in sidebar

        plt.figtext(0,1, 'cluster {} ({:,d} flakes)'.format(cluster_name, num_flakes), size=20, verticalalignment='top', horizontalalignment='left')

        # Sidebar that shows the feature vector for the centroid of this cluster
        self._plot_cluster_sidebar(feature_vector, feature_vector_orig, features_cluster, distributions, dist_bin_edges, fea_w=fea_w, fea_h=fea_h, **run_args)

        # Images of example flakes for this cluster
        self._plot_cluster_main(flakes_cluster, distances, fea_w=fea_w, fea_h=fea_h, plot_buffers=plot_buffers, **run_args)

        outfile = os.path.join(output_dir, 'cluster-{}-{}.png'.format(run_args['cluster_method'], cluster_name))
        plt.savefig(outfile, dpi=300)
        plt.close(self.fig.number)

        if 'output_all' in run_args and run_args['output_all']:
            # Output a summary (central, generic, peripheral)
            ########################################
            nrows, ncols = 8, 7
            num_per_page = nrows*ncols
            num_pages = int(np.ceil(num_flakes/num_per_page))
            for page in range(num_pages):

                num_this_page = num_per_page
                if page==(num_pages-1): # Last page
                    num_this_page = num_flakes - (num_pages-1)*num_per_page

                idx_start = page*num_per_page
                idx_end = idx_start+num_this_page

                if run_args['verbosity']>=5:
                    print('    page {:d} for cluster {} ({:,d}/{:,d} flakes)'.format(page+1, cluster_name, num_this_page, num_flakes))

                self.fig = plt.figure( figsize=(8,8), facecolor='white' )
                plt.figtext(0,1, 'cluster {} ({:,d}/{:,d} flakes)'.format(cluster_name, num_this_page, num_flakes), size=20, verticalalignment='top', horizontalalignment='left')

                # Sidebar that shows the feature vector for the centroid of this cluster
                if rescale is not None:
                    # Since we have access to the scaling between original coordinates for feature vector
                    # and the rescale coordinates (avg=0, std=1), we can compute the sidebar for just the
                    # flakes being displayed.

                    # There are two equivalent ways to get the information for this subset of flakes (this page of results)

                    # Method 1: Load features_orig for these flakes, and transform them
                    #flakes_page = flakes_cluster[idx_start:idx_end]
                    #features_orig = self.load_features(flakes_page, **run_args)
                    #features_rescaled = rescale.transform(features_orig)

                    # Method 2: Select subset of rescaled features, and inverse_transform them
                    features_rescaled = features_cluster[idx_start:idx_end]
                    features_orig = rescale.inverse_transform(features_rescaled)

                    # Compute centroid for this subset of flakes (this page of results)
                    feature_vector_orig = np.average(features_orig, axis=0)
                    feature_vector = rescale.transform( [feature_vector_orig] )[0]

                    self._plot_cluster_sidebar(feature_vector, feature_vector_orig, features_rescaled, distributions, dist_bin_edges, fea_w=fea_w, fea_h=fea_h, **run_args)

                else:
                    self._plot_cluster_sidebar(feature_vector, feature_vector_orig, features_cluster, distributions, dist_bin_edges, fea_w=fea_w, fea_h=fea_h, **run_args)

                self._plot_cluster_page(idx_start, flakes_cluster, distances, fea_w, fea_h, plot_buffers, nrows, ncols, **run_args)

                outfile = os.path.join(output_dir, 'cluster-{}-page{:03d}.png'.format(run_args['cluster_method'], page+1))
                plt.savefig(outfile, dpi=300)
                plt.close(self.fig.number)

    def _plot_cluster_page(self, idx, flakes_cluster, distances, fea_w, fea_h, plot_buffers, nrows, ncols, **run_args):

        # The total area we have available for plotting flakes
        left_buf, right_buf, bottom_buf, top_buf = plot_buffers
        left_buf += fea_w*( 2.2 + 2.3 )
        fig_width = 1.0-right_buf-left_buf
        fig_height = 1.0-top_buf-bottom_buf

        w = fig_width/ncols
        ystart = bottom_buf+fig_height
        for irow in range(nrows):
            for icol in range(ncols):
                ax_pos = [left_buf+icol*w, ystart-(irow+1)*w, w, w]
                if idx<len(flakes_cluster):
                    self._plot_flake_image(ax_pos, flakes_cluster[idx], distances[idx], **run_args)
                idx += 1

    def _plot_cluster_main(self, flakes_cluster, distances, fea_w, fea_h, plot_buffers, **run_args):

        # The total area we have available for plotting flakes
        left_buf, right_buf, bottom_buf, top_buf = plot_buffers
        left_buf += fea_w*( 2.2 + 2.3 )
        fig_width = 1.0-right_buf-left_buf
        fig_height = 1.0-top_buf-bottom_buf
        #self.ax = self.fig.add_axes( [left_buf, bottom_buf, fig_width, fig_height] )

        # Central flakes
        nrows, ncols = 3, 7
        w = fig_width/ncols
        idx = 0
        ystart = bottom_buf+fig_height
        plt.figtext(left_buf, ystart, 'central', size=8, verticalalignment='bottom', horizontalalignment='left')
        for irow in range(nrows):
            for icol in range(ncols):
                ax_pos = [left_buf+icol*w, ystart-(irow+1)*w, w, w]
                if idx<len(flakes_cluster):
                    self._plot_flake_image(ax_pos, flakes_cluster[idx], distances[idx], **run_args)
                idx += 1

        # Generic flakes
        if idx<len(flakes_cluster):
            ystart = ystart-nrows*w - 0.015
            #nrows, ncols = 2, 6
            w = fig_width/ncols
            idx = max( int( np.clip( len(flakes_cluster)/2, idx, len(flakes_cluster)-nrows*ncols ) ), idx )
            plt.figtext(left_buf, ystart, 'generic', size=8, verticalalignment='bottom', horizontalalignment='left')
            for irow in range(nrows):
                for icol in range(ncols):
                    ax_pos = [left_buf+icol*w, ystart-(irow+1)*w, w, w]
                    if idx<len(flakes_cluster):
                        self._plot_flake_image(ax_pos, flakes_cluster[idx], distances[idx], **run_args)
                    idx += 1

        # Peripheral flakes
        if idx<len(flakes_cluster):
            ystart = ystart-nrows*w - 0.015
            nrows, ncols = 2, 7
            w = fig_width/ncols
            idx = max( len(flakes_cluster)-nrows*ncols, idx )
            plt.figtext(left_buf, ystart, 'peripheral', size=8, verticalalignment='bottom', horizontalalignment='left')
            for irow in range(nrows):
                for icol in range(ncols):
                    ax_pos = [left_buf+icol*w, ystart-(irow+1)*w, w, w]
                    if idx<len(flakes_cluster):
                        self._plot_flake_image(ax_pos, flakes_cluster[idx], distances[idx], **run_args)
                    idx += 1

    def _plot_cluster_sidebar(self, feature_vector, feature_vector_orig, features_cluster, distributions, dist_bin_edges, fea_w, fea_h, **run_args):
        # Sidebar that shows the feature vector for the centroid of this cluster

        vmin, vmax = run_args['feature_normed_range']

        self.ax = self.fig.add_axes( [0.0, 0, fea_w, fea_h] )

        vector = np.asarray([feature_vector]).transpose()
        self.ax.imshow(vector, cmap='inferno', aspect='auto', vmin=vmin, vmax=vmax)
        self.ax.set_xticklabels([])
        self.ax.set_yticklabels([])

        xi, xf, yi, yf = self.ax.axis()
        if len(feature_vector)<80:
            for ifea, fea in enumerate(feature_vector):
                if fea<0:
                    color = 'white'
                else:
                    color = 'k'
                self.ax.text((xi+xf)*0.5, ifea, '{:.2f}'.format(fea), color=color, size=8, verticalalignment='center', horizontalalignment='center')

                self.ax.text(xf, ifea, '{:.3g}'.format(feature_vector_orig[ifea]), size=6, verticalalignment='center', horizontalalignment='left')

                # Miniature histogram (of the entire distribution)
                axc = self.fig.add_axes( [fea_w*2.2, fea_h-(ifea+1)*fea_h/len(feature_vector), fea_w*2.3, fea_h/len(feature_vector)] )
                w = dist_bin_edges[ifea][1]-dist_bin_edges[ifea][0]
                axc.bar( dist_bin_edges[ifea][:-1]+0.5*w, distributions[ifea], width=w, color='b', alpha=0.3 )
                plt.xlim(vmin,vmax)

                # Overlay the histogram for this cluster
                distribution, dist_bin_edge = np.histogram(features_cluster[:,ifea], bins=50, range=[vmin,vmax], density=True)
                distribution *= np.max(distributions[ifea])/np.max(distribution)
                #axc.bar( dist_bin_edge[:-1]+0.5*w, distribution, width=w, color='purple', alpha=0.2 )
                axc.plot( dist_bin_edge[:-1]+0.5*w, distribution, '-', color='purple', linewidth=0.8, alpha=0.3 )

                axc.axvline(fea, color='purple', linewidth=1)
                if fea<vmin:
                    axc.axvline(vmin, color='purple', linewidth=4)
                elif fea>vmax:
                    axc.axvline(vmax, color='purple', linewidth=4)

                axc.axes.get_xaxis().set_visible(False)
                axc.axes.get_yaxis().set_visible(False)

                if len(self.feature_names)==len(feature_vector):
                    axc.text(vmin, np.max(distributions[ifea]), self.feature_names[ifea], size=4, verticalalignment='top', horizontalalignment='left', alpha=0.25)
                axc.text(vmax, np.max(distributions[ifea]), '{:d}'.format(ifea), size=4, verticalalignment='top', horizontalalignment='right', alpha=0.25)

    def _plot_flake_image(self, ax_pos, flake_i, distance, **run_args):

        # Load parent image
        filename = flake_i['infile'].replace('\\', '/') # String replace in case files were saved on another platform.
        img = plt.imread(filename)
        h, w, c = img.shape

        # Define image sub-region that has the flake in it
        y1, y2, x1, x2 = flake_i['bbox']
        # Make the crop border a bit bigger than the flake bounding box
        box_size = (1+run_args['bbox_pad'])*max( abs(x2-x1), abs(y2-y1) )
        x1p = int(np.clip((x1+x2)*0.5 - box_size/2, 0, w))
        x2p = int(np.clip((x1+x2)*0.5 + box_size/2, 0, w))
        y1p = int(np.clip((y1+y2)*0.5 - box_size/2, 0, h))
        y2p = int(np.clip((y1+y2)*0.5 + box_size/2, 0, h))
        box = y1p, y2p, x1p, x2p

        # Adjust image of flake
        flake = img[y1p:y2p , x1p:x2p, :]
        in_range = self.get_in_range(img, run_args['image_contrast'], **run_args)
        flake = skimage.exposure.rescale_intensity(flake, in_range=in_range, out_range='dtype')

        # Plot flake
        self.ax = self.fig.add_axes(ax_pos)
        self.ax.axes.get_xaxis().set_visible(False)
        self.ax.axes.get_yaxis().set_visible(False)

        self.ax.imshow(flake)

        xi, xf, yi, yf = self.ax.axis()
        yc, xc = flake_i['center_of_mass']
        s = '{}\nflake{:03d}\n({}, {})'.format(flake_i['infile'], flake_i['index'], int(xc), int(yc))
        self.ax.text(xi, yf, s, color='white', size=3, verticalalignment='top', horizontalalignment='left')

        self.ax.text(xi, yi, '${:.1f} \, \mathrm{{\mu m}}$'.format(flake_i['radius_um']), color='r', size=5, verticalalignment='bottom', horizontalalignment='left')

        self.ax.text((xi+xf)*0.5, yi, '{:.1f}'.format(distance), color='white', size=2, verticalalignment='bottom', horizontalalignment='center')

        self.ax.text(xf, yi, '{:.3f}'.format(flake_i['flake_contrast']), color='orange', size=3, verticalalignment='bottom', horizontalalignment='right')

        # Various overlays on the flake
        xc -= x1p
        yc -= y1p
        size = flake_i['radius_pixels']

        if run_args['overlays']>=1:
            c = flake_i['contour']
            xs = (c[:,0] - x1p)
            ys = (c[:,1] - y1p)
            self.ax.plot(xs, ys, '-', linewidth=0.6, color='r', dashes=[4,1], alpha=0.2)

        if run_args['overlays']>=7:
            c = flake_i['convex_hull']
            xs = (c[:,1] - x1p)
            ys = (c[:,0] - y1p)
            self.ax.plot(xs, ys, '-', linewidth=0.5, color='g', alpha=0.5)

        if run_args['overlays']>=5:
            rect = patches.Rectangle( ((x1-x1p), (y1-y1p)), (x2-x1), (y2-y1), linewidth=1.0, edgecolor='orange', facecolor='none', alpha=0.5)
            self.ax.add_patch(rect)

        if run_args['overlays']>=3:
            # Cross hair and circle
            rect = patches.Rectangle( (xc-size/2, yc), size, 0, linewidth=0.6, edgecolor='r', facecolor='none', alpha=0.3) # Horizontal bar
            self.ax.add_patch(rect)
            rect = patches.Rectangle( (xc, yc-size/2), 0, size, linewidth=0.6, edgecolor='r', facecolor='none', alpha=0.3) # Vertical bar
            self.ax.add_patch(rect)

        if run_args['overlays']>=5:
            # Circle overlay denoting size
            circ = patches.Circle(xy=(xc,yc), radius=size, linewidth=0.6, edgecolor='r', facecolor='none', alpha=0.3)
            self.ax.add_patch(circ)

    def get_in_range(self, data, im_contrast, image_contrast_trim=None, **run_args):

        if image_contrast_trim is not None:
            image_contrast_trim = np.clip(image_contrast_trim, 0, 0.95)

            avg = np.average(data)
            avg /= 255
            amt = image_contrast_trim
            im_contrast = ( avg*amt , 1.0-(1.0-avg)*amt )

        in_range = ( im_contrast[0]*255, im_contrast[1]*255 )

        return in_range

class select_flakes(cluster):

    def __init__(self, name='select_flakes', **kwargs):

        self.name = self.__class__.__name__ if name is None else name

        self.default_ext = '.pkl'
        self.run_args = {
                        'file_extension' : '.pkl',
                        'force' : False,
                        'verbosity' : 3,
                        'num_jobs' : None,
                        'num_clusters' : 20,
                        'cluster_method' : 'selection',
                        'features' : 'all', # 'shape', 'color', 'all'
                        'feature_normed_range' : [-2, +2], # range for plotting the normed features
                        'bbox_pad' : 0.5,
                        'image_contrast' : (0, 1),
                        'image_contrast_trim' : None,
                        'overlays' : 3,
                        }
        self.run_args.update(kwargs)

        # WARNING: This association of features names is hard-coded, and is thus contingent
        # on the current implementation of Protocols.py>flake_analysis
        self.feature_names_color = [
            'g contrast',
            'v contrast',
            'gray',
            'gray std',
            'H',
            'S',
            'V',
            'H std',
            'S std',
            'V std',
            'R',
            'G',
            'B',
            'R std',
            'G std',
            'B std',
            'entropy'
            ]
        self.feature_names_color = self.feature_names_color + ['{}_inner'.format(f) for f in self.feature_names_color]

        self.feature_names_shape = ['P/A'] + ['hist {}'.format(i) for i in range(15)] + ['fractal dimension']

    @run_default
    def run(self, datas, output_dir, basename, **run_args):

        results = {}

        # Load all flakes identified by "find_flakes" protocol
        flakes = self.load_flakes(datas, **run_args)
        if run_args['verbosity']>=4:
            print('  {:,d} flakes identified in {:d} images'.format(len(flakes), len(datas)))

        # Compute the rescaling of feature vectors
        features_all_orig = self.load_features(flakes, **run_args)
        rescale = StandardScaler()
        features_all_rescaled = rescale.fit_transform(features_all_orig)

        flakes_selected = self.select(flakes, features_all_orig, features_all_rescaled, **run_args)
        features_orig = self.load_features(flakes_selected, **run_args)

        feature_vector_orig = np.average(features_orig, axis=0)
        feature_vector = rescale.transform( [feature_vector_orig] )[0]
        features = rescale.transform(features_orig)

        if run_args['verbosity']>=4:
            print("  Selected {:,d} flakes using '{}'".format(len(flakes_selected), run_args['cluster_method']))

        clustering = self.load_clustering(basename=basename, output_dir=output_dir, features_rescaled=features, **run_args)

        self.plot_cluster(output_dir, cluster_name='selection', feature_vector=feature_vector, feature_vector_orig=feature_vector_orig, flakes_cluster=flakes_selected, features_cluster=features, distributions=clustering['distributions'], dist_bin_edges=clustering['dist_bin_edges'], rescale=rescale, **run_args)

        return results

    def extract_features(self, feature_name, flakes, flake_features, **run_args):
        # Extract the specified feature, returning a list of that feature
        # for the entire list of flakes

        # Handle special case of relative standard deviation
        if feature_name.endswith(' std_inner __relative'):
            if run_args['verbosity']>=5:
                print("    Computing {}".format(feature_name))

            name = feature_name[:-len(' std_inner __relative')]
            features = self.extract_features(name, flakes, flake_features, **run_args)
            features_std = self.extract_features('{} std_inner'.format(name), flakes, flake_features, **run_args)

            return features_std/features

        elif feature_name.endswith(' std __relative'):
            if run_args['verbosity']>=5:
                print("    Computing {}".format(feature_name))

            features = self.extract_features(feature_name[:-len(' std __relative')], flakes, flake_features, **run_args)
            features_std = self.extract_features(feature_name[:-len(' __relative')], flakes, flake_features, **run_args)

            return features_std/features

        # Check if it appears as value associated with each flake object
        if feature_name in flakes[0]:
            if run_args['verbosity']>=5:
                print("      Extracting {} from flakes".format(feature_name))
            return np.asarray( [ f[feature_name] for f in flakes ] )

        # Default: lookup in self.feature_names
        i = self.feature_names.index(feature_name)
        if run_args['verbosity']>=5:
            print("      Extracting {} from flake_features, index {}".format(feature_name, i))

        return flake_features[:,i]

    def select(self, flakes, flake_features_orig, flake_features_rescaled, **run_args):

        # Generate a list of boolean arrays, which are selecting flakes with
        # features within the specified range
        conditions = []
        for key, value in run_args['selection'].items():
            if run_args['verbosity']>=5:
                print("    Adding condition: {} between {} and {}".format(key, value[0], value[1]))

            if key.endswith(' __rescaled'):
                features = self.extract_features(key[:-len(' __rescaled')], flakes, flake_features_rescaled, **run_args)
            else:
                features = self.extract_features(key, flakes, flake_features_orig, **run_args)
            conditions.append( (features>=value[0]) )
            conditions.append( (features<=value[1]) )

        idx = np.where(np.all(conditions, axis=0))[0]

        flakes = np.asarray(flakes)[idx]

        if run_args['verbosity']>=3 and len(flakes)<1:
            print("WARNING: Selection criteria too restrictive. (No flakes meet criteria.)")

        return flakes


import bge, json
from bge.logic import globalDict
from random import choice, random
from pprint import pprint
from ast import literal_eval as litev

if not 'player_active' in globalDict.keys():
	globalDict['player_active'] = False

def init(cont):
	""" Initializes the character. """

	own = cont.owner
	scene = own.scene

	# Sensors
	autostart = cont.sensors['autostart'].positive

	# Actuators
	track_to = cont.actuators['track_to']

	# Objects
	track_direction = own.childrenRecursive['track_direction']

	# Properties

	#### INITIALIZE ####
	if autostart:

		if track_to.object == None:
			track_to.object = track_direction

		cont.activate(track_to)

		own.childrenRecursive['char_mesh'].color[0] = random()

		own.childrenRecursive['camera_smooth'].timeOffset = 5

		if not globalDict['player_active']:
			scene.active_camera = own.childrenRecursive['camera_char']
			own['is_player'] = True
			globalDict['player_active'] = True

	pass

def set_direction(cont):
	""" Sets the direction of track object and properties of character. """

	own = cont.owner
	scene = own.scene

	# Sensors
	always = cont.sensors['always'].positive
	up = cont.sensors['up'].positive
	down = cont.sensors['down'].positive
	left = cont.sensors['left'].positive
	right = cont.sensors['right'].positive
	run = cont.sensors['run'].positive

	# Actuators
	track_to = cont.actuators['track_to']

	# Objects
	track_direction = own.childrenRecursive['track_direction']
	directions = [obj for obj in own.childrenRecursive if obj.name.startswith('dir_')]

	if len(directions) > 0:
		new_dic = {}
		for obj in directions:
			new_dic[obj.name] = obj
		directions = new_dic

	# Properties

	#### INITIALIZE ####

	if not run:
		own['run'] = False

	if not up and not down and not left and not right or up and down or left and right:
		own['walk'] = False
		own['run'] = False

	if up and not down or not up and down or left and not right or not left and right:
		own['walk'] = True

		if run:
			own['run'] = True

	if up and not down:

		if not left and not right:
			track_direction.worldPosition = directions['dir_U'].worldPosition

		elif left and not right:
			track_direction.worldPosition = directions['dir_UL'].worldPosition

		elif not left and right:
			track_direction.worldPosition = directions['dir_UR'].worldPosition

	if not up and down:

		if not left and not right:
			track_direction.worldPosition = directions['dir_D'].worldPosition

		elif left and not right:
			track_direction.worldPosition = directions['dir_DL'].worldPosition

		elif not left and right:
			track_direction.worldPosition = directions['dir_DR'].worldPosition

	if not up and not down:

		if left and not right:
			track_direction.worldPosition = directions['dir_L'].worldPosition

		elif not left and right:
			track_direction.worldPosition = directions['dir_R'].worldPosition

	pass

def mov_anim(cont):
	""" Moves the character and animates its armature. """

	own = cont.owner
	scene = own.scene

	# Sensors
	autostart = cont.sensors['autostart'].positive
	is_walk = cont.sensors['is_walk'].positive
	is_run = cont.sensors['is_run'].positive

	# Actuators
	motion = cont.actuators[0]

	# Objects
	char_armature = own.childrenRecursive['char_armature']

	# Properties
	LOOP = bge.logic.KX_ACTION_MODE_LOOP
	blend_in = 5
	motion_vec = [0, 0, 0]
	motion_spd = -0.07

	#### INITIALIZE ####
	if autostart:

		if not is_walk:
			char_armature.playAction('character', 0, 120, blendin=blend_in, play_mode=LOOP)
			motion_vec[1] = 0

		elif is_walk and not is_run:
			char_armature.playAction('character', 130, 145, blendin=blend_in, play_mode=LOOP)
			motion_vec[1] = motion_spd

		elif is_walk and is_run:
			char_armature.playAction('character', 150, 165, blendin=blend_in, play_mode=LOOP)
			motion_vec[1] = motion_spd * 2

		motion.dLoc = motion_vec
		cont.activate(motion)

	pass

def camera_collision(cont):
	""" Avoids the camera to pass through objects. """

	own = cont.owner
	scene = own.scene

	# Sensors
	always = cont.sensors['always'].positive

	# Objects
	camera = own.childrenRecursive['camera_char']
	axis = own.childrenRecursive['camera_axis']
	origin = own.childrenRecursive['camera_origin']

	# Properties
	dist = axis.getDistanceTo(origin)
	ray = own.rayCast(origin, axis, dist)

	#### INITIALIZE ####
	if always:

		if ray[0] != None:
			camera.worldPosition = ray[1]

		elif ray[0] == None:
			camera.worldPosition = origin.worldPosition


import identity_server.logic.session.login_session.logged_in_state as lst
import identity_server.logic.session.login_session.waiting_for_permission as wfp

from mongodb.Application import Application
from mongodb.ApplicationAccount import ApplicationAccount
from django.http.response import HttpResponse
import identity_server.logic.session.login_session.login_session_context as ctx
from typing import Type, Union
from django.http.request import HttpRequest
import identity_server.logic.session.session as ssn

class InitialLoginState(ssn.SessionState):
    """
    Session was not started.
    Checks is request is valid, returns login page in case if yes and bad request otherwise.
    """

    def required_request_params(self):
        return [
            'callback_url',
            'client_id'
        ]

    def route(self, request: HttpRequest) -> Union[Type[ssn.SessionState], None]:
        assert isinstance(self.session_context, ctx.LoginSessionContext)
        data = self._get_request_data(request)
        client_id = data['client_id']
        app = self.get_authorized_app(client_id)
        if app:
            self.session_context.authorized_clients[client_id] = app[0].permissions
            return lst.LoggedIn
        return super().route(request)

    def process_request(self, request: HttpRequest, **kwargs) -> HttpResponse:
        assert isinstance(self.session_context, ctx.LoginSessionContext)
        data = self._get_request_data(request)
        client_id = data['client_id']
        scope, app_name = self._get_app_info(client_id)
        is_logged_in = self.is_user_logged_in()
        self.session_context.assign(
            {'scope': scope, 'callback_url': data['callback_url'], 'client_id': client_id, 'app': app_name})

        self.set_session_state(wfp.WaitingForPermissions)
        return self.render_html(request, 'login_page.html', context={'scope': scope, 'app': app_name, 'clientId': client_id,  'is_logged_in': is_logged_in})

    def is_user_logged_in(self):
        assert isinstance(self.session_context, ctx.LoginSessionContext)
        return self.session_context.user_id != ''

    def get_authorized_app(self, client_id):
        assert isinstance(self.session_context, ctx.LoginSessionContext)
        user_id = self.session_context.user_id
        if user_id:
            authorized_app = ApplicationAccount.objects.filter(
                worker_id=user_id, client_id=client_id)
            return authorized_app

    def _get_app_info(self, client_id) -> Application:
        """
        Makes request to the database to get actual application name associated with given client id
        """
        app = Application.objects.filter(client_id=client_id).first()
        return app.permissions, app.name


""" Image editing class for head to bot, time-trail, obstacle where
there is only single agent
"""
import datetime
import logging
import rospy
import cv2

from markov.log_handler.logger import Logger
from markov.utils import get_racecar_idx
from mp4_saving import utils
from mp4_saving.constants import (RaceCarColorToRGB,
                                  IconographicImageSize,
                                  TrackAssetsIconographicPngs, RACE_COMPLETE_Y_OFFSET,
                                  RACE_TYPE_TO_VIDEO_TEXT_MAPPING, XYPixelLoc, AWS_DEEPRACER_WATER_MARK,
                                  SCALE_RATIO, FrameQueueData)
from mp4_saving.image_editing_interface import ImageEditingInterface
from mp4_saving.top_view_graphics import TopViewGraphics

LOG = Logger(__name__, logging.INFO).get_logger()

class SingleAgentImageEditing(ImageEditingInterface):
    """ Image editing class for head to bot, time-trail, obstacle where
    there is only single agent
    """
    def __init__(self, racecar_name, racecar_info, race_type):
        """ Initializing the required data for the head to bot, time-trail. This is used for single agent
        Arguments:
            racecars_info (list): list of dict having information of the agent
            race_type (str): Since this class is reused for all the different race_type
        """
        self.racecar_info = racecar_info
        self.race_type = race_type
        racecar_index = get_racecar_idx(racecar_name)
        self.racecar_index = racecar_index if racecar_index else 0
        # Store the font which we will use to write the phase with
        self.amazon_ember_regular_20px = utils.get_font('AmazonEmber-Regular', 20)
        self.amazon_ember_regular_16px = utils.get_font('AmazonEmber-Regular', 16)
        self.amazon_ember_heavy_30px = utils.get_font('AmazonEmber-Heavy', 30)
        self.amazon_ember_light_18px = utils.get_font('AmazonEmber-Light', 18)
        self.amazon_ember_light_20px = utils.get_font('AmazonEmber-Light', 20)
        self.amazon_ember_light_italic_20px = utils.get_font('AmazonEmber-LightItalic', 20)

        self.is_racing = rospy.get_param("VIDEO_JOB_TYPE", "") == "RACING"
        self.is_league_leaderboard = rospy.get_param("LEADERBOARD_TYPE", "") == "LEAGUE"
        self.leaderboard_name = rospy.get_param("LEADERBOARD_NAME", "")
        self._total_laps = int(rospy.get_param("NUMBER_OF_TRIALS", 0))

        # The track image as iconography
        self.track_icongraphy_img = utils.get_track_iconography_image()

        # Track image offset
        self.track_loc_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value if self.is_league_leaderboard \
            else XYPixelLoc.TRACK_IMG_WITHOUT_OFFSET_LOC.value

        # Gradient overlay image
        gradient_img_path = TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG_LEAGUE_LEADERBOARD.value \
            if self.is_league_leaderboard else TrackAssetsIconographicPngs.OBSTACLE_OVERLAY_PNG.value
        self.gradient_img = self._plot_track_on_gradient(gradient_img_path)
        self.gradient_alpha_rgb_mul, self.one_minus_gradient_alpha = utils.get_gradient_values(self.gradient_img)

        # Top camera information
        top_camera_info = utils.get_top_camera_info()
        self.top_view_graphics = TopViewGraphics(top_camera_info.horizontal_fov, top_camera_info.padding_pct,
                                                 top_camera_info.image_width, top_camera_info.image_height,
                                                 racecar_info)

    def _edit_major_cv_image(self, major_cv_image, mp4_video_metrics_info):
        """ Apply all the editing for the Major 45degree camera image
        Args:
            major_cv_image (Image): Image straight from the camera
        Returns:
            Image: Edited main camera image
        """
        # Applying gradient to whole major image and then writing text
        major_cv_image = utils.apply_gradient(major_cv_image, self.gradient_alpha_rgb_mul,
                                              self.one_minus_gradient_alpha)

        # Top left location of the picture
        loc_x, loc_y = XYPixelLoc.SINGLE_AGENT_DISPLAY_NAME_LOC.value

        # Display name (Racer name/Model name)
        display_name = self.racecar_info[self.racecar_index]['display_name']
        display_name_txt = display_name if len(display_name) < 15 else "{}...".format(display_name[:15])
        major_cv_image = utils.write_text_on_image(image=major_cv_image, text=display_name_txt,
                                                   loc=(loc_x, loc_y), font=self.amazon_ember_regular_20px,
                                                   font_color=RaceCarColorToRGB.White.value,
                                                   font_shadow_color=RaceCarColorToRGB.Black.value)
        # Lap Counter
        loc_y += 30
        current_lap = min(int(mp4_video_metrics_info[self.racecar_index].lap_counter) + 1, self._total_laps)
        lap_counter_text = "{}/{}".format(current_lap, self._total_laps)
        major_cv_image = utils.write_text_on_image(image=major_cv_image, text=lap_counter_text,
                                                   loc=(loc_x, loc_y), font=self.amazon_ember_heavy_30px,
                                                   font_color=RaceCarColorToRGB.White.value,
                                                   font_shadow_color=RaceCarColorToRGB.Black.value)
        # total_evaluation_time (Race time)
        loc_y += 45
        total_eval_milli_seconds = mp4_video_metrics_info[self.racecar_index].total_evaluation_time
        time_delta = datetime.timedelta(milliseconds=total_eval_milli_seconds)
        total_eval_time_text = "Race | {}".format(utils.milliseconds_to_timeformat(time_delta))
        major_cv_image = utils.write_text_on_image(image=major_cv_image, text=total_eval_time_text,
                                                   loc=(loc_x, loc_y), font=self.amazon_ember_light_18px,
                                                   font_color=RaceCarColorToRGB.White.value,
                                                   font_shadow_color=RaceCarColorToRGB.Black.value)
        # Reset counter
        loc_y += 25
        reset_counter_text = "Reset | {}".format(mp4_video_metrics_info[self.racecar_index].reset_counter)
        major_cv_image = utils.write_text_on_image(image=major_cv_image, text=reset_counter_text,
                                                   loc=(loc_x, loc_y), font=self.amazon_ember_light_18px,
                                                   font_color=RaceCarColorToRGB.White.value,
                                                   font_shadow_color=RaceCarColorToRGB.Black.value)
        # Speed
        loc_x, loc_y = XYPixelLoc.SPEED_EVAL_LOC.value
        if self.is_league_leaderboard:
            loc_x, loc_y = XYPixelLoc.SPEED_LEADERBOARD_LOC.value
        speed_text = "{} m/s".format(utils.get_speed_formatted_str(mp4_video_metrics_info[self.racecar_index].throttle))
        major_cv_image = utils.write_text_on_image(image=major_cv_image, text=speed_text,
                                                   loc=(loc_x, loc_y), font=self.amazon_ember_light_20px,
                                                   font_color=RaceCarColorToRGB.White.value,
                                                   font_shadow_color=RaceCarColorToRGB.Black.value)
        # Leaderboard name
        if self.is_league_leaderboard:
            loc_x, loc_y = XYPixelLoc.LEADERBOARD_NAME_LOC.value
            major_cv_image = utils.write_text_on_image(image=major_cv_image, text=self.leaderboard_name,
                                                       loc=(loc_x, loc_y), font=self.amazon_ember_regular_16px,
                                                       font_color=RaceCarColorToRGB.White.value,
                                                       font_shadow_color=RaceCarColorToRGB.Black.value)
        # Evaluation type
        loc_x, loc_y = XYPixelLoc.RACE_TYPE_EVAL_LOC.value
        if self.is_league_leaderboard:
            loc_x, loc_y = XYPixelLoc.RACE_TYPE_RACE_LOC.value
        race_text = "race" if self.is_racing else "evaluation"
        evaluation_type_txt = "{} {}".format(RACE_TYPE_TO_VIDEO_TEXT_MAPPING[self.race_type], race_text)
        major_cv_image = utils.write_text_on_image(image=major_cv_image, text=evaluation_type_txt,
                                                   loc=(loc_x, loc_y), font=self.amazon_ember_light_italic_20px,
                                                   font_color=RaceCarColorToRGB.White.value,
                                                   font_shadow_color=RaceCarColorToRGB.Black.value)

        # AWS Deepracer logo at the bottom for the community leaderboard
        if self.is_league_leaderboard:
            major_cv_image = utils.write_text_on_image(image=major_cv_image, text=AWS_DEEPRACER_WATER_MARK,
                                                       loc=XYPixelLoc.AWS_DEEPRACER_WATER_MARK_LOC.value,
                                                       font=self.amazon_ember_regular_16px,
                                                       font_color=RaceCarColorToRGB.White.value,
                                                       font_shadow_color=RaceCarColorToRGB.Black.value)

        # Check if the done flag is set and set the banner appropriately
        if mp4_video_metrics_info[self.racecar_index].done and (int(self._total_laps) >= current_lap):
            # When the cv2 text is written, it automatically drops the alpha value of the image
            rel_y_offset = XYPixelLoc.TRACK_IMG_WITH_OFFSET_LOC.value[1] if self.is_league_leaderboard else 0
            racecomplete_image = utils.get_image(TrackAssetsIconographicPngs.RACE_COMPLETE_OVERLAY_PNG.value,
                                                 IconographicImageSize.RACE_COMPLETE_IMAGE_SIZE.value)
            x_offset = major_cv_image.shape[1] - racecomplete_image.shape[1]//2
            y_offset = major_cv_image.shape[0] - RACE_COMPLETE_Y_OFFSET - rel_y_offset - racecomplete_image.shape[0]//2
            major_cv_image = utils.plot_rectangular_image_on_main_image(
                major_cv_image, racecomplete_image, (x_offset, y_offset))
        major_cv_image = cv2.cvtColor(major_cv_image, cv2.COLOR_RGB2BGRA)
        return major_cv_image

    def _plot_track_on_gradient(self, gradient_img_path):
        """ For the given gradient apply the track iconographic image and use this to apply gradient
        on each camera frame. Previously this was done on the top camera which changed every frame. But
        with the track iconographic image set static, adding the track on gradient is more optimized.

        Arguments:
            gradient_img_path (str): Gradient image path

        Returns:
            (Image): Edited gradient image with track image
        """
        gradient_img = utils.get_image(gradient_img_path, IconographicImageSize.FULL_IMAGE_SIZE.value)
        gradient_img = cv2.cvtColor(gradient_img, cv2.COLOR_RGBA2BGRA)

        track_icongraphy_scaled = utils.resize_image(self.track_icongraphy_img, SCALE_RATIO)
        track_icongraphy_alpha = track_icongraphy_scaled[:, :, 3]/255.0

        # Track image is placed at the bottom right with some offset (only in leaderboard tracks)
        x_min = -(self.track_loc_offset[1] + track_icongraphy_scaled.shape[0])
        x_max = gradient_img.shape[0] - self.track_loc_offset[1]
        y_min = -(self.track_loc_offset[0] + track_icongraphy_scaled.shape[1])
        y_max = gradient_img.shape[1] - self.track_loc_offset[0]

        # This is used as the offset for plotting the agent dots
        self.track_start_loc = (gradient_img.shape[1] + y_min, gradient_img.shape[0] + x_min)

        for channel in range(0, 4):
            gradient_img[x_min:x_max, y_min:y_max, channel] =\
                (track_icongraphy_alpha * track_icongraphy_scaled[:, :, channel]) + \
                (1 - track_icongraphy_alpha) * (gradient_img[x_min:x_max, y_min:y_max, channel])
        return gradient_img

    def _plot_agents_on_major_cv_image(self, major_cv_image, mp4_video_metrics_info):
        """ Add the agents, obstacles on the track.

        Arguments:
            major_cv_image (Image): Edited image having gradient, text, track
            mp4_video_metrics_info (List): List of ROS metric values of each agent

        Returns:
            Image: Edited image with gradient, text, track and agents with dots
        """
        agents_loc = [(metric.x, metric.y) for metric in mp4_video_metrics_info]
        objects_loc = []
        if mp4_video_metrics_info[0].object_locations:
            objects_loc = [(object_loc.x, object_loc.y) for object_loc in mp4_video_metrics_info[0].object_locations]
        return self.top_view_graphics.plot_agents_as_circles(
            major_cv_image, agents_loc, objects_loc, self.track_start_loc)

    def edit_image(self, major_cv_image, metric_info):
        mp4_video_metrics_info = metric_info[FrameQueueData.AGENT_METRIC_INFO.value]
        major_cv_image = self._edit_major_cv_image(major_cv_image, mp4_video_metrics_info)
        major_cv_image = self._plot_agents_on_major_cv_image(major_cv_image, mp4_video_metrics_info)
        return cv2.cvtColor(major_cv_image, cv2.COLOR_BGRA2RGB)


#!/usr/bin/env python

import numpy
import scipy.integrate
from pylab import *

datafile="../../../Mathematica/calculated_vals.tsv"

tag,x,e,f = numpy.loadtxt("data.txt",unpack=True)
tags=numpy.unique(tag)
flimit = numpy.zeros(len(tags))

for i in range(0,len(tags)):
    itag=tags[i]
    inds = numpy.where(tag == itag)
    xplot=x[inds]
    yplot=-f[inds]*31e-15
    isort=numpy.argsort(xplot)
    xplot = xplot[isort]
    yplot = yplot[isort]
    plot(xplot,yplot)
    flimit[i] = scipy.integrate.trapz(xplot,-yplot)

yscale('log')
xscale('log')
savefig('integrands.png')

clf()

dist,fpfa,fnaive,fright,ftemp=numpy.loadtxt(datafile,unpack=True)
dist=dist*1e6

plot(tags,flimit)
plot(dist,fpfa)
plot(dist,fright)
plot(dist,ftemp)
xscale('log')
yscale('log')
show()


from manimlib.imports import *

class Limite4_1 (ThreeDScene):
    def construct (self):
        titulo=TextMobject('''Existencia del Límite en Infinito\n
                            de Funciones de $\\mathbb{R}^n$ $\\rightarrow$ $\\mathbb{R}$''').scale(1.5)
        text=TextMobject("Sea $f:\\mathbb{R}^{n}\\rightarrow\\mathbb{R}$").move_to(2.2*UP)
        text1=TexMobject(r"\lim_{\vec{x}\rightarrow\infty}f(\vec{x})=L\leftrightarrow\forall\epsilon>0").move_to(1*UP)
        text2=TexMobject(r"\exists\delta>0 \ tq \ \forall \vec{x}\in B^{c}_{\delta}(\vec{0})").move_to(-0.2*UP)
        text3=TexMobject(r"\implies d(f(\vec{x}),L)<\epsilon").move_to(1.4*DOWN)
        G1=VGroup(text,text1,text2,text3)
        text4=TextMobject('''Veamos el siguiente ejemplo para aterrizar ideas:''')
        text5=TexMobject(r"f:\mathbb{R}^{2}\rightarrow\mathbb{R}")
        text6=TexMobject(r"f(x,y)=1+\frac{1}{x^{2}+y^{2}}").move_to(1.5*DOWN)
        G2=VGroup(text4,text5,text6)

        self.play(Write(titulo))
        self.wait(5.25)
        self.play(FadeOut(titulo))
        self.play(Write(text))
        self.play(Write(text1))
        self.play(Write(text2))
        self.play(Write(text3))
        self.wait(6)
        self.play(FadeOut(G1))
        self.play(Write(text4))
        self.wait(4.6)
        self.play(text4.shift,2*UP,runtime=1.5)
        self.play(Write(text5))
        self.play(Write(text6))
        self.wait(3)
        self.play(FadeOut(G2))
        self.wait()
        self.custom_method()

    def custom_method(self):
        axes=ThreeDAxes()
        superficie=superficie4()
        text1=TexMobject(r'''f(x,y)=1+\frac{1}{x^{2}+y^{2}}''')
        text1.to_corner(UL)
        text2=TextMobject("Tomemos", " $\epsilon$=0.5")
        text2.to_corner(UL)
        text2[1].set_color(RED)
        text3=TextMobject('''Y notemos que \n
                            podemos escoger''').to_corner(UL)
        text3_1=TextMobject("una"," $\\delta>0$").move_to(text3.get_center()+1*DOWN)
        text3_1[1].set_color(YELLOW_C)
        text4=TextMobject('''Tal que la imagen de los\n
                             puntos que no \n
                            pertenecen a $ B_{\\delta}(\\vec{0})$,''').to_corner(UL)
        text5=TextMobject('''están a una distancia $\\epsilon$\n
                                de 1.''').to_corner(UL)
        text5_1=TextMobject('''Es posible hacer lo mismo\n
                            con toda $\\epsilon>0$.''').to_corner(UL)
        text6=TextMobject('''Por lo cual:''').to_corner(UL)
        text7=TexMobject(r"\lim_{\vec{x}\rightarrow\infty}f(\vec{x})=1").move_to(text5.get_center()+1*DOWN)

        M=TextMobject("1").move_to(1*UP+0.2*LEFT)

        #epsilons se pueden modificar
        r=0.5
        r1=1
        linea=Line((0,0,1),(0,0,1+r),stroke_width=6,color=RED)
        linea_1=Line((0,0,1),(0,0,1+r1),stroke_width=6,color=RED)
        R=1.7
        R1=R-0.5
        linea1=Line((0,0,0),(R,0,0),stroke_width=6,color=YELLOW_C)

        circulo=Circle(radius=R,color=YELLOW_C)
        circulo1=Circle(radius=R1,color=YELLOW_C)
        #cilindro = ParametricSurface(
        #    lambda u, v: np.array([
        #        R*np.cos(TAU * v),
        #        R*np.sin(TAU * v),
        #        4*u
        #    ]),
        #    resolution=(6, 32)).fade(0.1).set_opacity(0.2)
        #cilindro.set_color(YELLOW_C)
        #cilindro1 = ParametricSurface(
        #    lambda u, v: np.array([
        #        R1*np.cos(TAU * v),
        #        R1*np.sin(TAU * v),
        #        4*u
        #    ]),
        #    resolution=(6, 32)).fade(0.1).set_opacity(0.2)
        #cilindro1.set_color(YELLOW_C)

        def puntosEnSuperficie(rad,lim,num):
            puntosDom = []
            puntosSur = []
            for i in range(num):
                azar = lim*np.random.rand(1,2)[0] + 0.1
                if (rad < np.sqrt(azar[0]**2 + azar[1]**2) < lim):
                    puntosDom.append(Dot(np.array([azar[0], azar[1],0]), color = BLUE))
                    puntosSur.append(Dot(superficie.func(azar[0], azar[1]), color = RED))
            return puntosDom, puntosSur

        puntosD1, puntosS1 = puntosEnSuperficie(R, 5, 6000)
        puntosD2, puntosS2 = puntosEnSuperficie(R1, R, 3000)

        GPuntosD1 = VGroup(*puntosD1)
        GPuntosS1 = VGroup(*puntosS1)
        GPuntosD2 = VGroup(*puntosD2)
        GPuntosS2 = VGroup(*puntosS2)

    ###Animacion
        self.set_camera_orientation(0.8*np.pi/2, -0.25*np.pi,distance=12)
        self.begin_ambient_camera_rotation(rate=0.001)
        self.play(ShowCreation(axes))
        self.add_fixed_in_frame_mobjects(text1)
        self.add_fixed_in_frame_mobjects(M)
        self.play(Write(text1))
        self.play(ShowCreation(superficie))
        self.wait()
        self.play(FadeOut(text1))
        self.add_fixed_in_frame_mobjects(text2)
        self.play(Write(text2))
        self.play(ShowCreation(linea))
        self.play(FadeOut(text2))
        self.add_fixed_in_frame_mobjects(text3)
        self.play(Write(text3))
        self.add_fixed_in_frame_mobjects(text3_1)
        self.play(Write(text3_1))
        self.play(ShowCreation(linea1))
        self.play(ShowCreation(circulo))
        self.play(FadeOut(text3),FadeOut(text3_1))
        self.add_fixed_in_frame_mobjects(text4)
        self.play(Write(text4))
        #self.play(ShowCreation(cilindro))
        self.wait()
        self.play(FadeOut(text4))
        self.play(FadeIn(GPuntosD1))
        self.add_fixed_in_frame_mobjects(text5)
        self.play(Write(text5),FadeOut(linea1))
        self.play(FadeIn(GPuntosS1))
        self.play(linea.shift,(R+0.1)*RIGHT,runtime=10)
        self.wait(6.5)
        self.play(FadeOut(text5))
        self.add_fixed_in_frame_mobjects(text5_1)
        self.play(Write(text5_1))
        self.play(ReplacementTransform(linea,linea_1))
        self.play(ReplacementTransform(circulo,circulo1))
        #self.play(ReplacementTransform(cilindro,cilindro1))
        self.play(FadeIn(GPuntosD2))
        self.play(FadeIn(GPuntosS2))
        self.play(linea_1.shift,(R1+0.1)*RIGHT,runtime=10)
        self.wait(3)
        self.play(FadeOut(text5_1))
        self.add_fixed_in_frame_mobjects(text6)
        self.play(Write(text6))
        self.add_fixed_in_frame_mobjects(text7)
        self.play(Write(text7))
        self.wait(2)
        self.play(FadeOut(text7),FadeOut(text6),FadeOut(axes),FadeOut(M),
                    FadeOut(superficie),FadeOut(linea_1),FadeOut(circulo1),FadeOut(GPuntosD1),
                    FadeOut(GPuntosS1),FadeOut(GPuntosD2),FadeOut(GPuntosS2))

Límite y continuidad en funciones multivariable/limite_infinito_Rn-R.py


from scipy.misc import comb

def exp(p, n):
    total = 0.0
    for k in range(n+1):
        total += comb(n, k, exact=False) * p**k * (1-p) ** (n-k)

    return total

def main():
    for p in [0.3, 0.75, 0.8, 1.0, 0.0, 0.5]:
        for n in range(1, 20):
            print('Checking n=%d, p=%f' % (n, p))
            print('Result: %f' % (exp(p, n)))

if __name__ == '__main__':
    main()


import pickle
import os
import pprint

def save_dict_to_file(output):
    global system_text
    global list_of_files
    file_ = f'{your_target_folder}/{list_of_files[i_file]}'
    filename, file_extension = os.path.splitext(file_)
    dict_data = {}
    for line_ in output.splitlines():
        key, value = line_.split(': ')
        dict_data[key] = value
    file_date_ = os.stat(file_).st_birthtime
    file_date = datetime.datetime.fromtimestamp(file_date_).strftime('%y%m%d_%H%M')
    new_filename = f"{my_reverse_date(dict_data['kuupaev'])}_{dict_data['firma']}_{dict_data['arve nr']}_{file_date}"

def load_dict_from_file(file_name):
    with open(file_name, 'rb') as file_dict:
        output_dict = pickle.load(file_dict)
        return output_dict

your_target_folder = '/Volumes/[C] Windows 10 (1)/Users/docha/OneDrive/Leka/Tsekkid for test/Tsekkid aprill'
list_of_files = [fn for fn in os.listdir(your_target_folder)
                  if any(fn.endswith(ext) for ext in ['.pkl',])]
#pprint.pprint(list_of_files)

for file_ in list_of_files:
    dict_ = load_dict_from_file(f'{your_target_folder}/{file_}')
    for k, v in dict_.items():
        if k == 'kuupaev' or k == 'summa kokku':
            if ',' in v:
                print(file_, k, v)


# one can of paint covers 5m^2 of wall, given a random height and width of wall
# calculate the minimum cans of paint to buy to fully cover the wall fully.
# define a function to take in inputs for width, height
# calculate the area of wall w*h, then calculate cans to buy rounded up to whole number
# output the number of cans

from math import ceil

def getPaintCans(w,h):
    print(f"You need at least {ceil((float(w)*float(h))/5)} cans of paint to fully cover the {w} by {h} wall.")

[width,height]=str.split(input("Input width and height of wall in metres, separated by a comma: \n"),",")
getPaintCans(width,height)


from turtle import *

def kwadrat(s,col):
    pd()
    fillcolor(col)
    begin_fill()
    for _ in range(4):
        fd(s)
        lt(90)
    end_fill()
    pu()

def trojkat(s,n,col):
    x=position()
    fd(s)
    for i in range(n,0,-2):
        for j in range(i):
            kwadrat(s,col)
            fd(s)
        bk((j+1)*s)
        lt(90)
        fd(s)
        rt(90)
        fd(s)
    setpos(x)

def filling(s,n):
    for _ in range(2):
        trojkat(s,n,"red")
        rt(90)
        bk((n+2)*s)
        trojkat(s,n,"green")
        rt(90)
        bk((n+2)*s)

def kwadraty(n):
    pu()
    k = 2*n+7
    s=480/k
    bk(240)
    lt(90)
    bk(240)
    rt(90)
    color("yellow")
    x=position()
    for i in range(k):
        for j in range(k):
            kwadrat(s,"black")
            fd(s)
        bk(s*k)
        lt(90)
        fd(s)
        rt(90)
    setpos(x)
    fd(s)
    lt(90)
    fd(s)
    rt(90)
    for _ in range(4):
        filling(s,n)
        rt(90)
        bk(2*s*n+5*s)

tracer(30,0)
kwadraty(12)
update()
done()


# https://school.programmers.co.kr/learn/courses/30/lessons/12981

def solution(n, words):
    check = set()
    check.add(words[0])
    cnt = 2
    for i in range(1, len(words)):
        st, ed = words[i - 1], words[i]
        if st[-1] != ed[0] or ed in check:
            return [cnt % n if cnt % n else n, cnt // n + 1 if cnt % n else 0]
        check.add(ed)
        cnt += 1
    return [0, 0]


from numpy import arange,log,exp,r_
from matplotlib import pyplot as plt
from scipy.special import gamma
import Cua2008
from numpy import fft,sin,pi
from numpy.random import normal

duration=60
hf_dt=0.01
mean=0.0
std=1.0
num_samples = int(duration/hf_dt)
t=arange(0,duration,hf_dt)
noise = normal(mean, std, size=num_samples)
freq=0.1
freq2=20.0
noise=sin(2*pi*t*freq+pi/4)+sin(2*pi*t*freq2+pi/6)
ft=fft.rfft(noise)
f=fft.rfftfreq(len(noise),hf_dt)

# GP window
Tw=duration
epsilon=0.2
eta=0.05

b=-epsilon*log(eta)/(1+eta*(log(epsilon)-1))
c=b/(epsilon*Tw)
#a=(exp(1)/(epsilon*Tw))**b
a=(((2*c)**(2*b+1))/gamma(2*b+1))**0.5
w=a*t**b*exp(-c*t)
plt.figure()
plt.plot(r_[0,t+10],r_[0,w])

#fft

#Cua window
i = 0 # Horizontal P-wave acceleration - rock:
i = 6 # Vertical P-wave acceleration - rock:
i = 12 # Horizontal S-wave acceleration - rock: **BAD #<-- S-wave alpha_t_rise for i=12 should be 0.064 instead of 0.64?
i = 19 # Vertical S-wave acceleration - soil:

M=5
R=10
TT=10
env=Cua2008.envelope(M,R,t,TT,Pcoeff=0,Scoeff=12)
plt.figure()
plt.plot(t,env)

plt.show()


from pydantic import BaseModel

class SourceURL(BaseModel):
    '''Source URL schema'''
    source_url: str

    class Config:
        orm_mode = True

class URLInfo(SourceURL):
    '''URL Information schema'''
    short_url_key: str
    short_url: str


import sys

from osgeo import gdal, osr

class GDALUtilities:
    """
    This class has the following capabilities

    1. Get raster info
    2. Read image band as an array
    3. Reproject a raster
    """

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

    def get_raster_info(self):
        self.dataset = gdal.Open(self.path, gdal.GA_ReadOnly)
        print(
            "Driver: {}/{}\n".format(
                self.dataset.GetDriver().ShortName, self.dataset.GetDriver().LongName
            )
        )
        print(
            "Size is {} x {} x {}\n".format(
                self.dataset.RasterXSize, self.dataset.RasterYSize, self.dataset.RasterCount
            )
        )
        print("Projection is {}\n".format(self.dataset.GetProjection()))
        geotransform = self.dataset.GetGeoTransform()
        if geotransform:
            print("Origin = ({}, {})\n".format(geotransform[0], geotransform[3]))
            print("Pixel Size = ({}, {})\n".format(geotransform[1], geotransform[5]))
        band_count = self.dataset.RasterCount
        for i in range(1, band_count + 1):
            band = self.dataset.GetRasterBand(i)
            band_name = band.GetDescription()
            if band_name:
                print(f"Band {i} Name: {band_name}")
            else:
                print(f"Band {i} has no name.")
        dataset = None

    def read_image(self, band: int = None):
        self.dataset = gdal.Open(self.path, gdal.GA_ReadOnly)
        band = self.dataset.GetRasterBand(band)
        data = band.ReadAsArray()
        self.dataset = None
        return data

    def reproject(
    self, output_path: str = None, target_crs: str = None  # EPSG:4326
    ):
        self.dataset = gdal.Open(self.path, gdal.GA_ReadOnly)
        input_dataset = self.dataset
        input_srs = input_dataset.GetProjectionRef()
        target_srs = osr.SpatialReference()
        target_srs.SetFromUserInput(target_crs)
        output_dataset = gdal.Warp(
            output_path, input_dataset, dstSRS=target_srs.ExportToWkt()
        )
        input_dataset = None
        output_dataset = None


import unittest

from onnx import defs, helper
from onnx.onnx_pb2 import NodeProto

class TestRelu(unittest.TestCase):

    def test_relu(self):
        self.assertTrue(defs.has('Relu'))
        node_def = helper.make_node(
            'Relu', ['X'], ['Y'])

if __name__ == '__main__':
    unittest.main()


#Annalisa Dattilio
#This program will allow the user to enter their size/measurements and be able to find their perfect size across online clothing stores domestically and internationally
mylist = list(("Nike", "Cotton On"))
for x in range(len(mylist)):
    print(mylist[x])

#how to show only the sizes for store user selects
user_store_selection = str(input("Please select a store from the list above: "))
#if user_store_selection == "nike":
    # print what
#elif user_store_selection == "cotton on":
    #print what
#else:
    #print("Please only select stores from the list.")

#NIKE(Women's Bottoms)
#https://www.nike.com/size-fit/womens-bottoms-alpha - Nike women's size chart

#Waist size in inches

Nike_waist_size_XXS_low = 21.25
Nike_waist_size_XXS_high = 23.5
Nike_waist_size_XS_low = 23.5
Nike_waist_size_XS_high = 26
Nike_waist_size_S_low = 26
Nike_waist_size_S_high = 29
Nike_waist_size_M_low = 29
Nike_waist_size_M_high = 31.5
Nike_waist_size_L_low = 31.5
Nike_waist_size_L_high = 34.5
Nike_waist_size_XL_low = 34.5
Nike_waist_size_XL_high = 38.5
Nike_waist_size__XXL_low = 38.5
Nike_waist_size__XXL_high = 42.5

#Hip size in inches

Nike_hip_size_XXS_low = 30.5
Nike_hip_size_XXS_high = 33
Nike_hip__size_XS_low = 33
Nike_hip__size_XS_high = 35.5
Nike_hip_size_S_low = 35.5
Nike_hip_size_S_high = 38.5
Nike_hip_size_M_low = 38.5
Nike_hip_size_M_high = 41
Nike_hip_size_L_low = 41
Nike_hip_size_L_high = 44
Nike_hip_size_XL_low = 44
Nike_hip_size_XL_high = 47
Nike_hip_size_XXL_low = 47
Nike_hip_size_XXL_high = 50

#Height in inches

Nike_height_XXS_low = 64
Nike_height_XXS_high = 68
Nike_height_XS_low = 64
Nike_height_XS_high = 68
Nike_height_S_low = 64
Nike_height_S_high = 68
Nike_height_M_low = 64
Nike_height_M_high = 68
Nike_height_L_low = 64
Nike_height_L_high = 68
Nike_height_XL_low = 64
Nike_height_XL_high = 68
Nike_height_XXL_low = 64
Nike_height_XXL_high = 68

user_waist_size = int(input("Enter waist size in centimeters: "))
print(user_waist_size / 2.54, end= " in. ") # the / operator is used to convert centimeters to inches through division
user_waist_size_in = user_waist_size / 2.54

user_hip_size = int(input("\nEnter hip size in centimeters: "))
print(user_hip_size * 0.393701, end= " in. ") # the * operator is used to convert centimeters to inches through multiplication
user_hip_size_in = user_hip_size * 0.393701

user_height = int(input("\nEnter height in centimeters: "))
print(user_height / 2.54, end= " in.")
user_height_in = user_height / 2.54

#waist size

if user_waist_size_in > 21.25 and user_waist_size_in < 23:
    print("\nYour waist fits size", "XXS", sep=": ")
if user_waist_size_in > 23.5 and user_waist_size_in < 26:
    print("\nYour waist fits size", "XS", sep=": ")
if user_waist_size_in > 26 and user_waist_size_in < 29:
    print("\nYour waist fits size", "S", sep=": ")
if user_waist_size_in > 29 and user_waist_size_in < 31.5:
    print("\nYour waist fits size", "M", sep=": ")
if user_waist_size_in > 31.5 and user_waist_size_in < 34.5:
    print("\nYour waist fits size", "L", sep=": ")
if user_waist_size_in > 34.5 and user_waist_size_in < 38.5:
    print("\nYour waist fits size", "XL", sep=": ")
if user_waist_size_in > 38.5 and user_waist_size_in < 42.5:
    print("\nYour waist fits size", "XXL", sep=": ")

#hip size

if user_hip_size_in > 30.5 and user_hip_size_in < 33:
    print("Your hips fit size", "XXS", sep=": ")
if user_hip_size_in > 33 and user_hip_size_in < 35.5:
    print("Your hips fit size", "XS", sep=": ")
if user_hip_size_in > 35.5 and user_hip_size_in < 38.5:
    print("Your hips fit size", "S", sep=": ")
if user_hip_size_in > 38.5 and user_hip_size_in < 41:
    print("Your hips fit size", "M", sep=": ")
if user_hip_size_in > 41 and user_hip_size_in < 44:
    print("Your hips fit size", "L", sep=":")
if user_hip_size_in > 44 and user_hip_size_in < 47:
    print("Your hips fit size", "XL", sep=": ")
if user_hip_size_in > 47 and user_hip_size_in < 50:
    print("Your hips fit size", "XXL", sep=": ")

#height
if user_height < 64 or user_height > 68:
    print("-If your height is below 5ft. 4in. know the pant legs will be slightly longer and may be baggy.\n-If your height is above 5ft. 8 in. know the pants will fit above the ankle")

#SPRINT 2

#COTTON ON(Women's Bottoms)
#https://cottonon.com/us/size-guide.html?gclid=Cj0KCQiA7bucBhCeARIsAIOwr--1ciZ5xW1MV06odv9bOfZkwd0VsflsiPgMhUOBfi0B5G0FwPpOWpcaAjuzEALw_wcB

#waist size
Cottonon_waist_size_xxxs_low = 21.6
Cottonon_waist_size_xxxs_high = 23.6
Cottonon_waist_size_xxs_low = 23.6
Cottonon_waist_size_xxs_high = 25.6
Cottonon_waist_size_xs_low = 25.6
Cottonon_waist_size_xs_high = 27.6
Cottonon_waist_size_s_low = 27.6
Cottonon_waist_size_s_high = 29.6
Cottonon_waist_size_m_low = 29.6
Cottonon_waist_size_m_high = 31.6
Cottonon_waist_size_l_low = 31.6
Cottonon_waist_size_l_high = 33.6
Cottonon_waist_size_xl_low = 33.6

#hip size
Cottonon_hip_size_xxxs_low = 31.6
Cottonon_hip_size_xxxs_high = 33.6
Cottonon_hip_size_xxs_low = 33.6
Cottonon_hip_size_xxs_high = 35.6
Cottonon_hip_size_xs_low = 35.6
Cotton_hip_size_xs_high = 37.6
Cottonon_hip_size_s_low = 37.6
Cottonon_hip_size_s_high = 39.6
Cottonon_hip_size_m_low = 39.6
Cottonon_hip_size_m_high = 41.6
Cottonon_hip_size_l_low = 41.6
Cottonon_hip_size_l_high = 43.6
Cottonon_hip_size_xl_low = 43.6

#WAIST SIZE
if user_waist_size_in > 21.6 and user_waist_size_in < 23.6:
    print("\nYour waist fits size", "0", sep=": ")
if user_waist_size_in > 23.6 and user_waist_size_in < 25.6:
    print("\nYour waist fits size", "2", sep=": ")
if user_waist_size_in > 25.6 and user_waist_size_in < 27.6:
    print("\nYour waist fits size", "4", sep=": ")
if user_waist_size_in > 27.6 and user_waist_size_in < 29.6:
    print("\nYour waist fits size", "6", sep=": ")
if user_waist_size_in > 29.6 and user_waist_size_in < 31.6:
    print("\nYour waist fits size", "8", sep=": ")
if user_waist_size_in > 31.6 and user_waist_size_in < 33.6:
    print("\nYour waist fits size", "10", sep=": ")
if user_waist_size_in > 33.6 and user_waist_size_in < 33.7:
    print("\nYour waist fits size", "12", sep=": ")

#HIP SIZE
if user_hip_size_in > 31.6 and user_hip_size_in < 33.6:
    print("\nYour hips fit size", "0", sep=": ")
if user_hip_size_in > 33.6 and user_hip_size_in < 35.6:
    print("\nYour hips fit size", "2", sep=": ")
if user_hip_size_in > 35.6 and user_hip_size_in < 37.6:
    print("\nYour hips fit size", "4", sep=": ")
if user_hip_size_in > 37.6 and user_hip_size_in < 39.6:
    print("\nYour hips fit size", "6", sep=": ")
if user_hip_size_in > 39.6 and user_hip_size_in < 41.6:
    print("\nYour hips fit size", "8", sep=": ")
if user_hip_size_in > 41.6 and user_hip_size_in < 43.6:
    print("\nYour hips fit size", "10", sep=": ")
if user_hip_size_in > 43.6 and user_hip_size_in < 43.7:
    print("\nYour hips fit size", "12", sep=": ")

user_waist_size != user_hip_size

#store does not carry your size. would you like to try a dufferent store? select y to continue or n to quit
answer = input("Would you like to continue? Enter yes or no: ")
#if answer == "yes":
    #what here (i want to prompt to run the program again)
#elif answer == "no":
    #what here (if no i want the program to stop and end there)
#else:
    #print("Please enter yes or no.")

#if(Nike_waist_size > 23.5 or Nike < 30
#if out of range, print store does not carry size

#additional websites used for assistance:
#https://www.w3schools.com/python/default.asp
#https://www.folkstalk.com/tech/how-to-ask-a-yes-or-no-question-on-python-with-code-examples/


import logging
import sys

import click
import requests
from bs4 import BeautifulSoup
from telegram.ext import CommandHandler, Filters, MessageHandler, Updater
from tinydb import Query, TinyDB

db = TinyDB("db.json")
Job = Query()

TELEGRAM_BOT_TOKEN = None

class JobExistsException(Exception):
    pass

def parse_result_item(item):
    """
    Takes a li item containing one search result and parses id, url and price from it.
    Returns a dict containing the results.
    """
    main = item.find_all("div", {"aditem-main"})
    price = item.find_all("p", {"aditem-main--middle--price"})
    article = item.find_all("article")

    if len(main) != 1 or len(article) != 1 or len(price) != 1:
        return
    main = main[0]
    article = article[0]
    price = price[0]

    result = {
        "ad_id": article["data-adid"],
        "price": price.text.strip(),
    }

    a = main.find_all("a")[0]
    result["url"] = "https://www.ebay-kleinanzeigen.de" + a["href"]

    return result

def execute_search(search_term):
    """
    Runs the search for one search term.
    Returns a list containing all parsed search results.
    """
    headers = {
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36"
    }
    url = f"https://www.ebay-kleinanzeigen.de/s-79249/{search_term}/k0l9364r20"

    response = requests.get(url, headers=headers)

    soup = BeautifulSoup(response.content, features="html.parser")

    ul = soup.find_all("ul", {"id": "srchrslt-adtable"})
    assert len(ul) == 1

    ul = ul[0]

    items = ul.find_all("li")

    results = []
    for i in items:
        data = parse_result_item(i)
        if data is not None:
            results.append(data)
    if len(results) == 0:
        logging.warning(
            f"No results found for search term '{search_term}'. Check if parser works correctly."
        )
    return results

def init_search(search_term, chat_id):
    """
    Initialize a new search term.
    Executes one search and marks all current results as known.
    """
    result = db.search(Job.search_term == search_term)

    if result:
        raise JobExistsException

    initial_results = execute_search(search_term)
    ids = [_["ad_id"] for _ in initial_results]
    db.insert({"search_term": search_term, "chat_id": chat_id, "known_ads": ids})

def echo(update, context):
    context.bot.send_message(chat_id=update.effective_chat.id, text=update.message.text)

def start_watching(update, context):
    """
    Command handler for starting to watch a new search term.
    """
    search_target = "".join(context.args)
    try:
        init_search(search_target, update.effective_chat.id)
    except JobExistsException:
        reply = "Hm, looks like I'm watching that already."
    else:
        reply = f"Ok, I'll start watching '{search_target}'"
    context.bot.send_message(chat_id=update.effective_chat.id, text=reply)

def stop_watching(update, context):
    """
    Command handler for stopping to watch a search term
    """
    search_term = "".join(context.args)
    result = db.search(Job.search_term == search_term)

    if not result:
        reply = "I don't think I am watching that."
    else:
        db.remove(Job.search_term == search_term)
        reply = "Ok. I'll no longer watch " + search_term
    context.bot.send_message(chat_id=update.effective_chat.id, text=reply)

def look_for_stuff(context):
    """
    Command handler to peridically check all active search jobs.
    """
    for job in db.all():
        known_ads = set(job["known_ads"])
        results = execute_search(job["search_term"])
        something_new = False
        for r in results:
            if r["ad_id"] not in known_ads:
                message = (
                    f"New item for {job['search_term']} ({r['price']}): {r['url']}"
                )
                context.bot.send_message(chat_id=job["chat_id"], text=message)
                known_ads.add(r["ad_id"])
                something_new = True

        if something_new:
            db.update(
                {"known_ads": list(known_ads)}, Job.search_term == job["search_term"]
            )
        else:
            # context.bot.send_message(chat_id=job["chat_id"], text=f"Nothing new for {job['search_term']}")
            pass

def status(update, context):
    message = "I'm currently watching: \n"
    for job in db.all():
        message += "- " + job["search_term"] + "\n"
    context.bot.send_message(chat_id=update.effective_chat.id, text=message)

@click.group()
def cli():
    pass

@cli.command()
@click.option("--token", prompt=True, help="The telegram bot api token")
def run(token):
    TELEGRAM_BOT_TOKEN = token

    updater = Updater(token=TELEGRAM_BOT_TOKEN, use_context=True)
    dispatcher = updater.dispatcher
    job_queue = updater.job_queue

    job_minute = job_queue.run_repeating(look_for_stuff, interval=5 * 60, first=0)

    echo_handler = MessageHandler(Filters.text & (~Filters.command), echo)
    dispatcher.add_handler(echo_handler)

    start_watching_handler = CommandHandler("start", start_watching)
    dispatcher.add_handler(start_watching_handler)

    stop_handler = CommandHandler("stop", stop_watching)
    dispatcher.add_handler(stop_handler)

    status_handler = CommandHandler("status", status)
    dispatcher.add_handler(status_handler)

    updater.start_polling()

@cli.command()
@click.argument("searchterm")
def search(searchterm):
    data = execute_search(searchterm)
    click.echo(data)

if __name__ == "__main__":
    cli()


#!/usr/bin/env python3
import os
import sys
import math
import struct

from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer

from litex.build.generic_platform import *
from litex.build.xilinx import XilinxPlatform

from litex.soc.cores.clock import *
from litex.soc.integration.soc_core import *

from litex.soc.integration.builder import *
from litex.soc.interconnect import csr_bus
from litex.soc.cores.uart import *

sys.path.append('../')

from periphs.misc import *
from periphs.accel import *

def get_common_ios():
    return [
        # clk / rst
        ("clk", 0, Pins(1)),
        ("rst", 0, Pins(1)),

        # serial
        ("serial", 0,
            Subsignal("tx", Pins(1)),
            Subsignal("rx", Pins(1))
        ),

        ("gpio_irq", 0, Pins("1")),
        ("user_led", 15, Pins("1")),

        # SPI master
        ("spi", 0,
            Subsignal("sclk", Pins(1)),
            Subsignal("miso", Pins(1)),
            Subsignal("mosi", Pins(1)),
            Subsignal("csn",  Pins(1)),
            Subsignal("irq",  Pins(1)),
        ),

        # SPI slave, accel simulator
        ("spi_slave", 0,
            # SPI slave part
            Subsignal("sck",   Pins(1)),
            Subsignal("miso",  Pins(1)),
            Subsignal("mosi",  Pins(1)),
            Subsignal("csn",   Pins(1)),
            Subsignal("int1",  Pins(1)),
            Subsignal("int2",  Pins(1)),
            #Subsignal("irq",  Pins(1)),
            Subsignal("led0",  Pins(1)),
            Subsignal("led1",  Pins(1)),
            Subsignal("led2",  Pins(1)),
            Subsignal("led3",  Pins(1)),
            Subsignal("led4",  Pins(1)),
            Subsignal("led5",  Pins(1)),
            Subsignal("led6",  Pins(1)),
            Subsignal("led15", Pins(1)),
            # UART part
            Subsignal("tx",    Pins(1)),
            Subsignal("rx",    Pins(1)),
        ),

        # MailBox sender interface
        ("mbx_snd", 0,
            Subsignal("dout_r", Pins("0 1 2 3 4 5 6 7")),
            Subsignal("dout_re", Pins(1)),
            Subsignal("int_r", Pins(1)),
            Subsignal("int_re",  Pins(1)),
        ),

        # MailBox sender interface
        ("mbx_rcv", 0,
            Subsignal("din_status", Pins("0 1 2 3 4 5 6 7")),
            Subsignal("len_status", Pins("0 1 2 3 4 5 6 7")),
            Subsignal("rd_r", Pins(1)),
            Subsignal("rd_re", Pins(1)),
            Subsignal("int",  Pins(1)),
        ),
    ]

class Platform(XilinxPlatform):
    def __init__(self):
        XilinxPlatform.__init__(self, "xc7a35tcpg236-1", io=[], toolchain="vivado")

class CRG(Module):
    def __init__(self, platform, soc_config):
        clk = platform.request("clk")
        rst = platform.request("rst")

        self.clock_domains.cd_sys = ClockDomain()
        self.cd_sys.clk.attr.add("keep")
        self.cd_sys.rst.attr.add("keep")

        self.comb += [
            self.cd_sys.clk.eq(clk),
        ]

        self.sync += [
            self.cd_sys.rst.eq(rst),
        ]

class BaseSoC(SoCCore):
    csr_map = {
        "ctrl":   0,
        "uart":   2,
        "timer0": 3,
    }
    interrupt_map = {
        "uart":   3,
        "timer0": 4,
    }
    mem_map = {
        "rom":    0x00000000,
        "sram":   0x10000000,
        "csr":    0xf0000000,
    }
    csr_map.update(SoCCore.csr_map)
    interrupt_map.update(SoCCore.interrupt_map)

    def __init__(self, platform, soc_config, **kwargs):
        platform.add_extension(get_common_ios())
        sys_clk_freq = soc_config["sys_clk_freq"]
        SoCCore.__init__(self, platform, sys_clk_freq,
                         with_uart=True,
                         integrated_main_ram_size=0,
                         **kwargs)
        # crg
        self.submodules.crg = CRG(platform, soc_config)

        if soc_config["platform_name"] in ["accel_sim_release"]:
            # Integrate SPI master
            self.submodules.spi_master = spi_master = SpiMaster(self.platform.request("spi", 0))
            self.add_csr("spi_master", 10, allow_user_defined=True)
            self.add_interrupt("spi_master", 6, allow_user_defined=True)
            self.register_mem("spi_master", 0x30000000, spi_master.bus, 32)
            spi_master.add_source(self.platform)

            # Custom accel simulator IP core
            self.submodules.accel = accel = AccelCore(freq=sys_clk_freq, baud=115200, pads=self.platform.request("spi_slave", 0))
            self.add_csr("accel", 11, allow_user_defined=True)
            self.add_interrupt("accel", 7, allow_user_defined=True)

            if soc_config["mbx_sender"] in ["yes"]:
                # Integrate mailbox sender
                self.submodules.mbx_snd = mbx_snd = MailBoxSenderInf(self.platform.request("mbx_snd", 0))
                self.add_csr("mbx_snd", 12, allow_user_defined=True)

            if soc_config["mbx_receiver"] in ["yes"]:
                # Integrate mailbox receiver
                self.submodules.mbx_rcv = mbx_rcv = MailBoxReceiverInf(self.platform.request("mbx_rcv", 0))
                self.add_csr("mbx_rcv", 13, allow_user_defined=True)
                self.add_interrupt("mbx_rcv", 8, allow_user_defined=True)

            # Integrate GPIO LED
            self.submodules.gpio_led = gpio_led = GpioLED(self.platform.request("user_led", 15))
            self.add_csr("gpio_led", 14, allow_user_defined=True)

        if soc_config["platform_name"] in ["accel_sim"]:
            # Integrate SPI master
            self.submodules.spi_master = spi_master = SpiMaster(self.platform.request("spi", 0))
            self.add_csr("spi_master", 10, allow_user_defined=True)
            self.add_interrupt("spi_master", 6, allow_user_defined=True)
            self.register_mem("spi_master", 0x30000000, spi_master.bus, 32)
            spi_master.add_source(self.platform)

            # Custom accel simulator IP core
            self.submodules.accel = accel = AccelCore(freq=sys_clk_freq, baud=115200, pads=self.platform.request("spi_slave", 0))
            self.add_csr("accel", 11, allow_user_defined=True)
            self.add_interrupt("accel", 7, allow_user_defined=True)

            if soc_config["mbx_sender"] in ["yes"]:
                # Integrate mailbox sender
                self.submodules.mbx_snd = mbx_snd = MailBoxSenderInf(self.platform.request("mbx_snd", 0))
                self.add_csr("mbx_snd", 12, allow_user_defined=True)

            if soc_config["mbx_receiver"] in ["yes"]:
                # Integrate mailbox receiver
                self.submodules.mbx_rcv = mbx_rcv = MailBoxReceiverInf(self.platform.request("mbx_rcv", 0))
                self.add_csr("mbx_rcv", 13, allow_user_defined=True)
                self.add_interrupt("mbx_rcv", 8, allow_user_defined=True)

        if soc_config["platform_name"] in ["accel_test"]:
            # Integrate SPI master
            self.submodules.spi_master = spi_master = SpiMaster(self.platform.request("spi", 0))
            self.add_csr("spi_master", 10, allow_user_defined=True)
            self.add_interrupt("spi_master", 6, allow_user_defined=True)
            self.register_mem("spi_master", 0x30000000, spi_master.bus, 32)
            spi_master.add_source(self.platform)

            # Integrate int module
            self.submodules.gpio_isr = GpioISR(self.platform.request("gpio_irq", 0), rissing_edge_detect=False)
            self.add_csr("gpio_isr", 11, allow_user_defined=True)
            self.add_interrupt("gpio_isr", 7, allow_user_defined=True)

            if soc_config["mbx_sender"] in ["yes"]:
                # Integrate mailbox sender
                self.submodules.mbx_snd = mbx_snd = MailBoxSenderInf(self.platform.request("mbx_snd", 0))
                self.add_csr("mbx_snd", 12, allow_user_defined=True)

            if soc_config["mbx_receiver"] in ["yes"]:
                # Integrate mailbox receiver
                self.submodules.mbx_rcv = mbx_rcv = MailBoxReceiverInf(self.platform.request("mbx_rcv", 0))
                self.add_csr("mbx_rcv", 13, allow_user_defined=True)
                self.add_interrupt("mbx_rcv", 8, allow_user_defined=True)

def main():
    # get config
    if len(sys.argv) < 2:
        print("missing config file")
        exit(1)
    exec(open(sys.argv[1]).read(), globals())

    # generate core
    platform = Platform()
    platform.name = soc_config["platform_name"]

    soc = BaseSoC(platform, soc_config,
                  ident=soc_config["soc_ident"],
                  integrated_rom_size=soc_config["rom_size"],
                  integrated_sram_size=soc_config["sram_size"],
                  cpu_type=soc_config["cpu"],
                  cpu_variant=soc_config["cpu_variant"]
                  )

    output_dir = "build/" + soc_config["platform_name"]
    build_name = soc_config["platform_name"] + "_core"

    builder = Builder(soc, output_dir=output_dir , compile_gateware=False)
    vns = builder.build(build_name=build_name, regular_comb=False)

    # prepare core (could be improved)
    def replace_in_file(filename, _from, _to):
        # Read in the file
        with open(filename, "r") as file :
            filedata = file.read()

        # Replace the target string
        filedata = filedata.replace(_from, _to)

        # Write the file out again
        with open(filename, 'w') as file:
            file.write(filedata)

    init_filename = "mem.init"
    mem_1_init_filename = "mem_1.init"
    mem_2_init_filename = "mem_2.init"

    os.system("mv " + output_dir + "/gateware/mem.init " + output_dir + "/gateware/" + build_name + ".init".format(init_filename))
    os.system("mv " + output_dir + "/gateware/mem_1.init " + output_dir + "/gateware/" + build_name + "_mem_1" + ".init".format(mem_1_init_filename))
    os.system("mv " + output_dir + "/gateware/mem_2.init " + output_dir + "/gateware/" + build_name + "_mem_2" + ".init".format(mem_2_init_filename))

    replace_in_file(output_dir + "/gateware/" + build_name + ".v", init_filename, build_name + ".init")
    replace_in_file(output_dir + "/gateware/" + build_name + ".v", mem_1_init_filename, build_name + "_mem_1" + ".init")
    replace_in_file(output_dir + "/gateware/" + build_name + ".v", mem_2_init_filename, build_name + "_mem_2" + ".init")

if __name__ == "__main__":
    main()