Skip to content
Snippets Groups Projects
Functions.py 34.6 KiB
Newer Older
from Graph_gff import Segments, Features, get_feature_start_on_segment, get_feature_stop_on_segment,invert_seg,search_segment
global segments_on_target_genome
segments_on_target_genome={}
# get the start position of the features on the linear genome, using their coordinates on the graph and the coordinantes of the segments on the genome
def get_feature_start_on_target_genome(start_seg,feat_id,walk):
    seg_start_pos=segments_on_target_genome[start_seg][walk][1]
    feat_start_pos=get_feature_start_on_segment(start_seg,feat_id)

# get the stop position of the features on the linear genome, using their coordinates on the graph and the coordinantes of the segments on the genome
def get_feature_stop_on_target_genome(stop_seg,feat_id,walk):
    seg_start_pos=segments_on_target_genome[stop_seg][walk][1]
    feat_stop_pos=get_feature_stop_on_segment(stop_seg,feat_id)
NMarthe's avatar
NMarthe committed

# get the start position of the features on the linear genome for inverted features
def get_feature_start_on_target_genome_inv(start_seg,feat_id,walk):
    seg_end_pos=segments_on_target_genome[start_seg][walk][2]
    feat_start_pos=get_feature_start_on_segment(start_seg,feat_id)
    return seg_end_pos-feat_start_pos+1

# get the stop position of the features on the linear genome for inverted features
def get_feature_stop_on_target_genome_inv(stop_seg,feat_id,walk):
    seg_end_pos=segments_on_target_genome[stop_seg][walk][2]
    feat_stop_pos=get_feature_stop_on_segment(stop_seg,feat_id)
    
    return seg_end_pos-feat_stop_pos+1



# functions to get the gff with one line per feature
def right_size(size,max_diff,feat):
    if max_diff==0:
    return not ((size>Features[feat].size*max_diff) or (size<Features[feat].size/max_diff)) 
def create_line_target_gff(first_seg,last_seg,feature_id,size_diff,inversion,walk,cov,id):
    [chr,strand,feature]=[segments_on_target_genome[first_seg][walk][0],Features[feature_id].strand,Features[feature_id]]
    annotation=f'{feature.annot};Size_diff={size_diff};coverage={cov};sequence_ID={id}' # Nb_variants={var_count};
        start=get_feature_start_on_target_genome_inv(last_seg,feature_id,walk)
        stop=get_feature_stop_on_target_genome_inv(first_seg,feature_id,walk)
        start=get_feature_start_on_target_genome(first_seg,feature_id,walk)
        stop=get_feature_stop_on_target_genome(last_seg,feature_id,walk)
    output_line=f'{chr}\tGrAnnoT\t{feature.type}\t{start}\t{stop}\t.\t{strand}\t.\t{annotation}\n'
    return output_line
# functions to get the alignment for the transfered genes
def segment_aln(type,seg_seq,seg_a,seg_b,first,feature_id,last):
                seq_aln=get_segment_sequence(seg_seq,seg_a)[feature.pos_start-1:]
                seq_aln=get_segment_sequence(seg_seq,seg_a)[:feature.pos_stop]
                seq_aln=get_segment_sequence(seg_seq,seg_a)
            line_a=seq_aln
            line_b=seq_aln
            len_aln=len(seq_aln)
            line_c=len_aln*"*"
        case "substitution":
            seq_aln_a=get_segment_sequence(seg_seq,seg_a)
            seq_aln_b=get_segment_sequence(seg_seq,seg_b)
            len_a=len(seq_aln_a)
            len_b=len(seq_aln_b)
            if len_a>len_b:
                diff_len=len_a-len_b
                line_a=seq_aln_a
                line_b=seq_aln_b+diff_len*"-"
                line_c=len_a*" "
            else:
                diff_len=len_b-len_a
                line_a=seq_aln_a+diff_len*"-"
                line_b=seq_aln_b
                line_c=len_b*" "
        case "insertion":
            seq_aln_b=get_segment_sequence(seg_seq,seg_b)
            len_b=len(seq_aln_b)
            line_a=len_b*"-"
            line_b=seq_aln_b
            line_c=len_b*" "
        case "deletion":
                seq_aln_a=get_segment_sequence(seg_seq,seg_a)[feature.pos_start-1:]
                seq_aln_a=get_segment_sequence(seg_seq,seg_a)
            len_a=len(seq_aln_a)
            line_a=seq_aln_a
            line_b=len_a*"-"
            line_c=len_a*" "
        case "end_deletion":
            seq_aln_a=""
                seq_aln_a+=get_segment_sequence(seg_seq,segment)
            seq_aln_a+=get_segment_sequence(seg_seq,seg_a[-1])[0:feature.pos_stop] # for the last segment, only take the part that the feature is on
            len_a=len(seq_aln_a)
            line_a=seq_aln_a
            line_b=len_a*"-"
            line_c=len_a*" "

    return [line_a,line_b,line_c,False] # check the orientation of the segment later
def parse_aln_lines(line_a,line_b,line_c,feature_id):
    if (len(line_a)!=len(line_b)) or (len(line_b)!=len(line_c)):
    nb_res_a=0
    nb_res_b=0

    while len_parsed<len_to_parse:
        len_header=len(feature_id)+11
        headers=[feature_id+"_source    ",feature_id+"_target    ",len_header*" "]

        add_a=line_a[len_parsed:len_parsed+60]
        add_b=line_b[len_parsed:len_parsed+60]
        add_c=line_c[len_parsed:len_parsed+60]
        nb_res_a+=len(add_a)-add_a.count("-")
        nb_res_b+=len(add_b)-add_b.count("-")
        aln_line+=f'{headers[0]}{add_a}    {nb_res_a}\n'
        aln_line+=f'{headers[1]}{add_b}    {nb_res_b}\n'
        aln_line+=f'{headers[2]}{add_c}\n\n'
def create_line_aln(feature_path_source_genome,feature_path_target_genome,seg_seq,feature_id):
    first=True # when writing the first part of the feature, dont take the whole segment, only the part that the feature is on
    last=False # same for the last part of the feature
    while (i<len(feature_path_source_genome)) and (j<len(feature_path_target_genome)):
        if i==len(feature_path_source_genome)-1:
            last=True
        if feature_path_source_genome[i] != feature_path_target_genome[j]: # if there is a difference between the two paths
            if feature_path_target_genome[j] not in feature_path_source_genome: # if the segment in target genome is absent in source genome
                if feature_path_source_genome[i] not in feature_path_target_genome: # if the segment in source genome is absent is target genome : substitution
                    [add_a,add_b,add_c,first]=segment_aln("substitution",seg_seq,feature_path_source_genome[i],feature_path_target_genome[j],first,feature_id,last)
                else: # target genome segment not in source_genome, but source_genome segment in target genome : insertion
                    [add_a,add_b,add_c,first]=segment_aln("insertion",seg_seq,"",feature_path_target_genome[j],first,feature_id,last)
            elif feature_path_source_genome[i] not in feature_path_target_genome: # source_genome segment not in target genome, but target genome segment in source_genome : deletion
                [add_a,add_b,add_c,first]=segment_aln("deletion",seg_seq,feature_path_source_genome[i],"",first,feature_id,last)
                line_a+=add_a;line_b+=add_b;line_c+=add_c
                i+=1
            else : # if both segments are present in the other genome but not at the same position. weird case never found yet
                [add_a,add_b,add_c,first]=segment_aln("substitution",seg_seq,feature_path_source_genome[i],feature_path_target_genome[j],first,feature_id,last)
        else: # segment present in both, no variation. 
            [add_a,add_b,add_c,first]=segment_aln("identity",seg_seq,feature_path_source_genome[i],feature_path_target_genome[j],first,feature_id,last)
    if i<=len(feature_path_source_genome)-1: # if we didn't reach the length of the segment list for the first genome, the end is missing for the second genome
        [add_a,add_b,add_c,first]=segment_aln("end_deletion",seg_seq,feature_path_source_genome[i:],"",first,feature_id,last)
    return parse_aln_lines(line_a,line_b,line_c,feature_id)
NMarthe's avatar
NMarthe committed
# functions to output the stats on the transfer

def stats_feature_missing_segment(feature_missing_segments,first_seg,last_seg,list_seg,feature_id,walk):
NMarthe's avatar
NMarthe committed
# [feature_missing_first,feature_missing_middle,feature_missing_last,feature_missing_all,feature_missing_total,feature_total,feature_ok]
    feature_missing_segments[5].append(feature_id)

    if first_seg=='' : # no segment of the feature is in the genome, the feature is missing entirely
NMarthe's avatar
NMarthe committed
        feature_missing_segments[3].append(feature_id)
    elif first_seg != list_seg[0]: # the first segment is missing 
NMarthe's avatar
NMarthe committed
        feature_missing_segments[0].append(feature_id)
    elif last_seg!=list_seg[-1]: # the last segment is missing
NMarthe's avatar
NMarthe committed
        feature_missing_segments[2].append(feature_id)

    # go through all the segments, check if some are missing in the middle of the feature
    elif (len(list_seg)!=1) and (feature_id not in feature_missing_segments[3]): # to access the second to last element
NMarthe's avatar
NMarthe committed
        for segment in list_seg[1-(len(list_seg)-2)]:
            if (segment not in segments_on_target_genome) or (walk not in segments_on_target_genome[segment]):
NMarthe's avatar
NMarthe committed
                feature_missing_segments[1].append(feature_id)
                break

    # go through the segments, to see if one is missing anywhere on the feature
    for segment in list_seg:
        if (segment not in segments_on_target_genome) or (walk not in segments_on_target_genome[segment]):
NMarthe's avatar
NMarthe committed
            if feature_id not in feature_missing_segments[4]:
                feature_missing_segments[4].append(feature_id)
                break

    # if the feature doesnt have a missing segment, it is complete.         ADD THE PATH CHECK FOR INSERTIONS !!
    if feature_id not in feature_missing_segments[4]:
        feature_missing_segments[6].append(feature_id)

def get_annot_features(list_features):
    list_annot_features=[]
    for feature in list_features:
        list_annot_features.append(Features[feature].note)
    return list_annot_features

def count_hypput_total(list_annot_first):
    total=len(list_annot_first)
    count_hypput=0
    for annot in list_annot_first:
        if ("hypothetical" in annot) or ("putative" in annot):
NMarthe's avatar
NMarthe committed
            count_hypput+=1
    return [count_hypput,total]

# print stats on the transfer : number of feature that have segments in different positions missing. 
def stats_features(feature_missing_segments):
# [feature_missing_first,feature_missing_middle,feature_missing_last,feature_missing_all,feature_missing_total,feature_total,feature_ok]
    list_annot_first=get_annot_features(feature_missing_segments[0])
    [hyp_put,total]=count_hypput_total(list_annot_first)
    print("\nthe first segment is missing for", total,"features, including",round(100*(hyp_put)/total,2),"% hypothetical or putative.")

    list_annot_middle=get_annot_features(feature_missing_segments[1])
    [hyp_put,total]=count_hypput_total(list_annot_middle)
    print("a middle segment is missing for", total,"features, including",round(100*(hyp_put)/total,2),"% hypothetical or putative.")

    list_annot_last=get_annot_features(feature_missing_segments[2])
    [hyp_put,total]=count_hypput_total(list_annot_last)
    print("the last segment is missing for", total,"features, including",round(100*(hyp_put)/total,2),"% hypothetical or putative.")

    list_annot_all=get_annot_features(feature_missing_segments[3])
    [hyp_put,total]=count_hypput_total(list_annot_all)
    print(total,"features are entirely missing, including",round(100*(hyp_put)/total,2),"% hypothetical or putative.")

    list_annot_total=get_annot_features(feature_missing_segments[4])
    [hyp_put,total]=count_hypput_total(list_annot_total)
    print("there is at least one segment missing for", total,"features, including",round(100*(hyp_put)/total,2),"% hypothetical or putative.")

    list_annot_ok=get_annot_features(feature_missing_segments[6])
    [hyp_put,total]=count_hypput_total(list_annot_ok)
    print(total ,"features are entirely present in the new genome, including",round(100*(hyp_put)/total,2),"% hypothetical or putative.")

    list_annot_features=get_annot_features(feature_missing_segments[5])
    [hyp_put,total]=count_hypput_total(list_annot_features)
    print("there is", total,"features in total, including",round(100*(hyp_put)/total,2),"% hypothetical or putative.")



# functions to generate the different gffs
def get_segments_positions_on_genome(pos_seg): # add to the dict the info about the segments.
NMarthe's avatar
NMarthe committed
    bed=open(pos_seg,'r')
    lines=bed.readlines() # read line by line ?
    bed.close()
    file_name='.'.join(pos_seg.split('/')[-1].split('.')[0:-1]) # split by '.' to get the filename without the extention, then join by '.' in case there is a '.' in the filename
NMarthe's avatar
NMarthe committed
    for line in lines:
        line=line.split()
        [seg,chrom,start,stop,strand,index]=[line[3],line[0],int(line[1])+1,int(line[2]),line[3][0:1],seg_count] # +1 in the start to convert the bed 0-based coordinate to a 1-based system
        # check if segment present twice on the same walk ???
        #segments_on_target_genome[seg]=[chrom,start,stop,strand,index,file_name]
        if seg not in segments_on_target_genome:
            segments_on_target_genome[seg]={} # dict of walks->segment_info; you get the info about the segment for each walk
        segments_on_target_genome[seg][file_name]=[chrom,start,stop,strand,index]
nina.marthe_ird.fr's avatar
nina.marthe_ird.fr committed
# look for the segment on either strand of the target genome
def search_seg_on_target_genome(segment):
    inverted_segment=invert_seg(segment)
    if segment in segments_on_target_genome:
        #if inverted_segment in segments_on_target_genome:
        #    print(segment," found in both orientations")
        return segment
    elif inverted_segment in segments_on_target_genome:
        #print("inverted seg found *****")
        return inverted_segment
    else:
        return False

def get_segments_sequence(segments_file,segments_list):
    file_segments=open(segments_file,'r')
    lines_segments=file_segments.readlines()
    file_segments.close()
NMarthe's avatar
NMarthe committed
    seg_seq={}
NMarthe's avatar
NMarthe committed
        line=line.split()
        seg_id='s'+line[1]
        if seg_id in segments_list:
            seg_seq[seg_id]=line[2]
def get_paths(walks_file,target_genome):
    file_walks=open(walks_file,'r')
    lines_walks=file_walks.readlines()
    file_walks.close()
        seq_name=line[1]+"_"+line[3]
        if target_genome in seq_name: # get the walk of the genome
            path=line[6].split(',')[1:]
            list_segments=[]
            for segment in path:
                if segment[0:1]=='>':
                    list_segments.append('>s'+segment[1:])
                elif segment[0:1]=='<':
                    list_segments.append('<s'+segment[1:])
def get_first_last_seg(list_seg): # get the first segment of the list that is in the target genome
    [first_seg_found,last_seg_found,walk_found]=['','','']
    for segment in list_seg:
        seg_found=search_seg_on_target_genome(segment)
        if seg_found:
            first_seg_found=seg_found
    if first_seg_found!='':
        for segment in reversed(list_seg):
            last_seg_found=search_seg_on_target_genome(segment)
            if last_seg_found:
                walk=find_common_walk(first_seg_found,last_seg_found)
                if walk:
                    walk_found=walk
                    break

    # return first match, not the longest (for now). still a match with the first seg being the earliest possible
    return [first_seg_found,last_seg_found,walk_found]

def find_common_walk(seg1,seg2):
    for walk in segments_on_target_genome[seg1]:
        if walk in segments_on_target_genome[seg2]:
            return walk
    return False
# functions to get the detail of the variations in the features
nina.marthe_ird.fr's avatar
nina.marthe_ird.fr committed
# add the path of the feature on the target genome in the object Feature
def add_target_genome_path(feature_id,target_genome_paths):
    feature=Features[feature_id]
    list_seg=feature.segments_list_source
    [first_seg,last_seg,walk_name]=get_first_last_seg(list_seg)
        feature_path=get_feature_path(target_genome_paths[walk_name],first_seg,last_seg,walk_name)
    feature.segments_list_target=feature_path
nina.marthe_ird.fr's avatar
nina.marthe_ird.fr committed
# find the feature's path in target genome
def get_feature_path(target_genome_path,first_seg,last_seg,walk_name):
    #first_seg=search_seg_on_target_genome(first_seg)
    #last_seg=search_seg_on_target_genome(last_seg)
    first_seg_index=segments_on_target_genome[first_seg][walk_name][4]
    last_seg_index=segments_on_target_genome[last_seg][walk_name][4]
    first_index=min(first_seg_index,last_seg_index)
    last_index=max(first_seg_index,last_seg_index)
    feature_path_target_genome=target_genome_path[first_index:last_index+1]
    return feature_path_target_genome

def count_variations(feature_id):
    feature=Features[feature_id]
    target_list=feature.segments_list_target
    if len(target_list)!=0:
        source_list=feature.segments_list_source
        inversion=detect_feature_inversion(source_list,target_list)
        target_dict=dict.fromkeys(target_list,"")
        source_dict=dict.fromkeys(source_list,"") # convert list into dict to search segments in dict quicker.
        var_count=0
        for segment in source_dict:
            if segment not in target_dict:
                var_count+=1
        for segment in target_dict:
            if segment not in source_dict:
                var_count+=1
        # this counts the substitutions twice, as insertion+deletion.
    return var_count

def get_id_cov(feature_id,seg_size): # seg_size has unoriented segments : s25
    feature=Features[feature_id]
    target_list=feature.segments_list_target # make dict instead of lists ??? also in alignment function...
    source_list=feature.segments_list_source

    inversion=detect_feature_inversion(source_list,target_list)
    if inversion:
        target_list=invert_segment_list(target_list)

    [match,subs,inser,delet]=[0,0,0,0]

    [i,j]=[0,0]
    first=True # when writing the first part of the feature, dont take the whole segment, only the part that the feature is on
    last=False # same for the last part of the feature # for id and ins.

    while (i<len(source_list)) and (j<len(target_list)):
        if i==len(source_list)-1:
            last=True
        if source_list[i] != target_list[j]: # if there is a difference between the two paths
            if target_list[j] not in source_list: # if the segment in target genome is absent in source genome
                if source_list[i] not in target_list: # if the segment in source genome is absent is target genome : substitution
                    add=segment_id_cov("substitution",seg_size,source_list[i],target_list[j],first,feature,last)
                    match+=add[0];subs+=add[1];inser+=add[2];delet+=add[3];first=add[4]
                    i+=1;j+=1
                else: # target genome segment not in source_genome, but source_genome segment in target genome : insertion
                    add=segment_id_cov("insertion",seg_size,source_list[i],target_list[j],first,feature,last)
                    match+=add[0];subs+=add[1];inser+=add[2];delet+=add[3];first=add[4]
                    j+=1
            elif source_list[i] not in target_list: # source_genome segment not in target genome, but target genome segment in source_genome : deletion
                add=segment_id_cov("deletion",seg_size,source_list[i],target_list[j],first,feature,last)
                match+=add[0];subs+=add[1];inser+=add[2];delet+=add[3];first=add[4]
                i+=1
            else : # if both segments are present in the other genome but not at the same position. weird case never found yet
                add=segment_id_cov("substitution",seg_size,source_list[i],target_list[j],first,feature,last)
                match+=add[0];subs+=add[1];inser+=add[2];delet+=add[3];first=add[4]
                i+=1;j+=1
        else: # segment present in both, no variation. 
            add=segment_id_cov("identity",seg_size,source_list[i],target_list[j],first,feature,last)
            match+=add[0];subs+=add[1];inser+=add[2];delet+=add[3];first=add[4]
            i+=1;j+=1

    if i<=len(source_list)-1: # if we didn't reach the length of the segment list for the first genome, the end is missing for the second genome
        add=segment_id_cov("end_deletion",seg_size,source_list[i:],'',first,feature,last)
        match+=add[0];subs+=add[1];inser+=add[2];delet+=add[3];first=add[4]

    cov=round((match+subs)/(match+subs+delet),3)
    id=round(match/(match+subs+inser+delet),3)
    #var_count=count_variations(feature_id)
    return [cov,id]

def segment_id_cov(type,seg_size,seg_a,seg_b,first,feature,last):
    [match,subs,inser,delet]=[0,0,0,0]
    match type:
        case "identity":
            if first:
                match+=seg_size[seg_a[1:]]-feature.pos_start+1
            elif last:
                match+=feature.pos_stop
            else:
                match+=seg_size[seg_a[1:]]
        case "substitution":
            if seg_size[seg_b[1:]]!=seg_size[seg_a[1:]]: # substitution can be between segments of different size
                if seg_size[seg_b[1:]]>seg_size[seg_a[1:]]:
                    subs+=seg_size[seg_a[1:]]
                    inser+=seg_size[seg_b[1:]]-seg_size[seg_a[1:]]
                elif seg_size[seg_b[1:]]<seg_size[seg_a[1:]]:
                    subs+=seg_size[seg_b[1:]]
                    delet+=seg_size[seg_a[1:]]-seg_size[seg_b[1:]]
            else:
                subs+=seg_size[seg_a[1:]]

        case "insertion":
            inser+=seg_size[seg_b[1:]]
        case "deletion":
            if first:
                delet+=seg_size[seg_a[1:]]-feature.pos_start+1
            else:
                delet+=seg_size[seg_a[1:]]
        case "end_deletion":
            for seg in seg_a[:-1]:
                delet+=seg_size[seg[1:]]
            delet+=feature.pos_stop

    return [match,subs,inser,delet,False] # check the orientation of the segment later

    match strand:
        case "+":
        case default:
            return ""
def get_sequence_list_seg(list_seg,i,feature,seg_seq):
    for k in range(i,len(list_seg)):
        if k==len(list_seg)-1:
            del_sequence+=get_segment_sequence(seg_seq,list_seg[k])[0:feature.pos_stop]
            del_sequence+=get_segment_sequence(seg_seq,list_seg[k])
def get_segment_sequence(seg_seq,segment):
        return reverse_complement(seg_seq[segment[1:]])

def reverse_complement(sequence):
    sequence_rc=""
    for char in sequence:
        sequence_rc+=complement(char)
    return sequence_rc[::-1]

def complement(nucl):
    match nucl:
        case "A":
            return "T"
        case "C":
            return "G"
        case "G":
            return "C"
        case "T":
            return "A"
    return nucl
    def __init__(self,feature_id,feature_type,chr,start_new,stop_new,inversion,size_diff,size_new):
        self.feature_id=feature_id
        self.feature_type=feature_type
        self.chr=chr
        self.start_new=start_new
        self.stop_new=stop_new
        self.inversion=inversion
        self.size_diff=size_diff
        self.size_new=size_new
        self.type=''
        self.last_seg_in_target=''
        self.seg_ref=list()
        self.seg_alt=list()
nina.marthe_ird.fr's avatar
nina.marthe_ird.fr committed
# initiate a Variation object with the information on the feature it is on
    feature=Features[feature_id]
    # get feature paths on the original genome and on the target genome
    feature_path_target_genome=feature.segments_list_target
    feature_path_source_genome=feature.segments_list_source
    inversion=detect_feature_inversion(feature_path_source_genome,feature_path_target_genome)

        feature_path_target_genome=invert_segment_list(feature_path_target_genome)
        start_new_genome=get_feature_start_on_target_genome_inv(last_seg,feature_id,walk)
        stop_new_genome=get_feature_stop_on_target_genome_inv(first_seg,feature_id,walk)
        start_new_genome=get_feature_start_on_target_genome(first_seg,feature_id,walk)
        stop_new_genome=get_feature_stop_on_target_genome(last_seg,feature_id,walk)
    size_diff=str(size_new_genome-feature.size)
    sequence_name=segments_on_target_genome[first_seg][walk][0]
    variation=Variation(feature_id,feature.type,sequence_name,start_new_genome,stop_new_genome,inversion,size_diff,size_new_genome)
    return(variation,feature_path_source_genome,feature_path_target_genome)
nina.marthe_ird.fr's avatar
nina.marthe_ird.fr committed
# reset the informations of the variation, but keep the information about the feature
    variation.type='' # make type enumerate
    variation.size_var=0
    variation.start_var=''
def get_old_new_pos_substitution(feat_start,variation,start_feat_seg_target,feat,walk):
    seg_pos=search_segment(variation.start_var)
    pos_old=str(int(Segments[seg_pos].start)-int(feat_start))

    var_start_seg=variation.start_on_target
    if variation.inversion:
        start_feat_seg_target=invert_seg(start_feat_seg_target)
        end_var=segments_on_target_genome[var_start_seg][walk][2]
        start_feat=get_feature_start_on_target_genome_inv(start_feat_seg_target,feat,walk)
        start_var=segments_on_target_genome[var_start_seg][walk][1]
        start_feat=get_feature_start_on_target_genome(start_feat_seg_target,feat,walk)
    return [pos_old,pos_new] # pos_old and pos_new are the base before the change

def get_old_new_pos_insertion(variation,feat_start,start_feat_seg_target,feat,walk):
    seg_pos=search_segment(variation.start_var) # start_var is the segment AFTER the insertion
    pos_old=str(int(Segments[seg_pos].start)-int(feat_start))

    start_var_seg=variation.start_var
    if variation.inversion:
        start_feat_seg_target=invert_seg(start_feat_seg_target)
        end_var=segments_on_target_genome[start_var_seg][walk][2]+len(variation.alt) # start_var_seg is the segment AFTER the insertion
        start_feat=get_feature_start_on_target_genome_inv(start_feat_seg_target,feat,walk) 
        start_var=segments_on_target_genome[start_var_seg][walk][1]-len(variation.alt) # start_var_seg is the segment AFTER the insertion
        start_feat=get_feature_start_on_target_genome(start_feat_seg_target,feat,walk) 
    return [pos_old,pos_new] # pos_old and pos_new are the base before the change

def get_old_new_pos_deletion(variation,feat_start,start_feat_seg_target,feat,walk):
    seg_pos=search_segment(variation.start_var)
        pos_old=int(Segments[seg_pos].start)-int(feat_start)+Features[feat].pos_start-1
        pos_old=int(Segments[seg_pos].start)-int(feat_start)
            print("error with variation position",variation.inversion,"***")

    if variation.last_seg_in_target=="": # deletion of the beggining of the feature, so no segment placed in the new genome yet. 
        start_var_seg=variation.last_seg_in_target
        if variation.inversion:
            start_feat_seg_target=invert_seg(start_feat_seg_target)
            start_var=segments_on_target_genome[start_var_seg][walk][1]-1
            start_feat=get_feature_start_on_target_genome_inv(start_feat_seg_target,feat,walk) 
            start_var=segments_on_target_genome[start_var_seg][walk][2]+1
            start_feat=get_feature_start_on_target_genome(start_feat_seg_target,feat,walk)
    return [pos_old,pos_new] # pos_old and pos_new are the base before the change
def init_new_var(variation,type,feature_path_source_genome,feature_path_target_genome,i,j,seg_seq,feature):
    variation.start_var=feature_path_source_genome[i]
        variation.start_on_target=feature_path_target_genome[j]
        variation.ref=get_segment_sequence(seg_seq,feature_path_source_genome[i])
        variation.alt=get_segment_sequence(seg_seq,feature_path_target_genome[j])
        variation.seg_ref.append(feature_path_source_genome[i])
        variation.seg_alt.append(feature_path_target_genome[j])
    elif type=="insertion":
        variation.ref="-"
        variation.alt=get_segment_sequence(seg_seq,feature_path_target_genome[j])
        variation.seg_alt.append(feature_path_target_genome[j])
    elif type=="deletion":
        if i==0: # if the deletion is at the start of the feature, the deletion doesnt always start at the start at the first segment : 
            #use pos_start, position of the feature on its first segment
            variation.ref=get_segment_sequence(seg_seq,feature_path_source_genome[i])[feature.pos_start-1:]
            variation.seg_ref.append(feature_path_source_genome[i])
        else: # else, the deletion will always start at the start of the first segment.
            variation.ref=get_segment_sequence(seg_seq,feature_path_source_genome[i])
            variation.seg_ref.append(feature_path_source_genome[i])
def continue_var(variation,seg_seq,feature_path_source_genome,feature_path_target_genome,i,j,genome_to_continue):

    if variation.type=="substitution":
        if genome_to_continue==0: # genome_to_continue allows to choose if the substitution continues for the original or the target genome, or both.
            variation.ref+=get_segment_sequence(seg_seq,feature_path_source_genome[i])
            variation.alt+=get_segment_sequence(seg_seq,feature_path_target_genome[j])
            variation.seg_ref.append(feature_path_source_genome[i])
            variation.seg_alt.append(feature_path_target_genome[j])
            variation.ref+=get_segment_sequence(seg_seq,feature_path_source_genome[i])
            variation.seg_ref.append(feature_path_source_genome[i])
        elif genome_to_continue==2: # insertion
            variation.alt+=get_segment_sequence(seg_seq,feature_path_target_genome[j])
            variation.seg_alt.append(feature_path_target_genome[j])
    elif variation.type=="insertion":
        variation.alt+=get_segment_sequence(seg_seq,feature_path_target_genome[j])
        variation.seg_alt.append(feature_path_target_genome[j])
    elif variation.type=="deletion":
        variation.ref+=get_segment_sequence(seg_seq,feature_path_source_genome[i])
        variation.seg_ref.append(feature_path_source_genome[i])

# functions to detect inversions

# gives the list of segments from list1 that are in list2
def get_common_segments(dict1,dict2):
    list_common=[]
    for segment in dict1:
        if segment in dict2:
            list_common.append(segment)
    return list_common

# check if common segments in the two dict have the same strand
def compare_strand(dict1,dict2): # dict1 and dict2 : [seg_id]->[seg_strand]
    seg_common=get_common_segments(dict1,dict2)

    # for each segment in common, check if the strand is the same
    same_strand_count=0
    for segment in seg_common:
            same_strand_count+=1
def detect_segment_order_inversion(dict1,dict2):
    list_1_common=get_common_segments(dict1,dict2)
    list_2_common=get_common_segments(dict2,dict1) # same segments, different orders
    list_2_common_reversed=list(reversed(list_2_common))
    while i<len(list_1_common):
        if list_2_common_reversed[i]==list_1_common[i]:
    return (cpt>len(list_1_common)*0.9) # if more than 90% of the segments are on the same position when the lists are reversed, there is an inversion. 
    
def detect_orient_inversion(dict1,dict2):
    [seg_common_count,same_strand_count]=compare_strand(dict1,dict2)
    if same_strand_count>=seg_common_count*0.9: # if more than 90% of segments shared have the same strand, no inversion
        return [False,dict1,dict2]
# takes two lists of segments for two genes, check if the first list is an inversion of the second one (if the segments in common are on the opposite strand)
def detect_feature_inversion(list_1,list_2):
    # convert list into dict with unstranded segment id as key and strand as value 
    strand1=[seg[0] for seg in list_1]
    id1=[seg[1:] for seg in list_1]
    dict1 = {id1[i]: strand1[i] for i in range(len(strand1))}
    strand2=[seg[0] for seg in list_2]
    id2=[seg[1:] for seg in list_2]
    dict2 = {id2[i]: strand2[i] for i in range(len(strand2))}
    # check if we have an inversion of the orientation of the segments
    [strand_inversion,dict1,dict2]=detect_orient_inversion(dict1,dict2)

    # check if we have an inversion of the order of the segments
    segment_order_inversion=detect_segment_order_inversion(dict1,dict2)
    # if there we have both inversions, the gene is in an inverted region
    if segment_order_inversion and strand_inversion:

def invert_segment_list(seg_list):
    list_inverted=list()
    for seg in seg_list: