diff --git a/examples/data_tests/generate_astec_lineage_dist.py b/examples/data_tests/generate_astec_lineage_dist.py
index f4aa04d3d9482dcfe27106394b732c1145655458..4f885ba8cb17ef8f37afd3c849b1bd97816d8d88 100644
--- a/examples/data_tests/generate_astec_lineage_dist.py
+++ b/examples/data_tests/generate_astec_lineage_dist.py
@@ -2,21 +2,15 @@ import os
 from morphonet.data import get_local_dataset,list_local_datasets
 from morphonet.data.dataproperty import DataProperty
 import xml.etree.ElementTree as ET
-from morphonet.data.utils import get_object_id,indent_xml,generate_init_naming_parameters,remove_folder,generate_prop_naming_parameters,load_properties_from_xml,get_object_t_id_ch,get_node
+import pickle as pkl
+
+from libs.lineage_distance import generate_lineage_comparison
+from morphonet.data.utils import get_object_id,indent_xml,generate_init_naming_parameters,remove_folder,generate_prop_naming_parameters,load_properties_from_xml,get_object_t_id_ch,get_node,get_id_t
 import argparse
 from pathlib import Path
-import edist.ted
-import edist.uted as uted
 import time
-def get_symetric_cells(property,name):
-    #print(name)
-    for cell_key in property.keys():
-        sname = property[cell_key]
-        if sname is not None:
-            if sname!=name and sname[0:-1] == name[0:-1]:
-                #print("found symetric "+str(sname))
-                return cell_key
-    return None
+
+
 
 parser = argparse.ArgumentParser()
 
@@ -41,94 +35,17 @@ if dataset is None :
     for dataset in datasets_available:
         print(dataset)
     exit()
+min_time = dataset.get_min_time()
+output_xml = os.path.join(seg_folder_name,"output.xml")
 
-property_object = DataProperty(dataset, "astec_lineage_distance", "", "float")
-treated_cells_names = {}
-print("Loading needed properties")
-property_name = dataset.get_property("cell_name")
-cell_lineage = dataset.get_property("cell_lineage")
-if cell_lineage is None :
-    cell_lineage = dataset.get_property("temporal")
-dict_name_by_t = {}
-dict_cells_by_name = {}
-dict_mincell_id_by_name = {}
-if property_name is not None and cell_lineage is not None:
-    def local_cost_normalized(x, y):
-        if x is None and y is None:
-            return 0
-        elif x is None or y is None:
-            return 1
-        elif x not in x_nodes or y not in y_nodes:
-            return 1
-        xl = x_life[x_nodes.index(x)]
-        yl = y_life[y_nodes.index(y)]
-        return abs(xl - yl) / (xl + yl)
-
-
-    # LINEAGE COMPARISON
-    # if not cell_id in existing_regions:
-    # existing_regions[cell_id] = {}
-    # Split the property into subs dict
-    print("Splitting names by time point")
-    for cell_key in property_name.values:
-        tc, idc, chanc = get_object_t_id_ch(cell_key)
-        name = property_name.values[cell_key]
-        if not name in dict_cells_by_name:
-            dict_cells_by_name[name] = []
-        dict_cells_by_name[name].append(cell_key)
-        if not int(tc) in dict_name_by_t:
-            dict_name_by_t[int(tc)] = {}
-        dict_name_by_t[int(tc)][cell_key] = name
-        if not name in dict_mincell_id_by_name:
-            dict_mincell_id_by_name[name] = cell_key
-        else:
-            tp,idcp,chanp = get_object_t_id_ch(dict_mincell_id_by_name[name])
-            if int(tc) < int(tp):
-                dict_mincell_id_by_name[name] = cell_key
-    print(dict_mincell_id_by_name)
-    for cell_key in property_name.get_keys():
-        name = property_name.values[cell_key]
-        # print("Trying to compute lineage dist for cell "+str(cell_key)+" with name : "+str(name))
-        if name is not None and name.endswith("*"):  # ONLY GET RIGHT SIDE
-            print("Working on cell : "+str(name))
-            # print("found cell on right side ")
-            # print("working time is "+str(time))
-            tc, idc, chanc = get_object_t_id_ch(cell_key)
-            mother_key = dict_mincell_id_by_name[name]
-            # print("found cell at time : "+str(tc)+" with id "+str(idc))
-            smo = name.replace('*','_')
-            if smo is not None:
-                # print("found symetric : "+str(smo))
-                # print("smo : "+str(smo))
-                # print("cell_key : "+str(cell_key))
-                ts = None
-                ids = None
-                chans = None
-                if smo in dict_mincell_id_by_name:
-                    for cell_f in dict_cells_by_name[smo]:
-                        tt, idt, chant = get_object_t_id_ch(cell_f)
-                        if ts is None or int(tt) < int(ts):
-                            ts = tt
-                            ids = idt
-                            chans = chant
-                    if mother_key is not None and not mother_key in treated_cells_names:
-                        x_nodes, x_adj, x_life = get_node(cell_lineage, mother_key)
-                        # print(x_nodes,x_adj,x_life)
-                        sym_key = get_object_id(ts,ids,chans)
-                        print(mother_key + " -> "+sym_key)
-                        y_nodes, y_adj, y_life = get_node(cell_lineage, sym_key)
-                        # print(y_nodes,y_adj,y_life)
-                        d = uted.uted(x_nodes, x_adj, y_nodes, y_adj, local_cost_normalized)
-                        # print(d)
-                        # exit()
-                        treated_cells_names[mother_key] = d
-                        property_object.set_object_value(tc, idc, seg_channel, d)
-                        property_object.set_object_value(ts, ids, seg_channel, d)
-                    else :
-                        property_object.set_object_value(tc, idc, seg_channel,treated_cells_names[mother_key])
-                        property_object.set_object_value(ts, ids, seg_channel, treated_cells_names[mother_key])
+generate_lineage_comparison(output_xml,min_time)
 
+if os.path.exists(output_xml):
+    print("-> importing lineage in MorphoNet dataset")
+    properties = load_properties_from_xml(output_xml)
     step = dataset.initialize_external_step(Path(__file__).stem)
-    dataset.write_property_to_step(step, property_object.name + ".txt", property_object)
-t2 = time.time()
-print("computed in : "+str(t2-t1)+" seconds")
\ No newline at end of file
+    for property_object in properties:
+        if property_object.name.startswith("float_symetric"):
+            dataset.write_property_to_step(step, property_object.name + ".txt", property_object)
+# DELETE ALL TEMP FILES
+os.system("rm -rf " + str(temp_file_path))
diff --git a/examples/data_tests/generate_lineage_names.py b/examples/data_tests/generate_lineage_names.py
index 8764e92fc35acaded145852f295caab72140aa31..2807f1de93da44b5f255caf869e26b1f4e2cbad7 100644
--- a/examples/data_tests/generate_lineage_names.py
+++ b/examples/data_tests/generate_lineage_names.py
@@ -5,8 +5,6 @@ import xml.etree.ElementTree as ET
 from morphonet.data.utils import indent_xml,generate_init_naming_parameters,remove_folder,generate_prop_naming_parameters,load_properties_from_xml,get_object_t_id_ch,get_symetric_cells,get_node
 import argparse
 from pathlib import Path
-import edist.ted
-import edist.uted as uted
 def compute_atlas():
     """
     Returns the list of files
@@ -135,59 +133,61 @@ print("Computing cell_neighbors using contact surface")
 property_object = DataProperty(dataset, "cell_neighbors", "", "space")
 if cell_contact_surface is not None:
     for id_cell in cell_contact_surface.get_keys():
+        neigh = []
         tc,idc,channelc = get_object_t_id_ch(id_cell)
-        for neigh in cell_contact_surface.values[id_cell]:
-            property_object.set_object_value(tc,idc,channelc,neigh[0])
+        if len(cell_contact_surface.values[id_cell]) > 0:
+            for neigh in cell_contact_surface.values[id_cell].keys():
+                property_object.set_object_value(tc,idc,channelc,neigh)
     dataset.write_property_to_step(step, property_object.name + ".txt", property_object)
 
 print("Computing lineage distance, this may be long ...")
-
-property_object = DataProperty(dataset, "astec_lineage_distance", "", "float")
-
-if property_name is not None and cell_lineage is not None:
-    def local_cost_normalized(x, y):
-        if x is None and y is None:
-            return 0
-        elif x is None or y is None:
-            return 1
-        elif x not in x_nodes or y not in y_nodes:
-            return 1
-        xl = x_life[x_nodes.index(x)]
-        yl = y_life[y_nodes.index(y)]
-        return abs(xl - yl) / (xl + yl)
-
-
-    # LINEAGE COMPARISON
-    # if not cell_id in existing_regions:
-    # existing_regions[cell_id] = {}
-
-    for cell_key in property_name.get_keys():
-        name = property_name.values[cell_key]
-        # print("Trying to compute lineage dist for cell "+str(cell_key)+" with name : "+str(name))
-        if name is not None and name.endswith("*"):  # ONLY GET RIGHT SIDE
-            # print("found cell on right side ")
-            # print("working time is "+str(time))
-            tc, idc, chanc = get_object_t_id_ch(cell_key)
-            # print("found cell at time : "+str(tc)+" with id "+str(idc))
-            smo = get_symetric_cells(tc, property_name, name)
-            # print("found symetric : "+str(smo))
-            # print("smo : "+str(smo))
-            # print("cell_key : "+str(cell_key))
-            if smo is not None:
-                ts, ids, chans = get_object_t_id_ch(smo)
-                x_nodes, x_adj, x_life = get_node(cell_lineage, cell_key)
-                # print(x_nodes,x_adj,x_life)
-                y_nodes, y_adj, y_life = get_node(cell_lineage, smo)
-                # print(y_nodes,y_adj,y_life)
-                d = uted.uted(x_nodes, x_adj, y_nodes, y_adj, local_cost_normalized)
-                # print(d)
-                # exit()
-                property_object.set_object_value(tc, idc, seg_channel, d)
-                property_object.set_object_value(ts, ids, seg_channel, d)
-
-    dataset.write_property_to_step(step, property_object.name + ".txt", property_object)
+if False:
+    property_object = DataProperty(dataset, "astec_lineage_distance", "", "float")
+
+    if property_name is not None and cell_lineage is not None:
+        def local_cost_normalized(x, y):
+            if x is None and y is None:
+                return 0
+            elif x is None or y is None:
+                return 1
+            elif x not in x_nodes or y not in y_nodes:
+                return 1
+            xl = x_life[x_nodes.index(x)]
+            yl = y_life[y_nodes.index(y)]
+            return abs(xl - yl) / (xl + yl)
+
+
+        # LINEAGE COMPARISON
+        # if not cell_id in existing_regions:
+        # existing_regions[cell_id] = {}
+
+        for cell_key in property_name.get_keys():
+            name = property_name.values[cell_key]
+            # print("Trying to compute lineage dist for cell "+str(cell_key)+" with name : "+str(name))
+            if name is not None and name.endswith("*"):  # ONLY GET RIGHT SIDE
+                # print("found cell on right side ")
+                # print("working time is "+str(time))
+                tc, idc, chanc = get_object_t_id_ch(cell_key)
+                # print("found cell at time : "+str(tc)+" with id "+str(idc))
+                smo = get_symetric_cells(tc, property_name, name)
+                # print("found symetric : "+str(smo))
+                # print("smo : "+str(smo))
+                # print("cell_key : "+str(cell_key))
+                if smo is not None:
+                    ts, ids, chans = get_object_t_id_ch(smo)
+                    x_nodes, x_adj, x_life = get_node(cell_lineage, cell_key)
+                    # print(x_nodes,x_adj,x_life)
+                    y_nodes, y_adj, y_life = get_node(cell_lineage, smo)
+                    # print(y_nodes,y_adj,y_life)
+                    d = uted.uted(x_nodes, x_adj, y_nodes, y_adj, local_cost_normalized)
+                    # print(d)
+                    # exit()
+                    property_object.set_object_value(tc, idc, seg_channel, d)
+                    property_object.set_object_value(ts, ids, seg_channel, d)
+
+        dataset.write_property_to_step(step, property_object.name + ".txt", property_object)
 
 
 
 # DELETE ALL TEMP FILES
-os.system("rm -rf " + str(temp_file_path))
\ No newline at end of file
+#os.system("rm -rf " + str(temp_file_path))
\ No newline at end of file
diff --git a/examples/data_tests/libs/lineage_distance.py b/examples/data_tests/libs/lineage_distance.py
new file mode 100644
index 0000000000000000000000000000000000000000..eb0b59dfdc9b6523611cde6021e601568c7d9311
--- /dev/null
+++ b/examples/data_tests/libs/lineage_distance.py
@@ -0,0 +1,1268 @@
+import numpy as np
+import matplotlib.pyplot as plt
+from scipy.cluster import hierarchy
+import pickle as pkl
+import re
+import os
+import xml.etree.ElementTree as ET
+from morphonet.tools import get_longid,get_id_t
+
+def indent_xml(elem, level=0):
+    """ Recursively auto indent an xml object to save it to file
+
+    :param elem: XML object to be indented
+    :type elem: xml.etree.ElementTree.Element
+    :param level: Level of indentation (Default value = 0)
+    :type level: int
+    :return: Indented XML object
+    :rtype: xml.etree.ElementTree.Element
+
+    """
+    i = "\n" + level*"  "
+    j = "\n" + (level-1)*"  "
+    if len(elem):
+        if not elem.text or not elem.text.strip():
+            elem.text = i + "  "
+        if not elem.tail or not elem.tail.strip():
+            elem.tail = i
+        for subelem in elem:
+            indent_xml(subelem, level+1)
+        if not elem.tail or not elem.tail.strip():
+            elem.tail = j
+    else:
+        if level and (not elem.tail or not elem.tail.strip()):
+            elem.tail = j
+    return elem
+
+def list_ids_with_name(list_names,cell_name):
+    """Using a dict of cell key to name coming, retrieve the list of cell keys with the name
+
+    :param list_names: List of names
+    :type list_names: dict
+    :param cell_name: The cell name to find
+    :type cell_name: str
+    :return: List of cell keys with the name
+    :rtype: list
+
+    """
+    list_ids = []
+    for cell_key in list_names:
+        if cell_name == list_names[cell_key]:
+            list_ids.append(cell_key)
+    return list_ids
+def find_lowest_t_with_cell(list_names, cell_name):
+    """Using a dict of cell key to name coming, retrieve the cell with the lowest time point
+
+    :param list_names: List of names
+    :type list_names: dict
+    :param cell_name: The cell name to find
+    :type cell_name: str
+    :return: Cell time point, cell id of the cell
+    :rtype: tuple
+
+    """
+    current_found_time = None
+    current_found_id = None
+    for cell_key in list_names:
+        if cell_name == list_names[cell_key]:
+            tc, idc = get_id_t(cell_key)
+            if current_found_time is None or current_found_time > int(tc):
+                current_found_time = int(tc)
+                current_found_id = int(idc)
+    return current_found_time,current_found_id
+
+
+
+# NEW DEFINITION OF LOCAL COST FUNCTIONS FOR TREE-EDIT DISTANCES
+# Definition of a LOCAL COST function.
+# this function computes the elementary distance between to any vertices of two trees
+def new_local_cost(v1=None,v2=None):
+    """Compute distance between two vertices
+
+    :param v1:  (Default value = None)
+    :param v2:  (Default value = None)
+
+    
+    """
+    if isinstance(v1,tuple)==True:
+        if v2 == None:
+            cost = 0
+            v = list(v1)
+            for i in range(len(v)):
+                cost+=v[i]
+            return cost
+        else:
+            d=len(v1)
+            return sum( [abs(v1[i]-v2[i]) for i in range(0,d)])
+    else:
+        if v2==None:
+            return v1
+        else:
+            return abs(v1-v2)
+
+# A first function to defined an attribute called 'label_for_distance' with a constant value
+# on each node of a given tree (to make simple tests)
+def give_label_dist_constant(tree,value):
+    """
+
+    :param tree:
+    :param value: 
+
+    """
+    # associate a constant attribute value with all the vertices of the tree
+    # Parameters
+    # ----------
+    # tree: treex tree
+    # value: number vector
+    tree.add_attribute_to_id('label_for_distance',value)
+    for child in tree.my_children:
+        give_label_dist_constant(child,value)
+
+# Define a more general function to attach a 'label_for_distance' to a tree
+# with more general values (by copying the value of another given attribute in 'label_for_distance')
+# and so that the edit-distance can then be used with this tree.
+
+def give_label_dist_attribute(tree,name):
+    """
+
+    :param tree: 
+    :param name: 
+
+    """
+    # associate a constant attribute value with all the vertices of the tree
+    # Parameters
+    # ----------
+    # tree: treex tree
+    # value: number vector
+    value = tree.get_attribute(name)
+    tree.add_attribute_to_id('label_for_distance',value)
+    for child in tree.my_children:
+        give_label_dist_attribute(child,name)
+
+def time_stamp(cell_id):
+    """ Retrieve the time stamp from a cell key
+
+    :param cell_id: Cell key
+    :type cell_id: int
+    :return: Time stamp
+    :rtype: int
+
+    """
+    return int(cell_id // 10000)
+
+def cell_lifespan_list(c,cell_lineage):
+    """
+
+    :param c: 
+    :param cell_lineage: 
+
+    """
+    cell_list = [c]
+    if c in cell_lineage:
+        child = cell_lineage[c]
+        while len(child) == 1:
+            cell_list.append(child[0])
+            if child[0] in cell_lineage:
+                child = cell_lineage[child[0]]
+            else:
+                child = []
+    return cell_list
+
+def life_span(cell_id,cell_lineage):
+    """
+
+    :param cell_id: 
+    :param cell_lineage: 
+
+    """
+    lst = cell_lifespan_list(cell_id,cell_lineage)
+    return time_stamp(lst[-1])-time_stamp(lst[0])+1
+
+# Two functions to create (Treex) trees from lineage data
+
+# definition of a function to build a tree from astec cell lineages
+# Note: this accesses the astec lineage as a global variable, as well as the translation dict from tree to astec
+def add_child_tree(t,astec_child,cell_lineage,astec2tree,maxtime=None):
+    """
+
+    :param t: 
+    :param astec_child: 
+    :param cell_lineage: 
+    :param astec2tree: 
+    :param maxtime:  (Default value = None)
+
+    """
+    import treex as tx
+    if maxtime == None:
+        if astec_child == None :
+            return False
+    else:
+        if astec_child == None or time_stamp(astec_child) > maxtime:
+            return False
+    ct = tx.Tree()
+    ct.add_attribute_to_id('astec_id',astec_child)
+    astec2tree[astec_child] = ct.my_id
+    t.add_subtree(ct)
+    if astec_child in cell_lineage: # astec_child is not terminal
+        for c in cell_lineage[astec_child]:
+            #print(c)
+            add_child_tree(ct, c,cell_lineage,astec2tree,maxtime) # recursive call on children of astec_child
+    return True
+
+def daughters(c,cell_lineage):
+    """
+
+    :param c: 
+    :param cell_lineage: 
+
+    """
+    if c in cell_lineage:
+        child = cell_lineage[c] #
+        if len(child) == 0:
+            return None
+        elif len(child) == 1:
+            return daughters(child[0],cell_lineage)
+        else:
+            return child
+    else:
+        return None
+
+def mother(c,cell_lineage):
+    """
+
+    :param c: 
+    :param cell_lineage: 
+
+    """
+    mother_dic = {}
+    for c in cell_lineage:
+        child = daughters(c,cell_lineage)
+        if child is not None:
+            mother_dic[child[0]] = c
+            mother_dic[child[1]] = c
+    if c in mother_dic:
+        return mother_dic[c]
+    else:
+        return None
+
+def has_sister(c,cell_lineage):
+    """
+
+    :param c: 
+    :param cell_lineage: 
+
+    """
+    m = mother(c)
+    if m is not None:
+        if m in cell_lineage:
+            child = cell_lineage[m]
+            if len(child) == 1:
+                return has_sister(m,cell_lineage)
+            else:
+                return True
+        else:
+            return False
+    else:
+        return False
+
+def sister(c,cell_lineage):
+    """
+
+    :param c: 
+    :param cell_lineage: 
+
+    """
+    m = mother(c)
+    if m is not None:
+        if m in cell_lineage:
+            child = cell_lineage[m]
+            if len(child) == 1:
+                return has_sister(m,cell_lineage)
+            else: # len == 2
+                return child[0] if child[0] != c else child[1]
+        else:
+            return None
+    else:
+        return None
+
+# Create the lineage tree of the cell given in argument
+def create_lineage_tree(astec_id,cell_lineage,maxtime=None):
+    """
+
+    :param astec_id: 
+    :param cell_lineage: 
+    :param maxtime:  (Default value = None)
+
+    """
+    import treex as tx
+    t = tx.Tree() # create tree (in treex, nodes are actually trees)
+    astec2tree = {}
+    # setting root data
+    t.add_attribute_to_id('astec_id',astec_id)
+    # store mapping between vertices
+    astec2tree[astec_id] = t.my_id
+
+    if astec_id in cell_lineage:
+        for c in cell_lineage[astec_id]:
+            #print(c)
+            add_child_tree(t,c,cell_lineage,astec2tree,maxtime)
+    return t
+
+def create_compressed_lineage_tree(astec_id,cell_lineage,cellnames=None,stop_at_division=False):
+    """
+
+    :param astec_id: 
+    :param cell_lineage: 
+    :param cellnames:  (Default value = None)
+
+    """
+    # print(astec_id)
+    import treex as tx
+    astec2tree = {}
+    lifespan = life_span(astec_id,cell_lineage)
+    t = tx.Tree()  # create tree (in treex, nodes are actually trees)
+    # setting root data
+    if cellnames is not None and astec_id in cellnames:
+        t.add_attribute_to_id('astec_id', cellnames[astec_id])
+    else:
+        t.add_attribute_to_id('astec_id', astec_id)
+    t.add_attribute_to_id('lifespan', lifespan)
+    # store mapping between vertices
+    keytree = astec_id
+    if cellnames is not None and astec_id in cellnames:
+        keytree = cellnames[astec_id]
+    astec2tree[keytree] = t.my_id
+    if not stop_at_division:
+        dlist = daughters(astec_id,cell_lineage)  # find the daughters (cell ids just after a division)
+        if dlist != None and dlist != []:
+            try:
+                assert len(dlist) == 2
+                for c in dlist:
+                    if c in cell_lineage:
+                        tchild = create_compressed_lineage_tree(c,cell_lineage,cellnames)
+                        if tchild != None:
+                            t.add_subtree(tchild)
+            except :
+                print("skipping")
+    return t
+
+def apply_compressed_compare(lineage_prop1,lineage_prop2,cell_key,cell_key2,stop_at_division=False):
+    """
+
+    :param lineage_prop1: 
+    :param lineage_prop2: 
+    :param cell_key: 
+    :param cell_key2: 
+
+    """
+    from treex.analysis.edit_distance.zhang_labeled_trees import zhang_edit_distance
+    tree1 = create_compressed_lineage_tree(cell_key, lineage_prop1,stop_at_division=stop_at_division)
+    tree2 = create_compressed_lineage_tree(cell_key2, lineage_prop2,stop_at_division=stop_at_division)
+    give_label_dist_attribute(tree1, 'lifespan')
+    give_label_dist_attribute(tree2, 'lifespan')
+
+    # Compute the zhang edit-distance between them (then making use of the information 'lifespan' on the nodes)
+    d = zhang_edit_distance(tree1, tree2, "lifespan", new_local_cost)
+    return d
+
+def apply_compare(lineage_prop1,lineage_prop2,cell_key,cell_key2,stop_at_divison=False):
+    """
+
+    :param lineage_prop1: 
+    :param lineage_prop2: 
+    :param cell_key: 
+    :param cell_key2: 
+
+    """
+    from treex.analysis.edit_distance.zhang_labeled_trees import zhang_edit_distance
+    tree1 = create_lineage_tree(cell_key, lineage_prop1)
+    tree2 = create_lineage_tree(cell_key2, lineage_prop2)
+    give_label_dist_constant(tree1, (1))
+    give_label_dist_constant(tree2, (1))
+    d = zhang_edit_distance(tree1, tree2, "label_for_distance", new_local_cost)
+    return d
+
+def read_lineage(lineage):
+    """
+
+    :param lineage: 
+
+    """
+    import os
+    converted = False
+    lineagepkl = None
+    if lineage.endswith(".xml"):
+        lineagepkl = lineage.replace(".xml",".pkl")
+        os.system("conda run -n astec astec_embryoproperties -i "+lineage+" -o "+lineagepkl)
+        converted = True
+        lineage = lineagepkl
+    f = open(lineage, 'rb')
+    astec_output1 = pkl.load(f)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    f.close()
+    if converted and lineagepkl is not None:
+        if os.path.isfile(lineagepkl):
+            os.system("rm "+lineagepkl)
+    return astec_output1
+def get_id_t(idl):
+    """Return the cell t,id
+
+    :param idl:
+    :type idl: int
+
+
+    """
+    t = int(int(idl) / (10 ** 4))
+    cell_id = int(idl) - int(t) * 10 ** 4
+    return t, cell_id
+def load_cell_names(lineage):
+    """
+
+    :param lineage: 
+
+    """
+    astec_output = read_lineage(lineage)
+    if not 'cell_name' in astec_output:
+        print("Name information is missing")
+        exit()
+    return astec_output['cell_name']
+def name_to_id(output_astec,namecell):
+    """
+
+    :param output_astec: 
+    :param namecell: 
+
+    """
+    if not 'cell_name' in output_astec:
+        print("Name information is missing")
+        exit()
+
+    cell_names1 = output_astec['cell_name']
+    cell_key_1 =""
+    for keyc in cell_names1:
+        if cell_names1[keyc] == namecell:
+            tc, idc = get_id_t(keyc)
+            previoust = None
+            if cell_key_1 != "":
+                previoust, previousid = get_id_t(cell_key_1)
+            if previoust is None or previoust > tc:
+                cell_key_1 = keyc
+
+    return cell_key_1
+
+
+def compare_cells_by_name(lineage_path_1,lineage_path_2,cell_names_pairs,stop_at_division=False):
+    """
+
+    :param lineage_path_1:
+    :param lineage_path_2:
+    :param cell_name1:
+    :param cell_name2:
+
+    """
+    distances_by_names = {}
+    tested_names = []
+    astec_output1 = read_lineage(
+        lineage_path_1)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    astec_output2 = read_lineage(
+        lineage_path_2)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    cell_lineage1 = astec_output1['cell_lineage']
+    cell_lineage2 = astec_output2['cell_lineage']
+    for cell_name1, cell_name2 in cell_names_pairs:
+        if not cell_name1 in tested_names:
+            cell_key_1 = name_to_id(astec_output1,cell_name1)
+            cell_key_2 = name_to_id(astec_output2,cell_name2)
+            if cell_key_1 != "" and cell_key_2 != "":
+                cell_key_1_int = int(cell_key_1)
+                cell_key_2_int = int(cell_key_2)
+                distance_found = apply_compressed_compare(cell_lineage1, cell_lineage2, cell_key_1_int,cell_key_2_int,stop_at_division=stop_at_division)
+                distances_by_names[cell_name1] = distance_found
+                distances_by_names[cell_name2] = distance_found
+                tested_names.append(cell_name1)
+                tested_names.append(cell_name2)
+    return distances_by_names
+
+def compare_cell_by_name(lineage_path_1,lineage_path_2,cell_name1,cell_name2):
+    """
+
+    :param lineage_path_1: 
+    :param lineage_path_2: 
+    :param cell_name1: 
+    :param cell_name2: 
+
+    """
+    astec_output1 = read_lineage(
+        lineage_path_1)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    astec_output2 = read_lineage(
+        lineage_path_2)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    cell_lineage1 = astec_output1['cell_lineage']
+    cell_lineage2 = astec_output2['cell_lineage']
+    cell_key_1 = name_to_id(astec_output1,cell_name1)
+    cell_key_2 = name_to_id(astec_output2,cell_name2)
+
+    return apply_compressed_compare(cell_lineage1, cell_lineage2, cell_key_1,cell_key_2)
+
+def compare_cell_by_key(lineage_path_1,lineage_path_2,cell_key_1,cell_key_2):
+    """
+
+    :param lineage_path_1: 
+    :param lineage_path_2: 
+    :param cell_key_1: 
+    :param cell_key_2: 
+
+    """
+    astec_output1 = read_lineage(lineage_path_1)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    astec_output2 = read_lineage(lineage_path_2)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    cell_lineage1 = astec_output1['cell_lineage']
+    cell_lineage2 = astec_output2['cell_lineage']
+
+    return apply_compare(cell_lineage1,cell_lineage2,cell_key_1,cell_key_2)
+
+def print_tree(lineage,cell_key,compressed=False,name=None):
+    """
+
+    :param lineage: 
+    :param cell_key: 
+    :param compressed:  (Default value = False)
+    :param name:  (Default value = None)
+
+    """
+    from treex.visualization.matplotlib_plot import view_tree
+    astec_output = read_lineage(lineage)
+    cell_lineage = astec_output['cell_lineage']
+    treeforcell = None
+    if compressed:
+        treeforcell=create_compressed_lineage_tree(cell_key, cell_lineage)
+    else :
+        treeforcell=create_lineage_tree(cell_key, cell_lineage)
+    fig = view_tree(treeforcell)
+    displayname = cell_key
+    if name is not None:
+        displayname = name
+    prefixfig = "dendogram_plots/"
+    if compressed:
+        prefixfig=prefixfig+"compressed_tree_"
+    else:
+        prefixfig = prefixfig + "tree_"
+    fig.savefig(prefixfig+lineage.split('.')[0].replace('/','_')+"_"+displayname.replace(".","")+".png")
+
+def print_tree_by_name(lineage,cell_name,compressed=False):
+    """
+
+    :param lineage: 
+    :param cell_name: 
+    :param compressed:  (Default value = False)
+
+    """
+    astec_output = read_lineage(lineage)
+    cell_key_1 = name_to_id(astec_output,cell_name)
+    print_tree(lineage,cell_key_1,compressed,cell_name)
+
+
+
+# Transform distance matrix into a condensed matrix (used by linkage function below)
+# (= vector array made from upper triangular part of the distance matrix)
+# i<j<m, where m is the number of original observations
+# m * i + j - ((i + 2) * (i + 1)) // 2.
+def condensed_matrix(dist_mat):
+    """
+
+    :param dist_mat: 
+
+    """
+    m = np.shape(dist_mat)[0]
+    cm = np.zeros(m*(m-1)//2)
+    for j in range(m):
+        for i in range(j):
+            cm[m * i + j - ((i + 2) * (i + 1)) // 2] = dist_mat[j][i]
+    return cm
+def count_cells(cell_list,remove_time=[]):
+    """
+
+    :param cell_list: 
+    :param remove_time:  (Default value = [])
+
+    """
+    cell_count_by_time = {}
+    for cell_key in cell_list:
+        cell_obj_t = time_stamp(cell_key)
+        if not cell_obj_t in remove_time:
+            if not cell_obj_t in cell_count_by_time:
+                cell_count_by_time[cell_obj_t] = 0
+            cell_count_by_time[cell_obj_t] += 1
+    return cell_count_by_time
+
+def get_cell_generation(cell_names,cellkey):
+    """
+
+    :param cell_names: 
+    :param cellkey: 
+
+    """
+    if cell_names is None or len(cell_names)==0:
+        return None
+    if not cellkey in cell_names:
+        return None
+    findgenin = cell_names[cellkey].split(".")[0]
+    return int(re.findall(r'\d+', findgenin)[-1])
+
+
+def compute_dendogram(lineage_trees):
+    """
+
+    :param lineage_trees: 
+
+    """
+    from treex.analysis.edit_distance.zhang_labeled_trees import zhang_edit_distance
+    nbcell=len(lineage_trees)
+    dist_array = np.zeros((nbcell, nbcell))
+    for i in range(nbcell):
+        for j in range(i, nbcell):
+            dist_array[i][j] = zhang_edit_distance(lineage_trees[i], lineage_trees[j], "lifespan", new_local_cost)
+            dist_array[j][i] = dist_array[i][j]
+    return hierarchy.linkage(condensed_matrix(dist_array), method='average', optimal_ordering=True)
+
+def Get_Cell_Names(path_lineage, info="cell_name"):
+    """ Extract the cell names information from a property file as a dict
+
+    :param path_lineage: Path to the property file
+    :type path_lineage: str
+    :param info: Name of cell names property (Default value = "cell_name")
+    :type info: str
+    :return: Dict of cell names (key = cell key , value = cell name)
+    :rtype: dict
+    """
+    cell_values={}
+    source = open(path_lineage)
+    tree = ET.parse(source)
+    tree = tree.getroot()
+    lineage_elem = tree.find(info)
+    if lineage_elem is not None:
+        for cell in lineage_elem.findall('cell'):
+            cell_id = cell.get('cell-id')
+            cell_child = cell.text
+            if cell_child is not None and cell_child != "None":
+                new_val = str(cell_child.replace("'", "").replace(" ", ""))
+                cell_values[cell_id] = new_val
+        # print(str(result))
+        return cell_values
+    return None
+
+def compute_cluster_for_names(lineage_list,namelist,lineage_names):
+    """
+
+    :param lineage_list: 
+    :param namelist: 
+    :param lineage_names: 
+
+    """
+    mapping_index={}
+    i=0
+    lineage_trees = []
+    lin=0
+    for lineage in lineage_list:
+        cells_to_compute = []
+        workedids = []
+        astec_output = read_lineage(lineage)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+        cell_lineage = astec_output['cell_lineage']
+        cellnames = None
+        if 'cell_name' in astec_output:
+            cellnames = astec_output['cell_name']
+        if cellnames is None:
+            print("No names found in : "+lineage)
+        filteredlineage = list(filter(lambda x: (cellnames is not None and x in cellnames and cellnames[x] in namelist), cell_lineage))
+        for cell in filteredlineage:
+                if cellnames[cell] in namelist:
+                    mother_cell = get_direct_mother(cell, cell_lineage,cellnames)
+                    if mother_cell is not None and not mother_cell in workedids:
+                        if mother_cell in cellnames and cellnames[mother_cell] in namelist:
+                            print("Added cell "+str(mother_cell)+" with name "+str(cellnames[mother_cell]))
+                            cells_to_compute.append(mother_cell)
+                            workedids.append(mother_cell)
+        # Compute the lineage trees:
+        for cellc in cells_to_compute:
+            print("cell "+str(cellc)+" found name : "+str(cellnames[cellc])+" at index "+str(i))
+            mapping_index[i]=cellnames[cellc]+" "+lineage_names[lin]
+            t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames)
+            give_label_dist_attribute(t, 'lifespan')
+            lineage_trees.append(t)
+            i+=1
+        lin+=1
+    Z = compute_dendogram(lineage_trees)
+    return mapping_index, lineage_trees, Z
+
+def get_next_mother(cell,cell_lineage):
+    """
+
+    :param cell: 
+    :param cell_lineage: 
+
+    """
+    for celltest in cell_lineage:
+        for cellval in cell_lineage[celltest]:
+            if cellval==cell:
+                return celltest
+    return None
+
+def get_daughters(cell,cell_lineage):
+    """
+
+    :param cell: 
+    :param cell_lineage: 
+
+    """
+    d = []
+    for celltest in cell_lineage:
+        if celltest==cell:
+            d=cell_lineage[celltest]
+    return d
+
+def get_direct_mother(cell,cell_lineage,cellnames=None):
+    """
+
+    :param cell: 
+    :param cell_lineage: 
+    :param cellnames:  (Default value = None)
+
+    """
+    if not cell in cell_lineage:
+        return None
+    directmother = get_next_mother(cell,cell_lineage)
+    if directmother is None:
+        return cell
+    daughters = get_daughters(directmother,cell_lineage)
+    directmothertwice = get_next_mother(directmother, cell_lineage)
+    if directmothertwice is None or len(daughters) > 1:
+        return directmother
+    return get_direct_mother(directmother,cell_lineage)
+
+def compute_cluster_for_generation(lineage_list,generation_list,lineage_names):
+    """
+
+    :param lineage_list: 
+    :param generation_list: 
+    :param lineage_names: 
+
+    """
+    mapping_index={}
+    i=0
+    lineage_trees = []
+    lin=0
+    for lineage in lineage_list:
+        workedids = []
+        cells_to_compute = []
+        astec_output = read_lineage(lineage)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+        cell_lineage = astec_output['cell_lineage']
+        cellnames = None
+        if 'cell_name' in astec_output:
+            cellnames = astec_output['cell_name']
+        if cellnames is None:
+            print("No names found in : "+lineage)
+        filtered_lineage=list(filter(lambda x: (x in cellnames and int(get_cell_generation(cellnames,x)) in generation_list), cell_lineage))
+        for cell in filtered_lineage:
+            mother_cell = get_direct_mother(cell,cell_lineage,cellnames)
+            if mother_cell is not None and not mother_cell in workedids:
+                cells_to_compute.append(mother_cell)
+                workedids.append(mother_cell)
+        # Compute the lineage trees:
+        for cellc in cells_to_compute:
+            mapping_index[i] = cellnames[cellc] + " " + lineage_names[lin]
+            t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames)
+            give_label_dist_attribute(t, 'lifespan')
+            lineage_trees.append(t)
+            i+=1
+        lin += 1
+    Z = compute_dendogram(lineage_trees)
+    return mapping_index, lineage_trees, Z
+
+
+def compute_cluster_for_stage(lineage_list,cell_count,lineage_names):
+    """
+
+    :param lineage_list: 
+    :param cell_count: 
+    :param lineage_names: 
+
+    """
+
+    mapping_index={}
+    i=0
+    lineage_trees = []
+    lin=0
+    for lineage in lineage_list:
+        workedids = []
+        cells_to_compute = []
+        astec_output = read_lineage(lineage)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+        cell_lineage = astec_output['cell_lineage']
+        cell_count_time=dict(sorted(count_cells(cell_lineage).items()))
+        cellnames = None
+        if 'cell_name' in astec_output:
+            cellnames = astec_output['cell_name']
+        timepoint = None
+        for time in cell_count_time:
+            if timepoint is None and cell_count_time[time] == cell_count:
+                timepoint=int(time)
+        if timepoint is None:
+            for time in cell_count_time:
+                if timepoint is None and cell_count_time[time] >= cell_count:
+                    timepoint = int(time)
+        filteredlineage = list(filter(lambda x: (int(time_stamp(x))==timepoint), cell_lineage))
+        for cell in filteredlineage:
+            mother_cell = get_direct_mother(cell,cell_lineage,cellnames)
+            if mother_cell is not None and not mother_cell in workedids:
+                cells_to_compute.append(mother_cell)
+                workedids.append(mother_cell)
+        # Compute the lineage trees:
+        for cellc in cells_to_compute:
+            mapping_index[i] = cellnames[cellc] + " " + lineage_names[lin]
+            t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames)
+            give_label_dist_attribute(t, 'lifespan')
+            lineage_trees.append(t)
+            i+=1
+        lin += 1
+    Z = compute_dendogram(lineage_trees)
+    return mapping_index, lineage_trees, Z
+
+def compute_cluster_for_time(lineage_list,timepoint_list,lineage_names):
+    """
+
+    :param lineage_list: 
+    :param timepoint_list: 
+    :param lineage_names: 
+
+    """
+    cells_to_compute = []
+    mapping_index={}
+    i=0
+    lineage_trees = []
+    lin=0
+    for lineage in lineage_list:
+        workedids = []
+        astec_output = read_lineage(lineage)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+        cell_lineage = astec_output['cell_lineage']
+        cellnames = None
+        if 'cell_name' in astec_output:
+            cellnames = astec_output['cell_name']
+        filteredlineage = list(filter(lambda x: (time_stamp(x) is not None and int(time_stamp(x)) in timepoint_list), cell_lineage))
+        for cell in filteredlineage:
+            if cell is not None and not cell in workedids:
+                cells_to_compute.append(cell)
+                workedids.append(cell)
+        # Compute the lineage trees:
+        for cellc in cells_to_compute:
+            cname = str(cellc)
+            if cellc in cellnames:
+                cname=cellnames[cellc]
+            mapping_index[i] = cname + " " + lineage_names[lin]
+            t = create_compressed_lineage_tree(cellc,cell_lineage,cellnames)
+            give_label_dist_attribute(t, 'lifespan')
+            lineage_trees.append(t)
+            i+=1
+        lin += 1
+    Z = compute_dendogram(lineage_trees)
+    return mapping_index, lineage_trees, Z
+def compute_cluster_for_time_single_lineage(lineage,timepoint):
+    """
+
+    :param lineage: 
+    :param timepoint: 
+
+    """
+    cells_to_compute = []
+    mapping_index={}
+    astec_output = read_lineage(lineage)  # astec_output is the set of all dictionary returned by ASTEC (itself a dic)
+    cell_lineage = astec_output['cell_lineage']
+    cellnames = None
+    if 'cell_name' in astec_output:
+        cellnames = astec_output['cell_name']
+    for cell in cell_lineage:
+        timecell = time_stamp(cell)
+        if timecell==timepoint:
+            cells_to_compute.append(cell)
+    nbcell = len(cells_to_compute)
+    lineage_trees = []
+    # Compute the lineage trees:
+    for i in range(nbcell):
+        cname=cells_to_compute[i]
+        if cellnames is not None and cname in cellnames:
+            cname = cellnames[cname]
+        mapping_index[i]=cname
+        t = create_compressed_lineage_tree(cells_to_compute[i],cell_lineage,cellnames)
+        give_label_dist_attribute(t, 'lifespan')
+        lineage_trees.append(t)
+    Z = compute_dendogram(lineage_trees)
+    return mapping_index,lineage_trees,Z
+def plot_cluster(dendogram,filename,axismapping=None,title=None,figxsize=None,figysize=None):
+    """
+
+    :param dendogram: 
+    :param filename: 
+    :param axismapping:  (Default value = None)
+    :param title:  (Default value = None)
+    :param figxsize:  (Default value = None)
+    :param figysize:  (Default value = None)
+
+    """
+    dist_threshold = 350  # this is used to define the grain of the classes (see horizontal dashed line on the figure)
+
+    # Function linkage performs the hirarchical clustering based on a condensed version of the distance matrix
+    # print(Z)
+
+    # - Then the cell hierarchy is computed as a dendrogram data-structure
+    # It contains the labels of the points, their color code and more (see scipy doc)
+
+    fig = plt.figure()
+    curr_axis = fig.gca()  # current viewport (called axis) in the fig window (fig)
+
+    # prepare the plot of the dendrogram and select the level of the classes see: 'color_threshold'
+    dn = hierarchy.dendrogram(dendogram, color_threshold=dist_threshold, ax=curr_axis, leaf_font_size=14)
+
+    curr_axis.axhline(y=dist_threshold, linestyle='--', linewidth=1)
+    curr_axis.set_title(title if title is not None else 'Hierarchical Clustering Dendrogram', size=24)
+    curr_axis.set_xlabel("cell lineage", size=18)
+    curr_axis.set_ylabel("edit-distance", size=18)
+    labels = [item.get_text() for item in curr_axis.get_xticklabels()]
+    for i in range(0,len(labels)):
+        previouslabs = labels[i]
+        if int(previouslabs) in axismapping:
+            print("Working on id : "+str(previouslabs)+" with label "+ str(axismapping[int(previouslabs)]))
+            writtenname = str(axismapping[int(previouslabs)])
+            if "." in writtenname:
+                namesplitted=writtenname.split('.')
+                labels[i]=namesplitted[0]+"."+namesplitted[1].lstrip("0").replace("_","-")
+            else :
+                labels[i] = writtenname
+    curr_axis.set_xticklabels(labels)
+    plt.xticks(fontsize=8)
+    fig.set_size_inches(figxsize if figxsize is not None else 40,figysize if figysize is not None else 15)  # size of the plot
+    #fig.set_size_inches(figxsize if figxsize is not None else 40, figysize if figysize is not None else 15)  # size of the plot
+    plt.savefig(filename)
+    plt.clf()
+def compute_classes_dendogram(dendogram,lineage_trees):
+    """
+
+    :param dendogram: 
+    :param lineage_trees: 
+
+    """
+    # - Finally Extract the classes from the dendrogram:
+    dist_threshold = 350
+    fig = plt.figure()
+    curr_axis = fig.gca()
+    classes = hierarchy.fcluster(dendogram, t=dist_threshold, criterion='distance')
+    dn = hierarchy.dendrogram(dendogram, color_threshold=dist_threshold, ax=curr_axis, leaf_font_size=14)
+
+    # and print the detailed results corresponding to the above figure
+    classlist = {}
+    for i in range(0,len(classes)):
+        k = dn['leaves'][i]
+        if not k in classlist:
+            classlist[k] = []
+        cell_id = lineage_trees[k].get_attribute('astec_id')
+        classlist[k].append(cell_id)
+    for k in classlist:
+        print("> Cells in class "+str(k))
+        for cell in classlist[k]:
+            print('     -> ', cell)
+
+
+def AddNodeToXML(xml_file, value_dict, node_name,node_subname,identifier_text='value',tag_type=""):
+    """ Save a given dict to a property file, specifying the property name and keys
+
+    :param xml_file: Path to the property file
+    :type xml_file: str
+    :param value_dict: Dict to be saved (key being cell keys , value being value)
+    :type value_dict: dict
+    :param node_name: Name of the property
+    :type node_name: str
+    :param node_subname: Name of the cell element (should be "cell")
+    :type node_subname: str
+    :param tag_type: If set, add a tag to the created property name, to specify morphonet_type
+    :type tag_type: str
+    :param identifier_text:  Text of the value for cell (Default value = 'value')
+    :type identifier_text: str
+
+    """
+    print(str(xml_file))
+    if not os.path.isfile(xml_file) or os.stat(xml_file).st_size == 0:
+        print("XML file not found , create it with path : "+str(xml_file))
+        f = open(xml_file,"w+")
+        f.close()
+        root = ET.Element('root')
+        tree = ET.ElementTree(root)
+        tree.write(xml_file)
+    source = open(xml_file)
+    tree = ET.parse(source)
+    tree = tree.getroot()
+    selec_node = node_name
+    name_selec_elem = tree.find(selec_node)
+    if name_selec_elem is None:
+        name_selec_elem = ET.SubElement(tree, selec_node)
+        if tag_type != "":
+            name_selec_elem.set("mn_type", tag_type)
+    for cell in name_selec_elem.findall(node_subname):
+        if cell.get(identifier_text) in value_dict.keys() or cell.get(
+                identifier_text) in value_dict.keys() and value_dict is not None and value_dict[cell.get(identifier_text)] is not None:
+            cell.text =str(value_dict[cell.get(identifier_text)])
+            value_dict.pop(cell.get(identifier_text), None)
+    for cell in value_dict:
+        new_cell = ET.SubElement(name_selec_elem, node_subname)
+        if cell != "":
+            new_cell.set(identifier_text, str(cell))
+        new_cell.text = str(value_dict[cell])
+    indent_xml(tree)
+    source.close()
+    mydata = ET.tostring(tree, encoding='utf8', method='xml').decode("utf8")
+    myfile = open(xml_file, "w+")
+    myfile.write(mydata)
+    myfile.close()
+
+
+def GetCellWholeLifetime(cell):
+    """ For a given cell, return the list of all cells in the cell lineage tree (past and future)
+
+    :param cell: Cell to retrieve the lineage tree
+    :type cell: Cell
+    :return: List of all cells
+    :rtype: list
+
+    """
+    list_cells = [cell,]
+    daughters = GetAllCellLifeFutur(cell)
+    mothers = GetCellAllLifePast(cell)
+    for dau in daughters:
+        if not dau in list_cells:
+            list_cells.append(dau)
+    for mot in mothers:
+        if not mot in list_cells:
+            list_cells.append(mot)
+    #print("cell : "+str(cell))
+    #print("Cells found : "+str(len(list_cells)))
+    return list_cells
+
+
+def GetAllCellLifeFutur(cell):
+    """ For a given cell, return the list of future cells in the lineage tree
+
+    :param cell: Cell to retrieve the lineage tree
+    :type cell: Cell
+    :return: List of all cells
+    :rtype: list
+
+    """
+    list_cells = []
+    if cell is None or cell.daughters is None or len(cell.daughters) < 1:
+        #print("Futur : cell is : "+str(cell))
+        #print("Futur : cell has no daughters")
+        return list_cells
+    for daughter in cell.daughters:
+        cells = GetAllCellLifeFutur(daughter)
+        for cell_child in cells:
+            list_cells.append(cell_child)
+
+    #print("number of daughters found : "+str(len(cells)))
+    if list_cells is not None:
+        list_cells.append(cell)
+    return list_cells
+
+def GetCellAllLifePast(cell):
+    """ For a given cell, return the list of past cells in the lineage tree
+
+    :param cell: Cell to retrieve the lineage tree
+    :type cell: Cell
+    :return: List of all cells
+    :rtype: list
+
+    """
+    list_cells = []
+    #if cell is not None and cell.mothers is not None:
+    #    print(cell.mothers)
+    if cell is None or cell.mothers is None or len(cell.mothers) == 0 or cell.mothers[0].daughters is None:
+        #print("Past : cell is : "+str(cell))
+        #print("Past : cell has no mothers")
+        return list_cells
+    for mother in cell.mothers:
+        cells =GetCellAllLifePast(mother)
+        for cell_child in cells:
+            list_cells.append(cell_child)
+    #print("number of mothers found : "+str(len(cells)))
+    if list_cells is not None:
+        list_cells.append(cell)
+    return list_cells
+
+class Cell:
+    """ Class representing the cells in memory. A cell has a time point (int) , and id (int) , a list of cells being
+    their direct mothers (should only contain one) , and a list of cells being their direct daughters (can be 0 to many)
+    """
+    id = -1
+    t = -1
+    mothers = []
+    daughters = []
+
+    def __init__(self,id_cell,time_cell):
+        self.id = id_cell
+        self.t = time_cell
+        self.mothers = []
+        self.daughters = []
+
+    def add_mother(self,cell):
+        """ Add a mother to the list of mothers , and add this cell to the mother's daughters list
+
+        :param cell: The cell that will be current cell mother
+        :type cell: Cell
+
+        """
+        #print("mother len before : " + str(len(self.mothers)))
+        #print("add mother for cell : "+str(self.t)+","+str(self.id)+" for mother : "+str(cell.t)+","+str(cell.id))
+        if self.mothers is None:
+            self.mothers = []
+        if not cell in self.mothers:
+            self.mothers.append(cell)
+        #print("mother len after : " + str(len(self.mothers)))
+        cell.add_daughter(self)
+
+
+    def add_daughter(self,cell):
+        """ Add the given cell to current cell daughters. This function should not be called directly ,
+        instead use "add_mother" on the parameter cell.
+
+        :param cell: Cell to add as a daughter
+        :type cell: Cell
+
+        """
+        #print("daughters len before : " + str(len(self.daughters)))
+        #print("add daughter for cell : " + str(self.t) + "," + str(self.id) + " for mother : " + str(cell.t) + "," + str(cell.id))
+        if self.daughters is None:
+            self.daughters = []
+        if not cell in self.daughters:
+            self.daughters.append(cell)
+
+
+def LoadCellList(path_lineage,lineage_property="cell_lineage"):
+    """ Load the content of "cell_lineage" property from the properties file , as a dict of key being cell keys, value the cell object
+
+    :param path_lineage: Path to the property file
+    :type path_lineage: str
+    :param lineage_property: Name of the lineage property
+    :type lineage_property: str
+    :return: Dict of key being cell keys, value the cell object
+    :rtype: dict
+
+    """
+    try:
+        source = open(path_lineage)
+    except:
+        return None
+    tree = ET.parse(source)
+    cell_list_g = {}
+    tree = tree.getroot()
+    lineage_elem = tree.find(lineage_property)
+    if lineage_elem is not None:
+        for cell in lineage_elem.findall('cell'):
+            cell_t,cell_id = get_id_t(cell.get('cell-id').replace("'","").replace('[','').replace(']','').replace(" ",""))
+            cell_key = str(get_longid(cell_t,cell_id))
+             #cell_key = str(cell_t)+","+str(cell_id)
+            if not cell_key in cell_list_g:
+                cell_object = Cell(int(cell_id),int(cell_t))
+                cell_list_g[cell_key] = cell_object
+            cell_childs = cell.text.split(',')
+            for cell_child_in_list in cell_childs:
+                cell_child_t,cell_child_id = get_id_t(cell_child_in_list.replace("'","").replace('[','').replace(']','').replace(" ",""))
+                cell_child_key = str(get_longid(cell_child_t,cell_child_id))
+                if not cell_child_key in cell_list_g:
+                    cell_child_object = Cell(int(cell_child_id), int(cell_child_t))
+                    cell_list_g[cell_child_key] = cell_child_object
+
+                if not cell_list_g[cell_child_key] in cell_list_g[cell_key].daughters:
+                    cell_list_g[cell_child_key].add_mother(cell_list_g[cell_key])
+        return cell_list_g
+    return None
+
+
+def generate_lineage_comparison(lineage_file,begin_time_point,info_name_1="float_symetric_cells_branch_length_distance",info_name_2="float_symetric_cells_lineage_tree_distance",info_name_3="float_symetric_cells_lineage_subtree_distance"):
+    """
+    Using a named lineage , generates properties representing the distance between symetric cells in lineage
+    The generated properties :
+    - name1 : the distance between symetric cells branch length
+    - name2 : the distance between symetric cells complete lineage tree
+    - name3 : the distance between symetric cells sub lineage tree
+
+    :param dendogram:
+    :param lineage_trees:
+    """
+    if not os.path.isfile(lineage_file):
+        raise Exception("The lineage file does not exist !")
+    names = Get_Cell_Names(lineage_file)
+
+    # Build names lists
+    names_tuples = [] #List of tuples that will compute a distance
+    tested_names = [] #buffer to prevent multiples testing
+    name_to_tid = {} #Used to retrieve ids for information generation
+    for idc in names:
+        current_name = names[idc]
+        mint, minid = find_lowest_t_with_cell(names, current_name) #Find first cell of branch
+        name_to_tid[current_name] = str(mint) + "," + str(minid)
+        other_name = None
+        if "_" in current_name:
+            other_name = current_name.replace("_", "*")
+        elif "*" in current_name:
+            other_name = current_name.replace("*", "_")
+        if other_name is not None and not current_name in tested_names and not other_name in tested_names and current_name != "" and other_name != "":
+            mint, minid = find_lowest_t_with_cell(names, other_name) # Find the first cell of symetric branch
+            name_to_tid[other_name] = str(mint) + "," + str(minid)
+            names_tuples.append((current_name, other_name)) #  Add to compute distance
+            names_tuples.append((other_name, current_name))
+            tested_names.append(current_name) # prevent adding it again
+            tested_names.append(other_name)
+
+    # build branch to branch length
+    distance_by_names = compare_cells_by_name(lineage_file, lineage_file, names_tuples, stop_at_division=True) #build distance
+    info_distance = {}
+    distance_by_names = dict(sorted(distance_by_names.items(), key=lambda key_val: key_val[1]))
+    for name in distance_by_names: # loop is here to fill the whole property (add the proeprty to each snapshot)
+        idscells = list_ids_with_name(names, name)
+        for cell in idscells:
+            if cell not in info_distance:
+                info_distance[cell] = distance_by_names[name]
+    AddNodeToXML(lineage_file, info_distance, info_name_1, "cell", identifier_text="cell-id") #Save in property file
+
+
+    all_cells = LoadCellList(lineage_file)    #compute lineage for both next info
+    distance_by_names = compare_cells_by_name(lineage_file, lineage_file, names_tuples, stop_at_division=False) #compute whole distance
+    info_distance = {}
+    distance_by_names = dict(sorted(distance_by_names.items(), key=lambda key_val: key_val[1]))
+
+    #Build subtree distances
+    for name in distance_by_names:
+        idscells = list_ids_with_name(names, name)
+        begin_t = 100000
+        begin_key = None
+        for cell in idscells:
+            tc, idcell = get_id_t(cell)
+            if tc < begin_t:
+                begin_t = tc
+                begin_key = cell
+        if begin_key is not None:
+            idscells = GetAllCellLifeFutur(all_cells[begin_key])
+            if not all_cells[begin_key] in idscells:
+                idscells.append(all_cells[begin_key])
+            for cellc in idscells:
+                longcellid = get_longid(cellc.t, cellc.id)
+                if longcellid not in info_distance:
+                    info_distance[longcellid] = distance_by_names[name]
+    AddNodeToXML(lineage_file, info_distance, info_name_3, "cell", identifier_text="cell-id")
+    # Build whole tree distance
+    info_distance = {}
+    for name in distance_by_names:
+        idscells = list_ids_with_name(names, name)
+        t_begin_key = None
+        for cell in idscells:
+            tc, idcell = get_id_t(cell)
+            if tc == begin_time_point:
+                t_begin_key = cell
+        if t_begin_key is not None:
+            idscells = GetCellWholeLifetime(all_cells[t_begin_key])
+            for cellc in idscells:
+                longcellid = get_longid(cellc.t, cellc.id)
+                if longcellid not in info_distance:
+                    info_distance[longcellid] = distance_by_names[name]
+    AddNodeToXML(lineage_file, info_distance, info_name_2, "cell", identifier_text="cell-id")
\ No newline at end of file
diff --git a/morphonet/tools.py b/morphonet/tools.py
index 8eadf30da3699f539b51089418de8a81b6172b2c..65ef202079a4aaf633c10a17f9527f1a415d37df 100644
--- a/morphonet/tools.py
+++ b/morphonet/tools.py
@@ -1558,7 +1558,7 @@ def get_property_type(property_name):
         return "float"
     if property_name.lower().find("area") >= 0:
         return "float"
-    if property_name.lower().startswith("fate_map"):
+    if property_name.lower().find("fate_map")  >= 0 :
         return "label"
     if property_name.lower().find("fate") >= 0:
         return "string"