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

from __future__ import annotations
from type.cell import cell_t
from task import feature as ft_, segmentation as sg_
from type.frame import frame_t
from type.tracks import tracks_t
from run_parameters import segm_protocol, cyto_seg, track_channel
import imageio as io_
import itk
import matplotlib.pyplot as pl_
import networkx as nx_
import numpy as np_
import scipy as sc_
import scipy.spatial.distance as dt_
from typing import Callable, Optional, Sequence, Tuple
from run_parameters import *
import os
import csv
from skimage import filters
from numpy import ones,vstack
from numpy.linalg import lstsq

class sequence_t:

    def __init__(self) -> None:
        #
        self.frames = {}  # Dictionary "channel" -> list of frames
        self.channel_content={} # Dictionnary to reorganize the frames of each channel
        self.tracking = None  # tracks

    @classmethod

    def FromTiffFile(
        cls,
        path,
        channel_names: Sequence[str],
        from_frame: int = 0,
        to_frame: int = 999999,
        n_channels: int=1,

    ) -> sequence_t:

        """
        FromTiffFile function
        Reads the frames and split channels.
        Gets the frames properties : channel name, time point, content and shape
        """
        #
        # Channel name == '___' => discarded
        #
        instance = cls()

        for name in channel_names:

            if name != "___":
                name= name.upper()
                instance.frames[name] = [] # create an empty list for each channel

        ch_idx = n_channels - 1 # channel index
        time_point = -1

        #get file path
        #frame_reader = io_.get_reader(path) # read the frame

        frame_reader=itk.imread(path)
        frame_reader=itk.array_from_image(frame_reader)#.astype(np_.uint8)

#        assert n_channels * (to_frame + 1) <= len(frame_reader) , \
#            f"Last frame {to_frame} outside sequence ({frame_reader.get_length()} frame(s))"

        for raw_frame in frame_reader:
            ch_idx += 1
            if ch_idx == n_channels: # if n_channels are read
                ch_idx = 0 # channel index is reset to 0
                time_point += 1 # time point increments by 1

            if time_point < from_frame:
                continue
            elif time_point > to_frame:
                break

            name = channel_names[ch_idx].upper() # get channel name

            if name != "___":

                print(f"Frame {name}.{time_point}")
                frame = frame_t.WithProperties(name, time_point, raw_frame) # get the frame properties using "frame" script

                instance.frames[name].append(frame) # save the frame properties

        return instance # return an instance of the frames properties

    def OrganizeFrames(self,channel_names: Sequence[str]):

        for channel in channel_names:
            if channel != "___" :
                channel=channel.upper()

                list_frames=[]
                for frames_ in self.frames[channel]:
                    list_frames.append(frames_.contents)

                self.channel_content[channel]=list_frames

        return self.channel_content

    def __str__(self) -> str:

        """
        __str__ method
        Makes a string containing informations about the segmented frames
        (channel name, image size, time points, number of cells per frame)
        to print at the end of segmentation
        """

        self_as_str = ""

        for channel, frames in self.frames.items():
            initial_time_point = frames[0].time_point
            self_as_str += (
                f"[{channel}]\n" # channel name
                f"    {frames[0].size[1]}x{frames[0].size[0]}x" # image size
                f"[{initial_time_point}..{frames[-1].time_point}]\n" # the first and the last time points
            )

            if segm_protocol == "cell" :
                for f_idx, f_cell in enumerate (self.cells): #(self.nuclei):
                    if f_cell is not None:
                        self_as_str += (
                            f"    {initial_time_point+f_idx}: c {f_cell.__len__()}\n" # time point and the number of cells in the frame
                        )

            elif segm_protocol == "nucl" or segm_protocol == " cell and nucl ":

                for f_idx, f_cell in enumerate (self.nuclei):
                    if f_cell is not None:
                        self_as_str += (
                            f"    {initial_time_point+f_idx}: c {f_cell.__len__()}\n" # time point and the number of cells in the frame
                        )

        return self_as_str # returns a string containing all the informations

    def RegisterChannelsOn(self, ref_channel: str) -> None:

        """
        RegisterChannelsOn function
        Aligns channels by calculating and correcting the between channel shift
        Calls the "RegisterOn" method from "frame" script.
        """

        channel_names = tuple(self.frames.keys()) # get channel names
        ref_channel_idx = channel_names.index(ref_channel) # get the reference channel index
        other_channel_idc = set(range(channel_names.__len__())) - {ref_channel_idx} # indices of the other channels

        ref_frames = self.frames[ref_channel] # get the reference frames

        for c_idx in other_channel_idc:

            floating_frames = self.frames[channel_names[c_idx]] # target channel
            for f_idx in range(ref_frames.__len__()):
                floating_frames[f_idx].RegisterOn(ref_frames[f_idx].contents) # get the between channel shift (see "frame" script)

    def BackgroundNormalization (self, channels,post_processing_fct: Callable = None):

        """
        BackgroundNormalization function
        Normalizes the background using a post processing function (defiened on the run script)
        """

        if post_processing_fct is not None:

            for channel_name in channels:
                #if channel_name != "___" :
                if channel_name == "cherry":
                    channel_name=channel_name.upper()

                    for frames_ in self.frames[channel_name]:
                        frames_.contents= post_processing_fct(frames_.contents)

#############   SEGMENTATION   #############

    def SegmentCellsOnChannel(self, cell_channel: None, nucl_channel: None, cyto_seg: str, file_name:str) -> None:

        """
        SegmentCellsOnChannel function
        Segment cells on each channel
        by calling the "SegmentCells" method from the "frame" script.
        """
        print("SegmentCellsOnChannel function")

        self.cell_channel = cell_channel.upper()
        self.nucl_channel=nucl_channel.upper()
        frames=self.channel_content

        frame_t.SegmentObject(self,frames,cell_channel, nucl_channel, cyto_seg, file_name)

    def RootCells(self):

        """
        RootCells function
        Gets the segmented cells with all the cell properties
        """
        if segm_protocol == "cell" :
            if self.cell_channel is None:
                raise RuntimeError(
                    "Segmentation-related function called before segmentation"
                )

            root_cell= self.cells[0]

        elif segm_protocol== "nucl" or  segm_protocol == "cell and nucl" :

            if self.nucl_channel is None:
                raise RuntimeError(
                    "Segmentation-related function called before segmentation"
                )

            root_cell= self.nuclei[0]

        #return self.frames[self.cell_channel][0].cells # returns the segmented root cells properties (at time_point=0)
        #return self.nuclei[0]

        return root_cell

#############   TRACKING   #################

    def TrackCells(
        self,channel_class: str, bidirectional: bool = True, max_dist: float = np_.inf
    ) -> None:

        """
        TrackCells method
        The tracking could be bidirectional or not.
        If bidirectional: Track cells by finding the nearest neighbor of each current cell.
        When the track is found, it's added to the tracking graph
        """
        if channel_class == "cell":
            track_channel= self.cell_channel
            type_= self.cells

        elif channel_class == "nucl":
            track_channel= self.nucl_channel
            type_= self.nuclei

        elif track_channel is None:
            raise RuntimeError(
                "Segmentation-related function called before segmentation"
            )

        self.tracking = tracks_t()  # call the track_t class from "tracks" script
        #frames = self.frames[track_channel]  # get the frames of the track channel

        for f_idx in range(1, type_.__len__()):
            prev_frame = type_[f_idx - 1] # previous frame
            curr_frame = type_[f_idx] # current frame

            # call the "CellPosition" method from "frame" script

            prev_pos = frame_t.CellPositions(self,prev_frame) # get the previous positions

            curr_pos = frame_t.CellPositions(self,curr_frame) # get the current position

            # calculate the spatial distance ( the shortest distance between prev_pos and curr_pos)
            all_dist = dt_.cdist(prev_pos, curr_pos) # calculate all distances between the previous and the current positions
                                                     # all_dist : is a matrix :all_dist [prev,curr] = distance between prev and curr

            for curr_cell in type_[f_idx]: # for current cell in the list of segmented cells

                curr_uid = curr_cell.uid # get the current cell unique identifier

                if len(all_dist[:, curr_uid]) != 0 :

                    prev_uid = np_.argmin(all_dist[:, curr_uid]) # get the previous cell unique identifier
                                                                 # coresponding to the identifier of the lowest distance over all the forward distances

                    dist= all_dist[prev_uid, curr_uid]

                    if dist <= max_dist:

                        if bidirectional:
                            #sym=symmetric
                            sym_curr_uid = np_.argmin(all_dist[prev_uid, :]) # get the identifier of lowest distance over all the backward distances

                            if sym_curr_uid == curr_uid:

                                # Note: a cell has a unique next cell due to bidirectional constraint
                                prev_cell = type_[f_idx-1][prev_uid] # get the previous cell
                                # add an edge on the graph between the previous and the current cell
                                self.tracking.add_edge(prev_cell, curr_cell)

                        else: # if not bidirectional
                            # previous cell
                            prev_cell = type_[f_idx-1][prev_uid]

                            # current cell
                            curr_pos=np_.array([[curr_cell.position[0]],[curr_cell.position[1]]])

                            self.tracking.add_edge(prev_cell, curr_cell)

        return all_dist

    def PlotTracking(self,file:str, show_figure: bool = True) -> None:
        """
        PlotTracking method
        Plots the tracking 3D graphs, using the "Plot" function of "tracks" script
        """
        if self.tracking is None:
            raise RuntimeError("Tracking-related function called before tracking")

        self.tracking.Plot(file ,show_figure=show_figure) # plot tracking

    def CellFeatureNames(self) -> tuple:
        """
        CellFeatureNames
        Get the cell feature names from the "features" dictionary
        """
        # get the cell feature names from features dictionary (use "cell" script)
        #return tuple(self.frames[self.segm_channel][0].cells[0].features.keys())
        return tuple(self.cells[0][0].features.keys())

    def OrganizeFeatures(self,features:dict)->None:

        #for type_, channels in features.items():
        for channel, types in features.items():
            for type_,root_cells in types.items():
                for idx, root_cell  in enumerate(root_cells):
                    if len(root_cell)>0:
                        cells_pix=[]
                        cells=[]
                        for cell in root_cell:
                            cells_pix.append(len(cell))
                            cells.append(cell)

                        signal=np_.empty((max(cells_pix),len(root_cell)))*np_.NaN

                        for col,cell in enumerate(cells) :

                            signal[0:len(cell),col]=cell

                        root_cells[idx]=signal

                    elif len(root_cell)==0:
                        root_cells[idx]= 1*np_.NaN

        return features

    def ComputeCellFeatures(self, protocol: str,same_channels:None, feature_channel:str,
                            cell_sig: None,
                            nucl_sig: None,
                            cyto_sig:None)-> None:

        """
        This fuction computes feature using (GetUniSignal) and (GetMultiSignals)
        functions.
        """

        if self.cell_channel is None or self.nucl_channel is None :

            raise RuntimeError(
                "Segmentation-related function called before segmentation"
            )

        intensities={}

        features={}

        protocol=protocol.lower()

        if protocol == "cell" :  # if cellular segmentation only               >>>>>> OK !

            for channel in cell_sig:

                channel= channel.upper()
                signal, props= GetUniSignal(self,channel,feature_channel,protocol)
               # signal, props= GetUniSignal(self,channel,feature_channel,"cell")

                intensities[channel]=signal
                if channel== feature_channel.upper():
                    features[channel]= props

        elif protocol == "nucl" : # if nuclear segmentation only               >>>>>> OK !

            print(" nuclear features ")

            for channel in nucl_sig:
                channel= channel.upper()
                signal,props= GetUniSignal(self,channel,feature_channel,"nucl")
                intensities[channel]=signal

                if channel== feature_channel.upper():
                    features[channel]= props

        elif protocol == "cell and nucl": # if cellular and nuclear segmentation >>>>>> OK POUR CETTE PARTIE !

            if same_channels != None:

                for channel in same_channels:
                    channel= channel.upper()
#                    signal,props= signal_.GetMultiSignals(self,channel,channel,channel,feature_channel,
#                                           "cell and nucl")
                    signal,props= GetMultiSignals(self,channel,channel,channel,feature_channel,
                                           "cell and nucl")                     # >>>>>>>>>>>>>> REVOIR LES PARAM D'ENTREE DE LA FONCTION !!!

                    intensities[channel]=signal
                    features = props

#>>>>>>>>>>>>>>>>>>>>>>>>>> A REVOIR !!!! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

            elif same_channels == None:

#______________________________________________________________________________

#======================== 1ERE PARTIE =========================================

                if len(cell_sig)==1 and len(nucl_sig)==1: # if the signal to extract is from one channel per structure
                    print ("len(cell_sig)==1 and len(nucl_sig)==1")
                    cell_sig= cell_sig[0].upper()
                    nucl_sig= nucl_sig[0].upper()

                    if cyto_sig== None : # if cyto segmentation is not performed
                        print("cyto_sig== None")
                        signal,props= GetMultiSignals(self,cell_sig,nucl_sig,None,feature_channel,
                                               "cell and nucl")
                        #intensities=self.OrganizeFeatures(signal)   # >>>>>>>> OrganizeFeatures : PREND QUE LE DICT DE SIG_TYPE ET LA ON A LES 2 CHANNEL ET SIG !!
                        intensities=signal
                        features = props
#==============================================================================

#======================== 2EME PARTIE =========================================

                    elif cyto_sig != None and len(cyto_sig)==1: # if cyto segmentation

                        cyto_sig= cyto_sig[0].upper()

                        signal,props= GetMultiSignals(self,cell_sig,nucl_sig,cyto_sig,feature_channel,
                                                   "cell and nucl")

                        #intensities=self.OrganizeFeatures(signal)   # >>>>>>>> OrganizeFeatures : PREND QUE LE DICT DE SIG_TYPE ET LA ON A LES 2 CHANNEL ET SIG !!
                        intensities=signal
                        features = props

#==============================================================================

#======================== 3EME PARTIE =========================================

                elif len(cell_sig) >1 or len(nucl_sig)>1:

                    print ("len(cell_sig) >1 or len(nucl_sig)>1 ")

                    for channel in cell_sig:

                        if cyto_sig == None:         # >>>>>>>>>>> OK !!

                            if channel in nucl_sig:

                                    print(channel)
                                    channel= channel.upper()
                                    signal,props= GetMultiSignals(self,channel,channel,None,feature_channel,
                                                   "cell and nucl")

                                    intensities[channel]= signal
                                    #intensities[channel]=signal
                                    features = props

#==============================================================================

#======================== 4EME PARTIE =========================================

                        elif cyto_sig != None :          # >>>>>>>>>> OK !!!!!

                            print("cyto_sig != None")
                            if channel in cyto_sig and channel not in nucl_sig:
                                print(channel)
                                channel= channel.upper()

                                signal= GetMultiSignals(self,channel,None,channel,None, # >>>> OK !
                                                              "cell and nucl")
                                #intensities[channel]=self.OrganizeFeatures(signal)
                                intensities=signal
                                #features = props

                            elif channel in cyto_sig and channel not in cell_sig:
                                print(channel)
                                channel= channel.upper()

                                signal= GetMultiSignals(self,None,channel,channel,None, # >>>> OK !
                                                              "cell and nucl")
                                #intensities[channel]=self.OrganizeFeatures(signal)
                                intensities=signal
                                #features = props

                            else:
                                print("else cyto_sig")
                                channel= channel.upper()
                                signal,props= GetMultiSignals(self,channel,channel,channel,feature_channel,
                                               "cell and nucl")
                                intensities[channel]=signal
                                #intensities=signal
                                features = props

#==============================================================================

                        else:                        # >>>>>>>>>> OK !!!!
                            print ( "ELSE ")

                            if channel in cell_sig and channel not in nucl_sig:

#                                channel= channel.upper()
#
#                                signal= GetMultiSignals(self,channel,channel, None, None,"cell and nucl")
#                                intensities[channel]=self.OrganizeFeatures(signal)

#                            elif channel in cell_sig:

                                cell_channel=channel.upper()
                                cell_signal, props= GetUniSignal(self,cell_channel,feature_channel,"cell")

                                intensities[cell_channel]=self.OrganizeFeatures(cell_signal)
                                features = props

                            elif channel in nucl_sig and channel not in cell_sig:

                                nucl_channel=channel.upper()
                                nucl_signal, props= GetUniSignal(self,nucl_channel,feature_channel,"nucl")

                                intensities[channel]=nucl_signal
                            #intensities[channel]=cell_signal
                                features = props
#______________________________________________________________________________

#                    for channel in cell_sig :
#
#                        if channel not in nucl_sig:
#
#                            print("nucl channels")
#                            print(channel)
#
#
#                            if channel == feature_channel:
#
#                                print ("if")
#                                channel=channel.upper()
#                                nucl_signal, props= GetUniSignal(self,channel,feature_channel,"nucl")
#
#                            else:                                    # >>>>>>> OK !!!!!
#                                print("else")
#                                channel=channel.upper()
#                                nucl_signal= GetUniSignal(self,channel,None,"nucl")
#                                intensities[channel]=self.OrganizeFeatures(nucl_signal)
#
#
#
#                    for channel in nucl_sig:                # >>>>>>> A REVOIR POUR LES FEATURES !!! PQ LE DICT EST ECRASé
#                        # POUR LE SIGNAL :
#                        # est ce qu'il faut avoir un canal en commun ?
#                        # revoir canal commun entre cyto et nucl
#                        # pour ne pas ecraser
#
#
#                        if channel not in cell_sig:
#
#                            print("nucl channels")
#                            print(channel)
#
#
#                            if channel == feature_channel:
#
#                                print ("if")
#                                channel=channel.upper()
#                                nucl_signal, props= GetUniSignal(self,channel,feature_channel,"nucl")
#
#                            else:                                    # >>>>>>> OK !!!!!
#                                print("else")
#                                channel=channel.upper()
#                                nucl_signal= GetUniSignal(self,channel,None,"nucl")
#                                intensities[channel]=self.OrganizeFeatures(nucl_signal)
#                                #intensities[channel]=nucl_signal
#                            #features = props
#
#                                if cyto_sig != None:
#                                    print("cyto_sig != None")
#                                    for channel in cyto_sig:
#                                        if channel not in cell_sig:
#                                            channel=channel.upper()
#                                            cyto_signal = GetUniSignal(self,channel,None,"cyto")
#                                            intensities[channel]=self.OrganizeFeatures(cyto_signal)
#                                            #intensities[channel]=cyto_signal
#                                            #features = props
        #signal=self.OrganizeFeatures(signal)

        return self.OrganizeFeatures(intensities),features #props

    def CalculSignal(self,file:str, intensity: dict,
                     numerator:str, denominator: str, num_struct:str, denom_struct:str,
                     mean: bool, median: bool, ratio: bool):

        """
        This fuction computes statistics operations on the extracted signal
        """

        calc_sig={}

        for channel, types in intensity.items():
            for type_,frames in types.items():
                cell_num=[]

                for idx, frame in enumerate(frames):
                    if type(frame) != float :
                        cell_num.append(frame.shape[1])

        cell_num=max(cell_num)
        frame_num=len(frames)

        for channel, types in intensity.items():
            calc_sig[channel]={}
            for type_,frames in types.items():
                calc_sig[channel][type_]={}
                #med=np_.empty((frame_num,cell_num))*np_.NaN
                #avg=np_.empty((frame_num,cell_num))*np_.NaN

                med=np_.empty((cell_num,frame_num))*np_.NaN
                avg=np_.empty((cell_num,frame_num))*np_.NaN

                if median== True and mean ==False or mean== True and median == False:
                    if median==True :
                        operation= "median"
                        calc_sig[channel][type_][operation]={}

                        for col, frame in enumerate(frames):
                            for idx in range(0,frame.shape[1]):

                                signal=frame[:,idx]
                                signal=signal[~ np_.isnan(signal)]

                                med[idx,col]= np_.median(signal)
                        sig=med

                    elif mean==True:
                        operation="mean"
                        calc_sig[channel][type_][operation]={}

                        for col, frame in enumerate(frames):
                            for idx in range(0,frame.shape[1]):

                                signal=frame[:,idx]
                                signal=signal[~ np_.isnan(signal)]

                                avg[idx,col]= np_.mean(signal)

                        sig=avg

                    calc_sig[channel][type_][operation]=sig

                    if not os.path.exists("output/"+str(file)+"/signal/"+str(operation)):
                        os.makedirs("output/"+str(file)+"/signal/"+str(operation))

                    fname= "output/"+str(file)+"/signal/"+str(operation)+"/"+str(type_)+"_"+str(operation)+".csv"

                    np_.savetxt(fname, sig, delimiter=",")

                elif median == True and mean == True :

                    if not os.path.exists("output/"+str(file)+"/signal/median_intensity"):
                        os.makedirs("output/"+str(file)+"/signal/median_intensity")

                    if not os.path.exists("output/"+str(file)+"/signal/mean_intensity"):
                        os.makedirs("output/"+str(file)+"/signal/mean_intensity")

                    calc_sig[channel][type_]["median"]={}
                    calc_sig[channel][type_]["mean"]={}

                    for col, frame in enumerate(frames):
                        if type(frame) != float :
                            for idx in range(0,frame.shape[1]):

                                signal=frame[:,idx]
                                signal=signal[~ np_.isnan(signal)]

                                med[idx,col]=np_.median(signal)
                                avg[idx,col]= np_.mean(signal)

                            calc_sig[channel][type_]["median"]=med
                            calc_sig[channel][type_]["mean"]=avg

                            fname1= "output/"+str(file)+"/signal/median_intensity/"+str(type_)+"_median.csv"
                            np_.savetxt(fname1, med, delimiter=",")

                            fname2= "output/"+str(file)+"/signal/mean_intensity/"+str(type_)+"_mean.csv"
                            np_.savetxt(fname2, avg, delimiter=",")

        if ratio== True :

            if median==True and mean==False :

                ratio_mat=calc_sig[numerator.upper()][num_struct.lower()]["median"]/calc_sig[denominator.upper()][denom_struct.lower()]["median"]
                calc_sig["median_ratio"]=ratio_mat
                fname= "output/"+str(file)+"/signal/median_ratio_.csv"
                np_.savetxt(fname, ratio_mat, delimiter=",")

            elif mean == True and median==False:
                ratio_mat=calc_sig[numerator.upper()][num_struct.lower()]["mean"]/calc_sig[denominator.upper()][denom_struct.lower()]["mean"]
                calc_sig["mean_ratio"]=ratio_mat
                fname= "output/"+str(file)+"/signal/mean_ratio_.csv"
                np_.savetxt(fname, ratio_mat, delimiter=",")

            elif median== True and mean == True :
                ratio_med=calc_sig[numerator.upper()][num_struct.lower()]["median"]/calc_sig[denominator.upper()][denom_struct.lower()]["median"]
                calc_sig["median_ratio"]=ratio_med
                fname= "output/"+str(file)+"/signal/median_ratio_.csv"
                np_.savetxt(fname, ratio_med, delimiter=",")

                ratio_avg=calc_sig[numerator.upper()][num_struct.lower()]["mean"]/calc_sig[denominator.upper()][denom_struct.lower()]["mean"]
                calc_sig["mean_ratio"]=ratio_avg
                fname1= "output/"+str(file)+"/signal/mean_ratio_.csv"
                np_.savetxt(fname1, ratio_avg, delimiter=",")

        return calc_sig

    def CellFeatureEvolution(self, root_cell: cell_t, feature: str) -> list:

        """
        CellFeatureEvolution function
        Calculates the feature evolution of each cell between the previous and the current position.
        Use the "TrackContainingCell" method of the "tarcks" script.
        """

        if self.tracking is None:
            raise RuntimeError("Tracking-related function called before tracking")
        # self.segm_channel is necessarily not None

        evolution = []

        track = self.tracking.TrackContainingCell(root_cell) # call the tracking method from "tracks" script
        if track is not None:
            current_piece = []
            for from_cell, to_cell in nx_.dfs_edges(track, source=root_cell): # get the current and the next cell positions idx

                if current_piece.__len__() == 0:
                    if feature in from_cell.features:
                        current_piece.append(
                            (from_cell.time_point, from_cell.features[feature]) # save the current cell position time point and features
                        )
                    else:
                        current_piece.append(
                            (from_cell.time_point, 0) # save the current cell position time point and features
                        )

                if feature not in to_cell.features:
                    if feature in from_cell.features:
                        current_piece.append((to_cell.time_point, from_cell.features[feature]))
                    elif feature not in from_cell.features:
                        current_piece.append((to_cell.time_point, 0))
                else:
                    current_piece.append((to_cell.time_point, to_cell.features[feature])) # save the next cell position time point and features

                if track.out_degree(to_cell) != 1: # if the number of edges pointing out of the node is # 1
                    evolution.append(current_piece) # save the current piece (time_point,feature)
                    current_piece = []

        return evolution

    def PlotCellFeatureEvolutions(
        self,
        cell_list: Sequence,
        feature: str,
        file: str,
        show_figure: bool = True,
    ) -> None:

        """
        This function plots the cell feature evolutions
        calculated by the "CellFeatureEvolution" function
        """

        figure = pl_.figure()
        axes = figure.gca() # get the current axes
        axes.set_title(feature) # set a title
        axes.set_xlabel("time points") # set the x label
        axes.set_ylabel("feature value") # set the y label
        plots = []
        labels = []
        colors = "bgrcmyk"

        for root_cell in cell_list:
            color_idx = root_cell.uid % colors.__len__()
            subplot = None

            for piece in self.CellFeatureEvolution(root_cell, feature): # get the feature evolution
                if not (isinstance(piece[0][1], int) or isinstance(piece[0][1], float)): # if the feature value isn't "int" or "float" type
                    break

                time_points = []
                feature_values = []
                for time_point, feature_value in piece: # get the time point and the feature value
                    time_points.append(time_point) # save the time point
                    feature_values.append(feature_value) # save the feature value

                subplot = axes.plot(
                    time_points, feature_values, colors[color_idx] + "-x"
                )[0] # plot f(time_points, feature_values)

            if subplot is not None:
                plots.append(subplot)
                labels.append(f"root_cell {root_cell.uid}") # get uid labels

        if plots.__len__() > 0: # if "plots" list isn't empty
            axes.legend(handles=plots, labels=labels, loc='lower right') # place the legend on the axes
            # save plot
            pl_.savefig(str(file)+"/plot_features/"+feature)
        else:
            pl_.close(figure)

        if show_figure:
            pl_.show()

    def WriteCellSignalEvolution(self, signal_list,file,type_,idx) :

        # save features in csv file

        if not os.path.exists("output/"+str(file)+"/signal/total_intencity/"+str(type_)):
            os.makedirs("output/"+str(file)+"/signal/total_intencity/"+str(type_))

        fname= "output/"+str(file)+"/signal/total_intencity/"+str(type_)+"/Track_Signal_"+str(idx+1)+".csv"

        np_.savetxt(fname, signal_list, delimiter=",", fmt='%s')

    def WriteFeatureEvolution(self, features_dict,file,type_):

        header1=["time_points", "uid", "position_X", "position_Y","area", "shift", "edge","bbox_area", "convex_area","eccentricity",
                "equivalent_diameter","maj_axis_len","min_axis_len","perimeter" ]

        header2= ["time_points", "uid", "position_X", "position_Y","area"]

        for type_, features in features_dict.items():

            if not os.path.exists("output/"+str(file)+"/features/"+str(type_)):
                os.makedirs("output/"+str(file)+"/features/"+str(type_))

            for feature_name, feature_lists in features.items():

                for idx, list_values in enumerate(feature_lists):

                    fname= "output/"+str(file)+"/features/"+str(type_)+"/Cell_"+str(idx+1)+".csv"

                    with open(fname,"w", newline='') as f :
                       if type_ == "cell":
                           writer = csv.DictWriter(f, fieldnames=header1)
                           writer.writeheader()

                       else:
                           writer = csv.DictWriter(f, fieldnames=header2)
                           writer.writeheader()

                       if len(list_values)>1:
                           for frame in range(0,len(list_values)):

                               if type_ == "cell":
                                    if frame < len(list_values):
                                        line={"time_points":features["time_points"][idx][frame], "uid":features["uid"][idx][frame],
                                               "position_X":features["position"][idx][frame][0],
                                               "position_Y":features["position"][idx][frame][1],"area":features["area"][idx][frame],
                                               "shift":features["shift"][idx][frame], "edge":features["edge"][idx][frame],
                                               "bbox_area":features["bbox_area"][idx][frame], "convex_area":features["convex_area"][idx][frame],
                                               "eccentricity":features["eccentricity"][idx][frame],
                                               "equivalent_diameter":features["equivalent_diameter"][idx][frame],
                                               "maj_axis_len":features["major_axis_length"][idx][frame],
                                               "min_axis_len":features["minor_axis_length"][idx][frame],
                                               "perimeter":features["perimeter"][idx][frame]}

                                        writer.writerow(line)

                               else:
                                    if frame < len(list_values):
                                        line={"time_points":features["time_points"][idx][frame], "uid":features["uid"][idx][frame],
                                               "position_X":features["position"][idx][frame][0],
                                               "position_Y":features["position"][idx][frame][1],"area":features["area"][idx][frame]}

                                        writer.writerow(line)

    def CellLabeling(self,channel,singnal,uid,file, features,intensity):

        """
        CellLabeling function allows to identify cells in the original image,
        by labeling them with their cell unique identifier.
        """

        if self.cell_channel != None :

            name="segmentation_map"

            if not os.path.exists("output/"+str(file)+"/"+name):
                os.makedirs("output/"+str(file)+"/"+name)

        #        for idx,frame in enumerate (self.channel_content[channel]):
        #
        #            if idx % 10 == 0 :
        #                pl_.figure(figsize=(30,30))
        #                pl_.imshow(frame, cmap="gray")
        #
        #                for cell in range(0,len(features["nucl"]["uid"])):
        #
        #                    if uid=="root_cell_uid": # the cell is labeled with the same uid over the diferent frames
        #                        if len(features["nucl"]["uid"][cell])>0 and idx < len(features["nucl"]["position"][cell]):
        #                            pl_.text(features["nucl"]["position"][cell][idx][0],features["nucl"]["position"][cell][idx][1],
        #                                         str(features["nucl"]["uid"][cell][0]),color="red", fontsize=40)
        #
        #                    elif uid=="cell_uid": # the cell is labeled with the specfic frame uid, if the uid isn't available,
        #                                       # the cell is labeled with "x"
        #                       if len(features["nucl"]["position"][cell])-1>= idx:
        #                           pl_.text(features["nucl"]["position"][cell][0][0],features["nucl"]["position"][cell][0][1],
        #                                     str(features["nucl"]["uid"][cell][idx]),color="red", fontsize=40)
        #                       else:
        #
        #                           pl_.text(features["nucl"]["position"][cell][0][0],features["nucl"]["position"][cell][0][1],
        #                                 "x" ,color="red", fontsize= 40 )

        #idx=0

        for idx in range (len(self.channel_content[channel])):

            frame= self.channel_content[channel][idx]
            pl_.figure(figsize=(30,30))
            pl_.imshow(frame, cmap="gray")

            if track_channel == "nucl" :
                for cell in range(0,len(features["cell"]["uid"])):
                    if len(features["cell"]["uid"][cell])>idx :
#                        pl_.text(features["cell"]["position"][cell][idx][0],features["cell"]["position"][cell][idx][1],
#                                     str(features["cell"]["uid"][cell][0]),color="red", fontsize=50)

                        pl_.text(features["cell"]["position"][cell][idx][0],features["cell"]["position"][cell][idx][1],
                                     str(cell),color="red", fontsize=50)

            elif track_channel == "cell" :

                for cell in range(0,len(features["cell"]["uid"])):
                    #print(cell)
                    if len(features["cell"]["uid"][cell])>0 :
                        pl_.text(features["cell"]["position"][cell][idx][0],features["cell"]["position"][cell][idx][1],
                                     str(features["cell"]["uid"][cell][0]),color="red", fontsize=50)

            # save figures

            path= "output/"+str(file)+"/"+name+"/frame"+str(idx)+".jpg"
            pl_.savefig(path)
            pl_.close()

        # ========= plot cell phenotype ==================================

#        for idx in range (len(self.channel_content[channel])):
#
#            frame= self.channel_content[channel][idx]
#            pl_.figure(figsize=(30,30))
#            pl_.imshow(frame, cmap="gray")
#
#            if track_channel == "nucl" :
#                for cell in range(0,len(features["cell"]["uid"])):
#                    if len(features["cell"]["uid"][cell])>idx :
##                        pl_.text(features["cell"]["position"][cell][idx][0],features["cell"]["position"][cell][idx][1],
##                                     str(features["cell"]["uid"][cell][0]),color="red", fontsize=50)
#
#                        pl_.text(features["cell"]["position"][cell][idx][0],features["cell"]["position"][cell][idx][1],
#                                     str(cell),color="red", fontsize=50)

        #=================================================================

        self.channel_content[channel]=None
        self.channel_content["CHERRY"]=None

    def GetTrainingMatrix(self,from_frame, to_frame,file,features,signal):

        if not os.path.exists("output/"+str(file)+"/features/cell_death_training"):
            os.makedirs("output/"+str(file)+"/features/cell_death_training")

        frame_number= to_frame-from_frame

        feature_names=[]
        matrices=[]

        row=len(features["cell"]["area"])
        col=len(features["cell"])-2

        for frame_idx in range(0,frame_number+1):

            frame_vec=[]
            training_matrix=np_.empty((row,col))*np_.NaN

            for feature_name, feature_list in features["cell"].items():

                if feature_name != "time_points" and feature_name != "pixels" and feature_name != "uid" and feature_name != "position":

                    feature_names.append(feature_name)
                    vec=np_.empty((row))*np_.NaN

                    for cell_idx, cell_features in enumerate(feature_list):

                        if len(cell_features) >= 0 and len(cell_features) > frame_idx: # >=10 quand j'analyse >100 frames

                            vec[cell_idx]=cell_features[frame_idx]

                    frame_vec.append(vec)

                    for col_idx, vector in enumerate(frame_vec):

                        training_matrix[:,col_idx]=vector

                    mean_matrix=signal["YFP"]["cell"]["mean"]
                    mean_matrix=mean_matrix.T
                    training_matrix[:,-2]=mean_matrix[:,frame_idx]

                    median_matrix=signal["YFP"]["cell"]["median"]
                    median_matrix=median_matrix.T
                    training_matrix[:,-1]=median_matrix[:,frame_idx]

#            mask = np_.all(np_.isnan(training_matrix) , axis=1)
#            training_matrix=training_matrix[~mask]
            matrices.append(training_matrix)
            fname= "output/"+str(file)+"/features/cell_death_training/cells_"+str(frame_idx)+".csv"

            np_.savetxt(fname,training_matrix, delimiter=",")

        feature_names.append("mean_intensity")
        feature_names.append("median_intensity")

        return matrices,feature_names

def GetMultiSignals(self,cell: None, nucl:None, cyto: None, features_channel: None,
                  class_: str):

    features={} # features dictionary
    intensities={} # signal dictionary

    if cell != nucl :
        if cell != None :
            intensities[cell] ={}
            intensities[cell]["cell"]=[]
            cell_type=self.cells
            cell_channel=cell
            cell_content= self.channel_content[cell_channel]

        elif nucl != None :
            intensities[nucl]= {}
            intensities[nucl]["nucl"]=[]
            nucl_type=self.nuclei
            nucl_channel=nucl
            nucl_content= self.channel_content[nucl_channel]

        if cyto != None :
            if cyto != cell and cyto == nucl or cyto == cell and cyto != nucl:
                print ("cell != cyto")
                #intensities[cyto]= {}
                intensities[cyto]["cyto"]=[]
                cyto_type=self.cyto
                cyto_channel=cyto
                cyto_content= self.channel_content[cyto_channel]

    else :
        if cell != None :
            intensities["cell"]=[]
            cell_type=self.cells
            cell_channel=cell
            cell_content= self.channel_content[cell_channel]

        if nucl != None :
            intensities["nucl"]=[]
            nucl_type=self.nuclei
            nucl_channel=nucl
            nucl_content= self.channel_content[nucl_channel]

        if cyto != None :
            intensities["cyto"]=[]

            cyto_type=self.cyto
            cyto_channel=cyto
            cyto_content= self.channel_content[cyto_channel]

    if features_channel != None:

        if cell != None :
            features["cell"]={}
            features["cell"]["time_points"]=[]
            features["cell"]["pixels"]=[]
            features["cell"]["uid"]=[]
            features["cell"]["position"]=[]
            features["cell"]["area"]=[]
            features["cell"]["edge"]=[]
            features["cell"]["shift"]=[]
            features["cell"]["bbox_area"]=[]
            features["cell"]["convex_area"]=[]
            features["cell"]["eccentricity"]=[]
            features["cell"]["equivalent_diameter"]=[]
            features["cell"]["major_axis_length"]=[]
            features["cell"]["minor_axis_length"]=[]
            features["cell"]["perimeter"]=[]

        if nucl != None :
            features["nucl"]={}
            features["nucl"]["time_points"]=[]
            features["nucl"]["pixels"]=[]
            features["nucl"]["uid"]=[]
            features["nucl"]["position"]=[]
            features["nucl"]["area"]=[]

        if cyto != None :

            features["cyto"]={}
            features["cyto"]["time_points"]=[]
            features["cyto"]["pixels"]=[]
            features["cyto"]["uid"]=[]
            features["cyto"]["position"]=[]
            features["cyto"]["area"]=[]

    for c_idx, root_cell in enumerate(self.RootCells()):

        track = self.tracking.TrackContainingCell(root_cell)

        if track is not None:

            leaves=[val for val in track.nodes() if track.out_degree(val)==0 and track.in_degree(val)==1]

            for leaf in leaves :
                # shortest path : list of nodes that constitue the shortest path between the root cell and the leaf
                sub_track=nx_.shortest_path(track, source=root_cell, target=leaf) # LISTE

                if cell != None :
                    cell_signal=[]

                if nucl != None :
                    nucl_signal=[]

                if cyto != None :
                    cyto_signal=[]

                if features_channel != None:

                    if cell != None :

                        cell_tp=[]
                        cell_pixels=[]
                        cell_uid=[]
                        cell_position=[]
                        cell_area=[]
                        cell_edge=[]
                        cell_shift=[0]
                        cell_bbox_area=[]
                        cell_convex_area=[]
                        cell_eccentricity=[]
                        cell_equivalent_diameter=[]
                        cell_major_axis_length=[]
                        cell_minor_axis_length=[]
                        cell_perimeter=[]

                    if nucl != None :
                        nucl_tp=[]
                        nucl_pixels=[]
                        nucl_uid=[]
                        nucl_position=[]
                        nucl_area=[]

                    if cyto != None :

                        cyto_tp=[]
                        cyto_pixels=[]
                        cyto_uid=[]
                        cyto_position=[]
                        cyto_area=[]

                for nucl_ in sub_track:

                    if track.out_degree[nucl_]< 3:

                        tp=nucl_.time_point
                    #to_tp=to_nucl.time_point

                        if cell != None :
                            cell_frame=cell_content[tp]
                            #to_cell_frame=cell_content[to_tp]

                        if nucl != None :
                            nucl_frame=nucl_content[tp]
                            #to_nucl_frame=nucl_content[to_tp]

                        for idx, cell_ in enumerate(cell_type[tp]) :

                            cell_pix=set(zip(*cell_.pixels))
                            nucl_pix=set(zip(*nucl_.pixels))

                            if len(nucl_pix.intersection(cell_pix))>0 and nucl_.features["area"]>50 and cell_.features["area"]>100:

                                if cell != None :
                                    cell_signal.append(cell_frame[cell_.pixels])
                                if nucl != None :
                                    nucl_signal.append(nucl_frame[nucl_.pixels])

                                if features_channel != None:
                                    if cell != None :
                                        cell_pixels.append(cell_.pixels)
                                        cell_tp.append(tp)
                                        cell_uid.append(cell_.uid)
                                        cell_position.append(cell_.position)
                                        cell_area.append(cell_.features["area"])
                                        cell_edge.append(cell_.features["edge"])
                                        cell_bbox_area.append(cell_.features["bbox_area"])
                                        cell_convex_area.append(cell_.features["convex_area"])
                                        cell_eccentricity.append(cell_.features["eccentricity"])
                                        cell_equivalent_diameter.append(cell_.features["equivalent_diameter"])
                                        cell_major_axis_length.append(cell_.features["major_axis_length"])
                                        cell_minor_axis_length.append(cell_.features["minor_axis_length"])
                                        cell_perimeter.append(cell_.features["perimeter"])

                                    if nucl != None :
                                        nucl_pixels.append(nucl_.pixels)
                                        nucl_tp.append(tp)
                                        nucl_uid.append(nucl_.uid)
                                        nucl_position.append(nucl_.position)
                                        nucl_area.append(nucl_.features["area"])

                                if cyto_seg == "yes" and  cyto != None:

                                    cyto_frame=cyto_content[tp]

                                    for id_,cyto_ in enumerate(cyto_type[tp]):
                                        cyto_pix= set(zip(*cyto_.pixels))

                                        if len(cell_pix.intersection(cyto_pix))>0 and cyto_.features["area"]>50:

                                            cyto_signal.append(cyto_frame[cyto_.pixels])

                                            if features_channel != None:
                                                cyto_pixels.append(cyto_.pixels)
                                                cyto_tp.append(tp)
                                                cyto_uid.append(cyto_.uid)
                                                cyto_position.append(cyto_.position)
                                                cyto_area.append(cyto_.features["area"])

        if cell != nucl :
            if cell != None :
                intensities[cell]["cell"].append(cell_signal)
            if nucl != None :
                intensities[nucl]["nucl"].append(nucl_signal)

            if cyto != None :#and cyto != cell and cyto != nucl:
                intensities[cyto]["cyto"].append(cyto_signal)

        else :
            if cell != None :
                intensities["cell"].append(cell_signal)
            if nucl != None :
                intensities["nucl"].append(nucl_signal)

            if cyto != None :
                intensities["cyto"].append(cyto_signal)

        if features_channel != None:
             #cell
            if cell != None :
                features["cell"]["pixels"].append(cell_pixels)
                features["cell"]["time_points"].append(cell_tp)
                features["cell"]["uid"].append(cell_uid)
                features["cell"]["position"].append(cell_position)
                features["cell"]["area"].append(cell_area)
                features["cell"]["edge"].append(cell_edge)
                features["cell"]["bbox_area"].append(cell_bbox_area)
                features["cell"]["convex_area"].append(cell_convex_area)
                features["cell"]["eccentricity"].append(cell_eccentricity)
                features["cell"]["equivalent_diameter"].append(cell_equivalent_diameter)
                features["cell"]["major_axis_length"].append(cell_major_axis_length)
                features["cell"]["minor_axis_length"].append(cell_minor_axis_length)
                features["cell"]["perimeter"].append(cell_perimeter)

                for i in range (1,len(cell_position)):  # LE METTRE DANS UNE FINCTION A PART
                    prev_cell=np_.empty((1, 2))
                    curr_cell=np_.empty((1, 2))
                    prev_cell[0,:]= cell_position[i-1]
                    curr_cell[0,:]= cell_position[i]
                    cell_shift.append(np_.linalg.norm(curr_cell-prev_cell))

                features["cell"]["shift"].append(cell_shift)

            #nucl
            if nucl != None :
                features["nucl"]["pixels"].append(nucl_pixels)
                features["nucl"]["time_points"].append(nucl_tp)
                features["nucl"]["uid"].append(nucl_uid)
                features["nucl"]["position"].append(nucl_position)
                features["nucl"]["area"].append(nucl_area)

            #cyto
            if cyto != None :
                features["cyto"]["pixels"].append(cyto_pixels)
                features["cyto"]["time_points"].append(cyto_tp)
                features["cyto"]["uid"].append(cyto_uid)
                features["cyto"]["position"].append(cyto_position)
                features["cyto"]["area"].append(cyto_area)

    if features_channel != None:

        return intensities,features

    else :
        return intensities

def GetUniSignal(self, channel:str, feature_channel:str, class_: str) -> None:

    """
    This fuction computes cell signal and features in the case of using a
    unique segmentation (cellular OR nuclear)

    """

    intensities={} # signal dictionary
    features={}  # features dictionary

    # choose the class according to the segmentation protocol

    if class_ == "cell":

        #class_=class_.lower()
        type_=self.cells

        #intensities[class_]=[]

    elif class_ == "nucl":

        #class_=class_.lower()
        type_=self.nuclei

        #intensities[class_]=[]

    elif class_ == "cyto":

        #class_=class_.lower()
        type_=self.cyto

    class_=class_.lower()
    intensities[class_]=[]

    content= self.channel_content[channel] # frame content

    #intensities[class_][channel]=[]

    if feature_channel != None and channel== feature_channel.upper():  # if the analysed channel is the channel to use to extract features

    # les listes finales :
        features[class_]={}
        features[class_]["pixels"]=[]
        features[class_]["time_points"]=[]
        features[class_]["uid"]=[]
        features[class_]["position"]=[]
        features[class_]["area"]=[]

    for c_idx, root_cell in enumerate(self.RootCells()): # for each root_cell in RootCells list

        track = self.tracking.TrackContainingCell(root_cell) # get the root cell corresponding track

    # initialize feature lists for each root_cell ( for each track )

        signal=[]
        tp=[]
        pixels=[]
        uid=[]
        position=[]
        area=[]

        if track is not None: # check if the track isn't empty

            for from_cell, to_cell in nx_.dfs_edges(track, source=root_cell): #

                if track.out_degree[to_cell] < 3:

                    from_tp=from_cell.time_point # get the current frame time point
                    to_tp=to_cell.time_point # get the next frame time point

                    from_cell_frame=content[from_tp] # get the current frame content
                    to_cell_frame=content[to_tp] # get the next frame content

                    if from_tp == 0: # the first frame (time point =0)

                        #for from_idx, from_type in enumerate(type_[from_tp]) : # get the first frame cell properties

                        signal.append(from_cell_frame[from_cell.pixels])

                        signal.append(to_cell_frame[to_cell.pixels])

                        if feature_channel != None and channel== feature_channel.upper():

                            pixels.append(from_cell.pixels)
                            tp.append(from_tp)
                            uid.append(from_cell.uid)
                            position.append(from_cell.position)
                            area.append(from_cell.features["area"])

#                        for to_idx, to_type in enumerate(type_[to_tp]) : # get the second frame cells

                            pixels.append(to_cell.pixels)
                            tp.append(to_tp)
                            uid.append(to_cell.uid)
                            position.append(to_cell.position)
                            area.append(to_cell.features["area"])

                    elif from_tp != 0:

#                        for to_idx, to_type in enumerate(type_[to_tp]) :

                        signal.append(to_cell_frame[to_cell.pixels])

                        if feature_channel != None and channel== feature_channel.upper():

                            pixels.append(to_cell.pixels)
                            tp.append(to_tp)
                            uid.append(to_cell.uid)
                            position.append(to_cell.position)
                            area.append(to_cell.features["area"])

        # >>>>>>> SAVE EACH ROOT CELL (TRACK) SIGNAL / FEATURES

        intensities[class_].append(signal)

        if feature_channel != None and channel== feature_channel.upper():

            features[class_]["pixels"].append(pixels)
            features[class_]["time_points"].append(tp)
            features[class_]["uid"].append(uid)
            features[class_]["position"].append(position)
            features[class_]["area"].append(area)

#______________________________________________________________________________
#                    frame=content[from_tp]
#                    obj_signal=[]
#
#                    if channel== features_channel.upper():
#                        pixels=[]
#                        uid=[]
#                        position=[]
#                        area=[]
#
#
#                    for from_idx, obj in enumerate(type_[from_tp]) :
#
#                        obj_signal.append(frame[obj.pixels])
#
#                        if channel== features_channel.upper():
#                            pixels.append(obj.pixels)
#                            uid.append(obj.uid)
#                            position.append(obj.position)
#                            area.append(obj.features["area"])
#
#                    intensities[class_][channel].append(obj_signal)
#                    if channel== features_channel.upper():
#                        features[class_]["pixels"].append(pixels)
#                        features[class_]["uid"].append(uid)
#                        features[class_]["position"].append(position)
#                        features[class_]["area"].append(area)
#______________________________________________________________________________
    if feature_channel == None :
        return intensities
    else :
        return intensities, features


import sys

def search(pos_list, c):
    result = 0
    f, l = 1, max(pos_list)
    while f <= l:
        mid = (f + l)//2
        if check(pos_list, c, mid):
            f = mid + 1
            result = mid
        else:
            l = mid - 1
    return result

def check(pos_list, c, meter):
    result = 1
    last_pos = pos_list[0]
    for pos in pos_list:
        if pos - last_pos >= meter:
            result += 1
            last_pos = pos
    return result >= c

if __name__ == '__main__':
    n, c = map(int, sys.stdin.readline().split())
    pos_list = [int(sys.stdin.readline().rstrip()) for _ in range(n)]
    pos_list = sorted(pos_list)
    answer = search(pos_list, c)
    print(answer)


import os

GPU = 0
os.environ['CUDA_VISIBLE_DEVICES'] = f'{GPU}'
import sys

sys.path.append('../../')
from pycode.tinyflow import autodiff as ad
from pycode.tinyflow.get_result import get_result
from util import *

class DenseNet121():
    def __init__(self, num_step, batch_size, log_path, job_id):
        self.n_filter = 32  # growth rate
        self.image_channel = 3
        self.image_size = 224
        self.dropout_rate = 0.2
        self.num_step = num_step
        self.batch_size = batch_size
        self.log_path = log_path
        self.job_id = job_id
        self.executor_ctx = None
        self.n_class = None
        self.ad = ad
        self.top_control_queue = None
        self.top_message_queue = None

    def bottleneck_layer(self, inputs, in_filter, layer_name, executor_ctx=None):

        W1 = self.ad.Variable(layer_name + "_W1")
        W2 = self.ad.Variable(layer_name + "_W2")

        # 1*1 conv
        bn1 = self.ad.bn_forward_op(inputs, "NCHW", "pre_activation")
        act1 = self.ad.activation_forward_op(bn1, "NCHW", "relu")
        conv1 = self.ad.convolution_2d_forward_op(act1, W1, "NCHW", "SAME", 1, 1)
        drop1 = self.ad.dropout_forward_op(conv1, "NCHW", self.dropout_rate)

        # 3*3 conv
        bn2 = self.ad.bn_forward_op(drop1, "NCHW", "pre_activation")
        act2 = self.ad.activation_forward_op(bn2, "NCHW", "relu")
        conv2 = self.ad.convolution_2d_forward_op(act2, W2, "NCHW", "SAME", 1, 1)
        drop2 = self.ad.dropout_forward_op(conv2, "NCHW", self.dropout_rate)
        if executor_ctx is None:
            W1_val = (4 * self.n_filter, in_filter, 1, 1)  # kernel_size=1*1
            W2_val = (self.n_filter, 4 * self.n_filter, 3, 3)  # kernel_size=3*3
        else:
            W1_val = ndarray.array(np.random.normal(loc=0, scale=0.1, size=(4 * self.n_filter, in_filter, 1, 1)), executor_ctx)  # kernel_size=1*1
            W2_val = ndarray.array(np.random.normal(loc=0, scale=0.1, size=(self.n_filter, 4 * self.n_filter, 3, 3)), executor_ctx)  # kernel_size=3*3

        dict = {W1: W1_val, W2: W2_val}
        return drop2, dict, self.n_filter

    def dense_block(self, inputs, in_filter, nb_layers, block_name, executor_ctx=None):
        x1 = inputs
        dict1 = {}
        for i in range(nb_layers):
            x2, dict2, out_filter = self.bottleneck_layer(x1, in_filter, block_name + "_bottleneck" + str(i), executor_ctx)
            x1 = self.ad.concat_forward_op(x1, x2)
            in_filter = in_filter + out_filter
            dict1.update(dict2)
        return x1, dict1, in_filter

    def transition_layer(self, inputs, in_filter, layer_name, executor_ctx=None):

        W1 = self.ad.Variable(layer_name + "_W1")
        bn1 = self.ad.bn_forward_op(inputs, "NCHW", "pre_activation")
        act1 = self.ad.activation_forward_op(bn1, "NCHW", "relu")
        conv1 = self.ad.convolution_2d_forward_op(act1, W1, "NCHW", "SAME", 1, 1)
        drop1 = self.ad.dropout_forward_op(conv1, "NCHW", self.dropout_rate)
        pool0 = self.ad.pooling_2d_forward_op(drop1, "NCHW", "mean", 0, 0, 2, 2, 2, 2)  # stride=2   pool_size=2*2
        if executor_ctx is None:
            W1_val = (int(0.5 * in_filter), in_filter, 1, 1)  # kernel_size=1*1
        else:
            W1_val = ndarray.array(np.random.normal(loc=0, scale=0.1, size=(int(0.5 * in_filter), in_filter, 1, 1)), executor_ctx)  # kernel_size=1*1
        dict = {W1: W1_val}
        return pool0, dict, int(0.5 * in_filter)

    def get_predict_results(self, n_class, **kwargs):
        X = self.ad.Placeholder("X")
        y_ = self.ad.Placeholder("y_")
        W0 = self.ad.Variable("W0")
        W1 = self.ad.Variable("W1")
        b1 = self.ad.Variable("b1")

        conv0 = self.ad.convolution_2d_forward_op(X, W0, "NCHW", "SAME", 2, 2)  # stride=2
        pool0 = self.ad.pooling_2d_forward_op(conv0, "NCHW", "max", 1, 1, 2, 2, 3, 3)  # stride=2   pool_size=3*3

        dense_1, dict_1, out_filter1 = self.dense_block(inputs=pool0, in_filter=2 * self.n_filter, nb_layers=6, block_name="dense1")
        transition_1, dict_2, out_filter2 = self.transition_layer(inputs=dense_1, in_filter=out_filter1, layer_name="trans1")

        dense_2, dict_3, out_filter3 = self.dense_block(inputs=transition_1, in_filter=out_filter2, nb_layers=12, block_name="dense2")
        transition_2, dict_4, out_filter4 = self.transition_layer(inputs=dense_2, in_filter=out_filter3, layer_name="trans2")

        dense_3, dict_5, out_filter5 = self.dense_block(inputs=transition_2, in_filter=out_filter4, nb_layers=24, block_name="dense3")
        transition_3, dict_6, out_filter6 = self.transition_layer(inputs=dense_3, in_filter=out_filter5, layer_name="trans3")

        dense_4, dict_7, out_filter7 = self.dense_block(inputs=transition_3, in_filter=out_filter6, nb_layers=16, block_name="dense4")

        bn1 = self.ad.bn_forward_op(dense_4, "NCHW", "pre_activation")
        act1 = self.ad.activation_forward_op(bn1, "NCHW", "relu")
        pool1 = self.ad.pooling_2d_forward_op(act1, "NCHW", "mean", 0, 0, 1, 1, 7, 7)  # global_pool

        flat = self.ad.flatten_op(pool1)
        dense = self.ad.dense(flat, W1, b1)
        y = self.ad.fullyactivation_forward_op(dense, "NCHW", "softmax")

        loss = self.ad.crossEntropy_loss(y, y_)

        W0_val = (2 * self.n_filter, self.image_channel, 7, 7)  # n_filter   n_channel=3   kernel_size=7*7
        W1_val = (out_filter7, n_class)
        b1_val = (n_class,)

        executor = self.ad.Executor(loss, y, 0.001, None, None, log_path=self.log_path, **kwargs)

        feed_dict = {W0: W0_val, W1: W1_val, b1: b1_val}
        feed_dict.update(dict_1)
        feed_dict.update(dict_2)
        feed_dict.update(dict_3)
        feed_dict.update(dict_4)
        feed_dict.update(dict_5)
        feed_dict.update(dict_6)
        feed_dict.update(dict_7)

        feed_dict_mv = {}
        for key, value in feed_dict.items():
            m_key = executor.Variable_node_to_mv[key][0]
            m_val = value
            v_key = executor.Variable_node_to_mv[key][1]
            v_val = value
            feed_dict_mv.update({m_key: m_val, v_key: v_val})

        feed_dict.update(feed_dict_mv)
        X_val = (self.batch_size, self.image_channel, self.image_size, self.image_size)  # number = batch_size  channel = 3  image_size = 224*224
        y_val = (self.batch_size, 1000)  # n_class = 1000
        feed_dict[X] = X_val
        feed_dict[y_] = y_val
        executor.init_operator_latency(feed_dict_sample=feed_dict, **kwargs)
        return executor.predict_results

    def run(self, executor_ctx, top_control_queue, top_message_queue, n_class, X_val, y_val, **kwargs):
        self.n_class = n_class
        self.top_control_queue = top_control_queue
        self.top_message_queue = top_message_queue
        self.executor_ctx = executor_ctx
        X = self.ad.Placeholder("X")
        y_ = self.ad.Placeholder("y_")
        W0 = self.ad.Variable("W0")
        W1 = self.ad.Variable("W1")
        b1 = self.ad.Variable("b1")

        conv0 = self.ad.convolution_2d_forward_op(X, W0, "NCHW", "SAME", 2, 2)  # stride=2
        pool0 = self.ad.pooling_2d_forward_op(conv0, "NCHW", "max", 1, 1, 2, 2, 3, 3)  # stride=2   pool_size=3*3

        dense_1, dict_1, out_filter1 = self.dense_block(inputs=pool0, in_filter=2 * self.n_filter, nb_layers=6, block_name="dense1", executor_ctx=executor_ctx)
        transition_1, dict_2, out_filter2 = self.transition_layer(inputs=dense_1, in_filter=out_filter1, layer_name="trans1", executor_ctx=executor_ctx)

        dense_2, dict_3, out_filter3 = self.dense_block(inputs=transition_1, in_filter=out_filter2, nb_layers=12, block_name="dense2", executor_ctx=executor_ctx)
        transition_2, dict_4, out_filter4 = self.transition_layer(inputs=dense_2, in_filter=out_filter3, layer_name="trans2", executor_ctx=executor_ctx)

        dense_3, dict_5, out_filter5 = self.dense_block(inputs=transition_2, in_filter=out_filter4, nb_layers=24, block_name="dense3", executor_ctx=executor_ctx)
        transition_3, dict_6, out_filter6 = self.transition_layer(inputs=dense_3, in_filter=out_filter5, layer_name="trans3", executor_ctx=executor_ctx)

        dense_4, dict_7, out_filter7 = self.dense_block(inputs=transition_3, in_filter=out_filter6, nb_layers=16, block_name="dense4", executor_ctx=executor_ctx)

        bn1 = self.ad.bn_forward_op(dense_4, "NCHW", "pre_activation")
        act1 = self.ad.activation_forward_op(bn1, "NCHW", "relu")
        pool1 = self.ad.pooling_2d_forward_op(act1, "NCHW", "mean", 0, 0, 1, 1, 7, 7)  # global_pool

        flat = self.ad.flatten_op(pool1)
        dense = self.ad.dense(flat, W1, b1)
        y = self.ad.fullyactivation_forward_op(dense, "NCHW", "softmax")

        loss = self.ad.crossEntropy_loss(y, y_)

        W0_val = ndarray.array(np.random.normal(loc=0, scale=0.1, size=(2 * self.n_filter, self.image_channel, 7, 7)), executor_ctx)  # n_filter   n_channel=3   kernel_size=7*7
        W1_val = ndarray.array(np.random.normal(loc=0, scale=0.1, size=(out_filter7, n_class)), executor_ctx)
        b1_val = ndarray.array(np.random.normal(loc=0, scale=0.1, size=(n_class)), executor_ctx)

        executor = self.ad.Executor(loss, y, 0.001, top_control_queue=top_control_queue,
                                    top_message_queue=top_message_queue, log_path=self.log_path)

        feed_dict = {W0: W0_val, W1: W1_val, b1: b1_val}
        feed_dict.update(dict_1)
        feed_dict.update(dict_2)
        feed_dict.update(dict_3)
        feed_dict.update(dict_4)
        feed_dict.update(dict_5)
        feed_dict.update(dict_6)
        feed_dict.update(dict_7)

        feed_dict_mv = {}
        for key, value in feed_dict.items():
            m_key = executor.Variable_node_to_mv[key][0]
            m_val = ndarray.array(np.zeros(shape=value.shape), executor_ctx)
            v_key = executor.Variable_node_to_mv[key][1]
            v_val = ndarray.array(np.zeros(shape=value.shape), executor_ctx)
            feed_dict_mv.update({m_key: m_val, v_key: v_val})

        feed_dict.update(feed_dict_mv)
        if 'predict_results' in kwargs.keys():
            executor.predict_results = kwargs['predict_results']
        else:
            X_val = np.random.normal(loc=0, scale=0.1, size=(
                self.batch_size, self.image_channel, self.image_size, self.image_size))  # number = batch_size  channel = 3  image_size = 224*224
            y_val = np.random.normal(loc=0, scale=0.1, size=(self.batch_size, 1000))  # n_class = 1000
            feed_dict[X] = ndarray.array(X_val, ctx=executor_ctx)
            feed_dict[y_] = ndarray.array(y_val, ctx=executor_ctx)
            executor.init_operator_latency(feed_dict_sample=feed_dict, **kwargs)
        gpu_record_cold_start = GPURecord(self.log_path, suffix='_cold_start')
        gpu_record = GPURecord(self.log_path)
        if self.job_id == 0:
            if 'schedule' in self.log_path:
                f1 = open(f"{self.log_path}/gpu_time.txt", "w+")
            f1_cold_start = open(f"{self.log_path}/gpu_time_cold_start.txt", "w+")
        start_record = -1
        start_cold_start_record = False
        already_start_record = False
        already_start_cold_start_record = False
        cold_start_i = 0
        start_i = 0
        for i in range(self.num_step):
            print("step", i)
            if self.job_id == 0:
                if i == 0:
                    if 'vanilla' in self.log_path:
                        gpu_record_cold_start.start()
                        start_time_cold_start = time.time()
                        cold_start_i = i
                        print(f'vanilla start_record at: {i}')
                if not already_start_record:
                    if start_cold_start_record and not already_start_cold_start_record:
                        gpu_record_cold_start.start()
                        start_time_cold_start = time.time()
                        cold_start_i = i
                        already_start_cold_start_record = True
                        print(f'cold_start start_record at: {i}')
                    if i == start_record:
                        gpu_record.start()
                        start_time = time.time()
                        start_i = i
                        already_start_record = True
                        print(f'start_record at: {i}')
                    if self.ad.have_got_control_message == 2 and start_record == -1:
                        print('got control message')
                        start_record = i + 1
                    if self.ad.have_got_control_message == 1:
                        start_cold_start_record = True
            feed_dict[X] = ndarray.array(X_val, ctx=self.executor_ctx)
            feed_dict[y_] = ndarray.array(y_val, ctx=self.executor_ctx)
            res = executor.run(feed_dict=feed_dict)
            loss_val = res[0]
            feed_dict = res[1]
        if self.job_id == 0:
            gpu_record_cold_start.stop()
            gpu_record.stop()
            end_time = time.time()
            if 'schedule' in self.log_path:
                f1.write(f'time_cost:{(end_time - start_time) / (self.num_step - start_i)}')
                f1.flush()
                f1.close()
            f1_cold_start.write(f'time_cost:{(end_time - start_time_cold_start) / (self.num_step - cold_start_i)}')
            f1_cold_start.flush()
            f1_cold_start.close()
        print(loss_val)

        print("success")
        if not self.top_message_queue.empty():
            self.top_message_queue.get()
        if not self.top_control_queue.empty():
            self.top_control_queue.get()
        self.top_message_queue.close()
        self.top_control_queue.close()
        self.top_control_queue.join_thread()
        self.top_message_queue.join_thread()
        return 0

    # def run(self, executor_ctx, top_control_queue, top_message_queue, n_class, X_val, y_val, **kwargs):
    #     self.init_model(executor_ctx, n_class, top_control_queue, top_message_queue, **kwargs)
    #     return self.run_without_init(X_val, y_val)

def run_exp(workloads, analysis_result=True, skip=None, **kwargs):
    for path, repeat, jobs_num, batch_size in workloads:
        raw_path = path
        for i in range(2):
            if i == 0 and skip != 'schedule':
                path = raw_path + 'schedule'
                print(path)
                main(path, repeat, jobs_num, batch_size, DenseNet121, **kwargs)
            elif skip != 'vanilla':
                path = raw_path + 'vanilla'
                print(path)
                main(path, repeat, jobs_num, batch_size, DenseNet121, **kwargs)
        if analysis_result:
            get_result(raw_path, repeat)

if __name__ == '__main__':
    run_exp([['./log/Inception V3 bs4/', 3, 1, 4]])


import itertools
import json
import math

def get_formatter(test_type):
    if test_type == "iozone" or test_type == "fio":
        return format_io_stat
    elif test_type == "pgbench":
        return format_pgbench_stat
    else:
        raise Exception("Cannot get formatter for type %s" % test_type)

def format_io_stat(res):
    if len(res) != 0:
        bw_mean = 0.0
        for measurement in res:
            bw_mean += measurement["bw_mean"]

        bw_mean /= len(res)

        it = ((bw_mean - measurement["bw_mean"]) ** 2 for measurement in res)
        bw_dev = sum(it) ** 0.5

        meta = res[0]['__meta__']

        sync = meta['sync']
        direct = meta['direct_io']

        if sync and direct:
            ss = "d+"
        elif sync:
            ss = "s"
        elif direct:
            ss = "d"
        else:
            ss = "a"

        key = "{0} {1} {2} {3}k".format(meta['action'], ss,
                                        meta['concurence'],
                                        meta['blocksize'])

        data = json.dumps({key: (int(bw_mean), int(bw_dev))})

        return data

def format_pgbench_stat(res):
    """
    Receives results in format:
    "<num_clients> <num_transactions>: <tps>
     <num_clients> <num_transactions>: <tps>
     ....
    "
    """
    if res:
        data = {}
        grouped_res = itertools.groupby(res, lambda x: x[0])
        for key, group in grouped_res:
            results = list(group)
            sum_res = sum([r[1] for r in results])
            mean = sum_res/len(results)
            sum_sq = sum([(r[1] - mean) ** 2 for r in results])
            if len(results) > 1:
                dev = math.sqrt(sum_sq / (len(results) - 1))
            else:
                dev = 0
            data[key] = (mean, dev)
        return data


# -*- coding: utf-8 -*-
import unittest
import HTMLTestRunner
import time, os
from Parameter import *
import coverage
from appium import webdriver
from appium.webdriver.common.touch_action import TouchAction
from selenium.common.exceptions import NoSuchElementException

# source指定的是待測程式所在資料夾名稱
# cov = coverage.coverage(config_file=True)
cov = coverage.coverage()
cov.start()

#檢查app是否安裝,若沒安裝則會自行安裝(Parameter)
check_app_installed()

#讀取testcase路徑
if (desired_caps['platformName'] == 'Android'):
    testcase_path = ".//testcase/Android"
else:
    testcase_path = ".//testcase/ios"

def creat_suite():
    uit = unittest.TestSuite()
    #discover = unittest.defaultTestLoader.discover(testcase_path, pattern="test_*.py")
    #discover = unittest.defaultTestLoader.discover(specific_testcase_path, pattern="test_*.py")
    discover = unittest.defaultTestLoader.discover(testcase_path, pattern="test_3_4*.py")

    for test_suite in discover:
        print(test_suite)
        for test_case in test_suite:
            uit.addTest(test_case)
    return uit

now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime())

if (desired_caps['platformName'] == 'Android'):
    if not os.path.exists('report/Android'):  os.makedirs('report/Android')
    reports_address = "report/Android/"
    report_path = "report/Android/" + now + ".html"
else:
    if not os.path.exists('report/ios'):  os.makedirs('report/ios')
    reports_address = "report/ios/"
    report_path = "report/ios/" + now + ".html"

suite = creat_suite()
file_results = open(report_path, "wb")
enviroments = u"  執行環境:"+OS+" "+"裝置:"+desired_caps['deviceName']+" "+"版本:"+desired_caps['platformName']+" "+desired_caps['platformVersion']
runner = HTMLTestRunner.HTMLTestRunner(stream=file_results, title=u"72app",description=enviroments, verbosity=2)
# verbosity参数可以控制执行结果的输出，0 是简单报告、1 是一般报告、2 是详细报告。
runner.run(suite)
file_results.close()


# -*- coding: utf-8 -*-
"""
Created on Mon Feb 25 15:04:43 2019

@author:

To represent mini-batch method
"""

import sys,os
sys.path.append(os.pardir)
from dataset.mnist import load_mnist
import numpy as np
from common.functions import softmax, cross_entropy_error

(x_train,y_train),(x_test,y_test) = \
     load_mnist(normalize=True, one_hot_label=True)

batch_size = 10
train_size = x_train.shape[0]
batch_mask = np.random.choice(train_size,batch_size)
x_batch = x_train[batch_mask]
y_batch = y_train[batch_mask]

class simpleNet:
    def __init__(self):
        self.w = np.random.randn(2,3)

    def predict(self,x):
        return np.dot(x,self.w)

    def loss(self,x,t):
        z=self.predict(self.w,x)
        y=softmax(z)
        loss=cross_entropy_error(y,t)

        return loss

net=simpleNet()
print(net.w)


from flask import Flask, request
import os
import librosa
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler

app = Flask(__name__)

@app.route('/', methods = ['GET', 'POST'])
def hello():
    filelist = os.listdir('static')
    train_df = pd.DataFrame(filelist)
    train_df = train_df.rename(columns={0:'file'})
    train_df = train_df.sample(frac=1).reset_index(drop=True)
    speaker = []
    for i in range(0, len(train_df)):
        speaker.append(train_df['file'][i][0:-5])
    train_df['speaker'] = speaker
    train_features = train_df.apply(extract_features, axis=1)
    features_test = []
    for i in range(0, len(train_features)):
        features_test.append(np.concatenate((train_features[i][0], train_features[i][1],
                    train_features[i][2], train_features[i][3],
                    train_features[i][4]), axis=0))
    X_test = np.array(features_test)
    print(X_test)
    ss = StandardScaler()
    X_test = ss.fit_transform(X_test)
    return print(X_test)

def extract_features(files):
    file_name = os.path.join(os.path.abspath('static')+'/'+str(files.file))
    X, sample_rate = librosa.load(file_name, res_type='kaiser_fast')
    mfccs = np.mean(librosa.feature.mfcc(y=X, sr=sample_rate, n_mfcc=40).T,axis=0)
    stft = np.abs(librosa.stft(X))
    chroma = np.mean(librosa.feature.chroma_stft(S=stft, sr=sample_rate).T,axis=0)
    mel = np.mean(librosa.feature.melspectrogram(X, sr=sample_rate).T,axis=0)
    contrast = np.mean(librosa.feature.spectral_contrast(S=stft, sr=sample_rate).T,axis=0)
    tonnetz = np.mean(librosa.feature.tonnetz(y=librosa.effects.harmonic(X),
    sr=sample_rate).T,axis=0)
    return mfccs, chroma, mel, contrast, tonnetz


import time
import math
import sys
from os import path, listdir
from os.path import exists, isfile, join, splitext
import re
import logging
import pickle

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

from polylidar import extractPlanesAndPolygons, extract_planes_and_polygons_from_mesh, extract_point_cloud_from_float_depth, extract_uniform_mesh_from_float_depth
from polylidarutil import (plot_polygons_3d, generate_3d_plane, set_axes_equal, plot_planes_3d,
                           scale_points, rotation_matrix, apply_rotation, COLOR_PALETTE)
from polylidarutil.open3d_util import construct_grid, create_lines, flatten
from polylidarutil.plane_filtering import filter_planes_and_holes

from scipy.spatial import Delaunay
from scipy.stats import describe
import open3d as o3d

DIR_NAME = path.dirname(__file__)
FIXTURES_DIR = path.join(DIR_NAME, '../../tests', 'fixtures')
REALSENSE_DIR = path.join(FIXTURES_DIR, 'realsense')
REALSENSE_COLOR_DIR = path.join(REALSENSE_DIR, 'color')
REALSENSE_DEPTH_DIR = path.join(REALSENSE_DIR, 'depth')
REALSENSE_SCENE_DIR = path.join(REALSENSE_DIR, 'scene')
REALSENSE_TRAJECTORY = path.join(REALSENSE_SCENE_DIR, 'trajectory.log')
REALSENSE_INTRINSICS = path.join(
    REALSENSE_DIR, 'camera_intrinsic_rgb_424.json')

# Conversion between D400 to T265
H_t265_d400 = np.array([
    [1, 0, 0, 0],
    [0, -1.0, 0, 0],
    [0, 0, -1.0, 0],
    [0, 0, 0, 1]])

# Conversion from D400 frame to "Standard" frame (z-axis= to [0,0,1])
R_Standard_d400 = np.array([
    [1, 0, 0, 0],
    [0, 0.0, 1.0, 0],
    [0, -1.0, 0, 0],
    [0, 0, 0, 1]])

logging.basicConfig(level=logging.INFO)

### Read Saved Data from Realsense Directory ###
# Some of this code is pulled from Open3D to read saved RGBD Files
def sorted_alphanum(file_list_ordered):
    def convert(text): return int(text) if text.isdigit() else text
    def alphanum_key(key): return [convert(c)
                                   for c in re.split('([0-9]+)', key)]
    return sorted(file_list_ordered, key=alphanum_key)

def get_file_list(path_, extension=None):
    if extension is None:
        file_list = [path.join(path_, f)
                     for f in listdir(path_) if isfile(join(path_, f))]
    else:
        file_list = [
            path.join(path_, f)
            for f in listdir(path_)
            if isfile(join(path_, f)) and splitext(f)[1] == extension
        ]
    file_list = sorted_alphanum(file_list)
    file_list = [path.abspath(file_) for file_ in file_list]
    return file_list

def get_rgbd_file_lists(path_color=REALSENSE_COLOR_DIR, path_depth=REALSENSE_DEPTH_DIR):
    color_files = get_file_list(path_color, ".jpg") + \
        get_file_list(path_color, ".png")
    depth_files = get_file_list(path_depth, ".png")
    return color_files, depth_files

def read_trajectory(filename):
    traj = []
    with open(filename, 'r') as f:
        metastr = f.readline()
        while metastr:
            mat = np.zeros(shape=(4, 4))
            for i in range(4):
                matstr = f.readline()
                mat[i, :] = np.fromstring(matstr, dtype=float, sep=' \t')
            traj.append(mat)
            metastr = f.readline()
    return traj

def convert_trajectory(traj, inv=True):
    new_traj = []
    for i in range(len(traj)):
        extrinsic_1 = np.linalg.inv(H_t265_d400) @ traj[i] @ H_t265_d400
        if inv:
            extrinsic_1 = np.linalg.inv(extrinsic_1)
        new_traj.append(extrinsic_1)
    return new_traj

def get_realsense_data(color_dir=REALSENSE_COLOR_DIR, depth_dir=REALSENSE_DEPTH_DIR,
                       trajectory_fpath=REALSENSE_TRAJECTORY, intrinsic_fpath=REALSENSE_INTRINSICS):
    color_files, depth_files = get_rgbd_file_lists(
        path_color=color_dir, path_depth=depth_dir)
    traj = convert_trajectory(read_trajectory(trajectory_fpath))
    intrinsics = o3d.io.read_pinhole_camera_intrinsic(intrinsic_fpath)
    return color_files, depth_files, traj, intrinsics

### End Read Saved Data from Realsense Directory ###

def create_open3d_pc(points):
    """ Creates an Open3D point cloud """
    pcd = o3d.geometry.PointCloud()
    pcd.points = o3d.utility.Vector3dVector(points)
    return pcd

def create_open_3d_mesh(triangles, points, color=COLOR_PALETTE[0]):
    """Create an Open3D Mesh given triangles vertices

    Arguments:
        triangles {ndarray} -- Triangles array
        points {ndarray} -- Points array

    Keyword Arguments:
        color {list} -- RGB COlor (default: {[1, 0, 0]})

    Returns:
        mesh -- Open3D Mesh
    """
    mesh_2d = o3d.geometry.TriangleMesh()
    if triangles.ndim == 1:
        triangles = triangles.reshape((int(triangles.shape[0] / 3), 3))
        # Open 3D expects triangles to be counter clockwise
        triangles = np.ascontiguousarray(np.flip(triangles, 1))
    mesh_2d.triangles = o3d.utility.Vector3iVector(triangles)
    mesh_2d.vertices = o3d.utility.Vector3dVector(points)
    mesh_2d.compute_vertex_normals()
    mesh_2d.compute_triangle_normals()
    mesh_2d.paint_uniform_color(color)
    return mesh_2d

def extract_mesh_planes(points, triangles, planes, color=None):
    " Converts Polylidar Mesh Planes into Open3D format "

    meshes = []
    color_ = color
    for i, plane in enumerate(planes):
        if color is None:
            color_ = COLOR_PALETTE[i % (len(COLOR_PALETTE) - 1)]
        else:
            color_ = COLOR_PALETTE[0]
        tris = np.ascontiguousarray(np.flip(triangles[plane, :], 1))
        mesh = create_open_3d_mesh(tris, points, color_)
        meshes.append(mesh)
    return meshes

def get_frame_data(idx, color_files, depth_files, traj, intrinsic, depth_trunc=3.0, stride=2):
    """Gets Frame Data

    Arguments:
        idx {int} -- Index of frame
        color_files {list} -- list of color images
        depth_files {list} -- list of depth images
        traj {list} -- list of extrinsic matrices corresponding to frames
        intrinsic {Open3D intrisics} -- Open3D intrinsics array

    Keyword Arguments:
        depth_trunc {float} -- How much to truncate depth image in meters (default: {3.0})
        stride {int} -- stride for downsampling pont cloud (default: {2})

    Returns:
        tuple -- PointCloud, RGBD Image, extrinsics (D4XX Frame -> Global Standard Frame)
    """
    depth_1 = o3d.io.read_image(depth_files[idx])
    color_1 = o3d.io.read_image(color_files[idx])

    extrinsic = traj[idx]
    rgbd_image_1 = o3d.geometry.RGBDImage.create_from_color_and_depth(
        color_1, depth_1, convert_rgb_to_intensity=False, depth_trunc=depth_trunc)

    pcd_1 = o3d.geometry.PointCloud.create_from_depth_image(
        depth_1, intrinsic, extrinsic, stride=stride, depth_trunc=depth_trunc)
    return pcd_1, rgbd_image_1, R_Standard_d400 @ np.linalg.inv(extrinsic)

def prep_mesh(mesh):
    " Prepares mesh for visualization "
    mesh_list = mesh
    if not isinstance(mesh_list, list):
        mesh_list = [mesh]
    for mesh_ in mesh_list:
        mesh_.compute_triangle_normals()
        mesh_.paint_uniform_color(COLOR_PALETTE[0])

def filter_and_create_open3d_polygons(points, polygons):
    " Apply polygon filtering algorithm, return Open3D Mesh Lines "
    config_pp = dict(filter=dict(hole_area=dict(min=0.025, max=0.785), hole_vertices=dict(min=6), plane_area=dict(min=0.5)),
                     positive_buffer=0.01, negative_buffer=0.03, simplify=0.02)
    planes, obstacles = filter_planes_and_holes(polygons, points, config_pp)
    all_poly_lines = create_lines(planes, obstacles, line_radius=0.01)
    return all_poly_lines

def run_test(pcd, rgbd, intrinsics, extrinsics, bp_alg=dict(radii=[0.02, 0.02]), poisson=dict(depth=8), callback=None, stride=2):
    points = np.asarray(pcd.points)
    # Create Pseudo 3D Surface Mesh using Delaunay Triangulation and Polylidar
    polylidar_kwargs = dict(alpha=0.0, lmax=0.10, minTriangles=100,
                            zThresh=0.03, normThresh=0.99, normThreshMin=0.95, minHoleVertices=6)
    t1 = time.perf_counter()
    delaunay, planes, polygons = extractPlanesAndPolygons(points, **polylidar_kwargs)
    t2 = time.perf_counter()
    all_poly_lines = filter_and_create_open3d_polygons(points, polygons)
    triangles = np.asarray(delaunay.triangles).reshape(int(len(delaunay.triangles) / 3), 3)
    mesh_2d_polylidar = extract_mesh_planes(points, triangles, planes, COLOR_PALETTE[0])
    mesh_2d_polylidar.extend(flatten([line_mesh.cylinder_segments for line_mesh in all_poly_lines]))
    time_mesh_2d_polylidar = (t2 - t1) * 1000
    polylidar_alg_name = 'Polylidar2D'
    callback(polylidar_alg_name, time_mesh_2d_polylidar, pcd, mesh_2d_polylidar)
    # Uniform Mesh Grid
    polylidar_inputs, timings = make_uniform_grid_mesh(np.asarray(
        rgbd.depth), np.ascontiguousarray(intrinsics.intrinsic_matrix), extrinsics, stride=stride)
    mesh_uniform_grid = create_open_3d_mesh(polylidar_inputs['triangles'], polylidar_inputs['vertices'])
    time_mesh_uniform = timings['mesh_creation']
    uniform_alg_name = 'Uniform Grid Mesh'
    callback(uniform_alg_name, time_mesh_uniform, pcd, mesh_uniform_grid)
    # Polylidar3D with Uniform Mesh Grid
    # pickle.dump(polylidar_inputs, open('realsense_mesh.pkl', 'wb'))
    vertices = polylidar_inputs['vertices']
    triangles = polylidar_inputs['triangles']
    halfedges = polylidar_inputs['halfedges']
    t1 = time.perf_counter()
    planes, polygons = extract_planes_and_polygons_from_mesh(vertices, triangles, halfedges, **polylidar_kwargs)
    t2 = time.perf_counter()
    all_poly_lines = filter_and_create_open3d_polygons(vertices, polygons)
    triangles = triangles.reshape(int(triangles.shape[0] / 3), 3)
    mesh_3d_polylidar = extract_mesh_planes(vertices, triangles, planes)
    mesh_3d_polylidar.extend(flatten([line_mesh.cylinder_segments for line_mesh in all_poly_lines]))
    time_polylidar3D = (t2 - t1) * 1000
    polylidar_3d_alg_name = 'Polylidar with Uniform Grid Mesh'
    callback(polylidar_3d_alg_name, time_polylidar3D,
             create_open3d_pc(vertices), mesh_3d_polylidar)

    # Estimate Point Cloud Normals
    t3 = time.perf_counter()
    pcd.estimate_normals(
        search_param=o3d.geometry.KDTreeSearchParamHybrid(radius=0.10, max_nn=20))
    t4 = time.perf_counter()
    time_estimate_point_normals = (t4 - t3) * 1000
    point_normal_alg_name = 'Point Normal Estimation'
    callback(point_normal_alg_name, time_estimate_point_normals, pcd, None)
    # Create True 3D Surface Mesh using Ball Pivot Algorithm
    radii = o3d.utility.DoubleVector(bp_alg['radii'])
    t5 = time.perf_counter()
    mesh_ball_pivot = o3d.geometry.TriangleMesh.create_from_point_cloud_ball_pivoting(
        pcd, radii)
    prep_mesh(mesh_ball_pivot)
    t6 = time.perf_counter()
    time_mesh_ball_pivot = (t6 - t5) * 1000
    ball_point_alg_name = 'Ball Pivot'
    callback(ball_point_alg_name, time_mesh_ball_pivot, pcd, mesh_ball_pivot)
    # Create True 3D Surface Mesh using Poisson Reconstruction Algorithm
    t7 = time.perf_counter()
    mesh_poisson, densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
        pcd, **poisson)
    vertices_to_remove = densities < np.quantile(densities, 0.1)
    mesh_poisson.remove_vertices_by_mask(vertices_to_remove)
    t8 = time.perf_counter()
    prep_mesh(mesh_poisson)
    time_mesh_poisson = (t8 - t7) * 1000
    poisson_alg_name = 'Poisson'
    callback(poisson_alg_name, time_mesh_poisson, pcd, mesh_poisson)

    results = [
        dict(alg=polylidar_alg_name, mesh=mesh_2d_polylidar,
             execution_time=time_mesh_2d_polylidar),
        dict(alg=point_normal_alg_name, mesh=None,
             execution_time=time_estimate_point_normals),
        dict(alg=ball_point_alg_name, mesh=mesh_ball_pivot,
             execution_time=time_mesh_ball_pivot),
        dict(alg=poisson_alg_name, mesh=mesh_poisson,
             execution_time=time_mesh_poisson)
    ]
    return results

def make_uniform_grid_mesh(im, intrinsics, extrinsics, stride=2, **kwargs):
    """Create a Unifrom Grid Mesh from an RGBD Image

    Arguments:
        img {ndarray} -- MXN Float Depth Image
        intrinsics {ndarray} -- 3X3 intrinsics matrix
        extrinsics {ndarray} -- 4X4 matrix

    Keyword Arguments:
        stride {int} -- Stride for creating point cloud (default: {2})

    Returns:
        tuple(dict, dict) - Mesh and timings
    """
    t0 = time.perf_counter()
    points, triangles, halfedges = extract_uniform_mesh_from_float_depth(im, intrinsics, stride=stride)
    t1 = time.perf_counter()
    points = np.asarray(points)
    triangles = np.asarray(triangles)
    halfedges = np.asarray(halfedges)
    points = points.reshape((int(points.shape[0] / 3), 3))
    # Rotate Point Cloud
    points = np.column_stack((points, np.ones(points.shape[0])))
    points = np.ascontiguousarray(((extrinsics @ points.T).T)[:, :3])
    t2 = time.perf_counter()
    polylidar_inputs = dict(
        vertices=points, triangles=triangles, halfedges=halfedges)
    timings = dict(mesh_creation=(t1 - t0) * 1000, pc_rotation=(t2 - t1) * 1000)
    return polylidar_inputs, timings

def callback(alg_name, execution_time, pcd, mesh=None):
    axis_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=0.2)
    axis_frame.translate([0, 0.8, -0.7])
    grid_ls = construct_grid(size=2, n=20, plane_offset=-0.8, translate=[0, 1.0, 0.0])
    logging.info("%s took %.2f milliseconds", alg_name, execution_time)
    if mesh:
        if isinstance(mesh, list):
            o3d.visualization.draw_geometries(
                [*mesh, pcd, grid_ls, axis_frame])
        else:
            o3d.visualization.draw_geometries([mesh, pcd, grid_ls, axis_frame])

def main():
    color_files, depth_files, traj, intrinsics = get_realsense_data()
    axis_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=0.2)
    axis_frame.translate([0, 0.8, -0.7])
    grid_ls = construct_grid(size=2, n=20, plane_offset=-0.8, translate=[0, 1.0, 0.0])
    for idx in range(len(color_files)):
        if idx < 3:
            continue
        pcd, rgbd, extrinsics = get_frame_data(idx, color_files, depth_files, traj, intrinsics, stride=2)
        pcd = pcd.rotate(R_Standard_d400[:3, :3], center=False)

        logging.info("File %r - Point Cloud; Size: %r", idx, np.asarray(pcd.points).shape[0])
        o3d.visualization.draw_geometries([pcd, grid_ls, axis_frame])
        results = run_test(pcd, rgbd, intrinsics, extrinsics, callback=callback, stride=2)

if __name__ == "__main__":
    main()


#!/usr/bin/env python3
# -*- coding:utf-8 -*-
"""
二叉树类
"""

class TreeNode:
    """
    树的结点类
    """
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None

class Tree:
    """
    树类
    """
    def __init__(self):
        """
        写死了一棵树， 用于测试
        """
        self.root = TreeNode(1)
        self.root.left = TreeNode(2)
        self.root.left.left = TreeNode(4)
        self.root.left.left.right = TreeNode(5)
        self.root.left.left.left = TreeNode(9)
        self.root.right = TreeNode(3)
        self.root.right.left = TreeNode(6)
        self.root.right.left.right = TreeNode(7)
        self.root.right.right = TreeNode(8)

    def lrd(self):
        """
        非递归后序遍历
        (9 5 4 2 7 6 8 3 1)
        """
        tag = list()
        stack = list()
        stack.append(self.root)
        tag.append(0)
        p = None
        while len(stack) != 0:
            if p is None:
                p = stack.pop()
                p_tag = tag.pop() + 1
            if p_tag == 1:
                stack.append(p)
                tag.append(p_tag)
                p = p.left
                p_tag = 1
            elif p_tag == 2:
                stack.append(p)
                tag.append(p_tag)
                p = p.right
                p_tag = 1
            else:
                # 3次
                print(p.val, end=' ')
                p = None

    def dlr(self):
        """
        非递归先序遍历
        (1 2 4 9 5 3 6 7 8)
        """
        stack = []
        stack.append(self.root)
        while len(stack) != 0:
            p = stack.pop()
            print(p.val, end=' ')
            if p.right is not None:
                stack.append(p.right)
            if p.left is not None:
                stack.append(p.left)

    def ldr(self):
        """
        非递归中序遍历
        (9 4 5 2 1 6 7 3 8)
        """
        stack = []
        p = self.root
        while len(stack) != 0 or p is not None:
            if p is not None:
                stack.append(p)
                p = p.left
            else:
                p = stack.pop()
                print(p.val, end=' ')
                p = p.right

    def high(self):
        """
        计算树的高度
        :return: 树的高度
        """
        return self.__cal_high(self.root, 1, 1)

    def __cal_high(self, root_now, high_now, high_max):
        if root_now is None:
            return max(high_now-1, high_max)
        else:
            high_left = self.__cal_high(root_now.left, high_now+1, high_max)
            high_right = self.__cal_high(root_now.right, high_now+1, high_max)
            return max(high_left, high_right)

if __name__ == '__main__':
    t = Tree()
    t.lrd()
    print()
    t.dlr()
    print()
    t.ldr()
    print()
    print(t.high())