
    DUfd                         d dl Z d dlZd dlZd dlZ e j        de j                  fdZd Z	d Z
d Zd Zd Zdd	ZddZd ZddZd ZddZddZddZd  ej        ej                  j        ffdZ	 ddZ	 	 	 	 	 	 	 	 ddZddZd ZdS )    Nz(\d+)c                 Z    t          d |                    |           D                       S )Nc                 \    g | ])}||                                 rt          |          n|*S  )isdigitint.0xs     Q/var/www/html/software/conda/lib/python3.11/site-packages/bioframe/core/arrops.py
<listcomp>znatsort_key.<locals>.<listcomp>	   s4    PPP1aPAIIKK.#a&&&QPPP    )tuplesplit)s	_NS_REGEXs     r   natsort_keyr      s+    PP	8J8JPPPQQQr   c                 .    t          | t                    S )N)key)sortedr   )iterables    r   	natsortedr      s    (,,,,r   c                     t          j        |           } t          |           st          j        g t                    S t          t          d | D                        }t          j        |d d d                   S )Ndtypec              3   4   K   | ]}t          |          V  d S N)r   r   s     r   	<genexpr>zargnatsort.<locals>.<genexpr>   s(      55!{1~~555555r   )npasarraylenarrayr   r   ziplexsort)r"   colss     r   
argnatsortr&      so    JuEu:: 'x#&&&&55u555677D:d44R4j!!!r   c                 l    t          j        | |k              d         }|d         |d         dz   }}||fS )zDFind the first and the last occurence + 1 of the value in the array.r   r      )r   where)arrval
block_idxslohis        r   _find_block_spanr/      s;     #*%%a(J]JrNQ.Br6Mr   c                 x    t          j        | j        |j        z   f| j                  }| |ddd<   ||ddd<   |S )a\  
    Interweave two arrays.

    Parameters
    ----------
    a, b : numpy.ndarray
        Arrays to interweave, must have the same length/

    Returns
    -------
    out : numpy.ndarray
        Array of interweaved values from a and b.

    Notes
    -----
    From https://stackoverflow.com/questions/5347065/interweaving-two-numpy-arrays
    r   r   N   r(   )r   emptysizer   )abouts      r   
interweaver7       sH    $ (AFQVO%QW
5
5
5CC1IC1IJr   c                     t           j                            | t          ||                    ddd         }d|||k    <   |S )a#  
    Calculate sums of slices of an array.

    Parameters
    ----------
    arr : numpy.ndarray
    starts : numpy.ndarray
        Starts for each slice
    ends : numpy.ndarray
        Stops for each slice

    Returns
    -------
    sums : numpy.ndarray
        Sums of the slices.
    Nr1   r   )r   addreduceatr7   )r*   startsendssumss       r   
sum_slicesr>   8   sA    " 6??3
64 8 899##A#>DD4Kr   c                    |du |du k    rt          d          ||| z
  }t          j        |           r"t          j        t	          |          |           } t          j        | |          }t          j        |                                          t          j        |                                |z
  |          z
  }||z   }|S )a  
    Create concatenated ranges of integers for multiple start/length.

    Parameters
    ----------
    starts : numpy.ndarray
        Starts for each range
    stops : numpy.ndarray
        Stops for each range
    lengths : numpy.ndarray
        Lengths for each range. Either stops or lengths must be provided.

    Returns
    -------
    concat_ranges : numpy.ndarray
        Concatenated ranges.

    Notes
    -----
    See the following illustrative example:

    starts = np.array([1, 3, 4, 6])
    stops = np.array([1, 5, 7, 6])

    print arange_multi(starts, lengths)
    >>> [3 4 4 5 6]

    From: https://codereview.stackexchange.com/questions/83018/vectorized-numpy-version-of-arange-with-multiple-start-stop

    Nz)Either stops or lengths must be provided!)	
ValueErrorr   isscalarfullr!   repeatarangesumcumsum)r;   stopslengths	cat_startcat_counter	cat_ranges         r   arange_multirL   N   s    @ 	7d?++DEEE&.	{6 -UV,, 	&'**I )GKKMM**RY7"G. . K
 K'Ir   Fc                     | j         |j         cxk    r|j         cxk    r|j         k    sn t          d          |r| |k    ||k    z  S | |k     ||k     z  S )aE  
    Take pairs of intervals and test if each pair has an overlap.

    Parameters
    ----------
    starts1, ends1, starts2, ends2 : numpy.ndarray
        Interval coordinates. All four arrays must have the same size.
        Warning: if provided as pandas.Series, indices will be ignored.

    closed : bool
        If True then treat intervals as closed and accept single-point overlaps.

    Returns
    -------
    have_overlap : numpy.ndarray
        A boolean array where the i-th element says if the i-th interval in set 1
        overlaps the i-th interval in set 2.
    z.All four input arrays must have the same size.)r3   r@   )starts1ends1starts2ends2closeds        r   _check_overlaprS      sx    ( LEJDDDD',DDDD%*DDDDIJJJ 55 W%566%GeO44r   c                 n    t          j        ||          t          j        | |          z
  }d||dk     <   |S )aT  
    Take pairs of intervals and return the length of an overlap in each pair.

    Parameters
    ----------
    starts1, ends1, starts2, ends2 : numpy.ndarray
        Interval coordinates. All four arrays must have the same size.
        Warning: if provided as pandas.Series, indices will be ignored.

    Returns
    -------
    overlap_size : numpy.ndarray
        An array where the i-th element contains the length of an overlap between
        the i-th interval in set 1 and the i-th interval in set 2.
        0 if the intervals overlap by a single point, -1 if they do not overlap.
    r   r   )r   minimummaximum)rN   rO   rP   rQ   overlap_sizes        r   _size_overlaprX      s;    $ :eU++bj'.J.JJL%'L!"r   c                    | |||fD ]6}t          |t          j                  rt          j        dt
                     7t          j        |           } t          j        |          }t          j        |          }t          j        |          }t          |           }t          |          }t          j	        | |g          }	t          j	        ||g          }
t          j	        t          j
        d|dz              t          j
        d|dz             g          }t          j        |
|	g          }|	|         |
|         ||         }}
}	t          j
        d||z             }t          j        |	|
|rdnd          }||dz   k    }||         ||         }}t          j        t          j        ||         ||z
  dz
            |t          |dz   |                   g          j        }||dddf         |dddf         z  dk             }|                    d           |dddf         dz  dz
  |dddf<   |dddf         dz
  |dddf<   |r0|t          j        |dddf         |dddf         g                   }|S )	ak  
    Take two sets of intervals and return the indices of pairs of overlapping intervals.

    Parameters
    ----------
    starts1, ends1, starts2, ends2 : numpy.ndarray
        Interval coordinates. Warning: if provided as pandas.Series, indices
        will be ignored.

    closed : bool
        If True, then treat intervals as closed and report single-point overlaps.

    Returns
    -------
    overlap_ids : numpy.ndarray
        An Nx2 array containing the indices of pairs of overlapping intervals.
        The 1st column contains ids from the 1st set, the 2nd column has ids
        from the 2nd set.

    MOne of the inputs is provided as pandas.Series and its index will be ignored.r(   r   rightleftNr   )axis)
isinstancepdSerieswarningswarnSyntaxWarningr   r    r!   concatenaterD   r$   searchsortedvstackrC   rL   Tsort)rN   rO   rP   rQ   rR   rh   vecn1n2r;   r<   idsordermatch_starts
match_ends
match_maskoverlap_idss                    r   _overlap_intervals_legacyrr      s   , /  c29%% 	M#   j!!GJuEj!!GJuE 
WB	WB^Wg.//F>5%.))D .29QQ///1b1f1E1EF
G
GC Jf~&&EutE{CJ#DF 9QR((L&/LwwfMMJ lQ..J+J7J9O*L )Ic,'l)BQ)FGG\A-z::;	
 
   k!!!Q$/+aaad2CCqHIK " $AAAqD)R014K1#AAAqD)A-K1  V!"*k!!!Q$.?QQQPQTAR-S"T"TUr   c                 Z    |                                 }||| k    xx         dz  cc<   | |gS )aj  
    Convert points to len1 segments for internal use in overlap().
    This enables desired overlap behavior for points and preserves
    behavior for semi-open intervals of len>=1.
    Parameters
    ----------
    starts, ends : numpy.ndarray

    Returns
    -------
    pseudo_ends : numpy.ndarray
    An array of pseudo-ends for overlapping intervals.
    r(   )copy)r;   r<   pseudo_endss      r    _convert_points_to_len1_segmentsrv     s<     ))++K1$K  r   c           	      t   | |||fD ]6}t          |t          j                  rt          j        dt
                     7t          j        |           } t          j        |          }t          | |          \  } }t          j        |          }t          j        |          }t          ||          \  }}t          |           }t          |          }t          j
        d|          }	t          j
        d|          }
t          j        || g          }t          j        ||g          }| |         ||         |	|         }	}} ||         ||         |
|         }
}}t          j        || d          }t          j        |||rdnd          }t          j        | |d          }t          j        | ||rdnd          }||k    }||k    }||         ||         }}||         ||         }}t          j        t          j        |	|         ||z
            dddf         |
t          ||                   dddf         g|	t          ||                   dddf         t          j        |
|         ||z
            dddf         gg          }|r0|t          j        |dddf         |dddf         g                   }|S )aj  
    Take two sets of intervals and return the indices of pairs of overlapping intervals.

    Parameters
    ----------
    starts1, ends1, starts2, ends2 : numpy.ndarray
        Interval coordinates. Warning: if provided as pandas.Series, indices
        will be ignored.

    closed : bool
        If True, then treat intervals as closed and report single-point overlaps.
    Returns
    -------
    overlap_ids : numpy.ndarray
        An Nx2 array containing the indices of pairs of overlapping intervals.
        The 1st column contains ids from the 1st set, the 2nd column has ids
        from the 2nd set.

    rZ   r   r\   r[   Nr(   )r^   r_   r`   ra   rb   rc   r   r    rv   r!   rD   r$   re   blockrC   rL   )rN   rO   rP   rQ   rR   rh   ri   rj   rk   ids1ids2order1order2match_2in1_startsmatch_2in1_endsmatch_1in2_startsmatch_1in2_endsmatch_2in1_maskmatch_1in2_maskrq   s                       r   overlap_intervalsr     s   * /  c29%% 	M#   j!!GJuE5guEENGUj!!GJuE5guEENGU 
WB	WB9QD9QD Z())FZ())F"6?E&M4<DUG"6?E&M4<DUG &AAogu6SggVTTO'BBogu6SggVTTO &(99O%(99O/*( '
 	/*( ' ( 	$/CT1TUUAAtG \"3_EEFqqq$wO	 \"3_EEFqqq$wO	$/CT1TUUAAtG	
 K"  V!"*k!!!Q$.?QQQPQTAR-S"T"TUr   c                 P   t          | ||||          }t          j        t          j        |dddf         | j        d                   dk              d         }t          j        t          j        |dddf         |j        d                   dk              d         }|||fS )a  
    Take two sets of intervals and return the indices of pairs of overlapping intervals,
    as well as the indices of the intervals that do not overlap any other interval.

    Parameters
    ----------
    starts1, ends1, starts2, ends2 : numpy.ndarray
        Interval coordinates. Warning: if provided as pandas.Series, indices
        will be ignored.

    closed : bool
        If True, then treat intervals as closed and report single-point overlaps.

    Returns
    -------
    overlap_ids : numpy.ndarray
        An Nx2 array containing the indices of pairs of overlapping intervals.
        The 1st column contains ids from the 1st set, the 2nd column has ids
        from the 2nd set.

    no_overlap_ids1, no_overlap_ids2 : numpy.ndarray
        Two 1D arrays containing the indices of intervals in sets 1 and 2
        respectively that do not overlap with any interval in the other set.

    )rR   Nr   )	minlengthr(   )r   r   r)   bincountshape)rN   rO   rP   rQ   rR   ovidsno_overlap_ids1no_overlap_ids2s           r   overlap_intervals_outerr   |  s    6 guguVLLLEh
E!!!Q$K7=+;<<<A 	O h
E!!!Q$K7=+;<<<A 	O /?22r   c                 &   | |fD ]6}t          |t          j                  rt          j        dt
                     7t          j        |           } t          j        |          }t          j        || g          }| |         ||         }} t          j	        
                    |          }t          j        t          |           dz   t                    }d|d<   d|d<   || dd         |dd         |z   k    |dd<   n| dd         |dd         k    |dd<   t          j        |          dd         dz
  }t          j        | j        d         d          }|||<   | dd         |dd                  }|dd         |dd                  }	|||	fS )a	  
    Merge overlapping intervals.

    Parameters
    ----------
    starts, ends : numpy.ndarray
        Interval coordinates. Warning: if provided as pandas.Series, indices
        will be ignored.

    min_dist : float or None
        If provided, merge intervals separated by this distance or less.
        If None, do not merge non-overlapping intervals. Using
        min_dist=0 and min_dist=None will bring different results.
        bioframe uses semi-open intervals, so interval pairs [0,1) and [1,2)
        do not overlap, but are separated by a distance of 0. Such intervals
        are not merged when min_dist=None, but are merged when min_dist=0.

    Returns
    -------
    cluster_ids : numpy.ndarray
        The indices of interval clusters that each interval belongs to.
    cluster_starts : numpy.ndarray
    cluster_ends : numpy.ndarray
        The spans of the merged intervals.

    Notes
    -----
    From
    https://stackoverflow.com/questions/43600878/merging-overlapping-intervals/58976449#58976449
    rZ   r(   r   Tr   r   N)r^   r_   r`   ra   rb   rc   r   r    r$   rV   
accumulatezerosr!   boolrF   rB   r   )
r;   r<   min_distri   rm   cluster_borderscluster_ids_sortedcluster_idscluster_startscluster_endss
             r   merge_intervalsr     s   @ ~  c29%% 	M#   ZF:dDJf~&&E%=$u+DF:  &&Dhs6{{Qd;;;OOAOB &qrr
T#2#Y-A A" &qrr
d3B3i 7"?33CRC81<'&,q/2..K+KAAAYss34N7?122./L44r   c                    t          | |d          \  }}}t          j        ||d         d          }t          j        ||d         d          }|||         }|||         }t          j        |d         |f         }t          j        ||d         f         }	|d         |	d         k    rdnd}|d         |	d         k    rdnd }|||         }|	||         }	||	fS )Nr   )r   r[   r(   r\   r   )r   r   re   r_)
r;   r<   bounds_merged_startsmerged_endsr-   r.   complement_startscomplement_endss
             r   complement_intervalsr     s    
 %4FD1$M$M$M!A}k	fQi	9	9B	q	6	:	:B!"R%(Mbe$K fQi45eM6!945O #q'999B!"%)<<<4B)"R%0%be,Oo--r   r(   c                 "   | |||fD ]6}t          |t          j                  rt          j        dt
                     7t          j        |           } t          j        |          }t          j        |          }t          j        |          }| j        d         }|j        d         }	t          j	        dt                    }
|dk    r|dk    r|t          j        |          }nt          j        | |g          }t          j        d|	          |         }||         }t          j        || d          }t          j        ||z
  d          }t          j        t          j        |          ||z
            }t#          ||          }t          j        |||         g          j        }
n|dk    r|dk    r|t          j        |          }nt          j        ||g          }t          j        d|	          |         }||         }t          j        ||d          }t          j        ||z   |	          }t          j        t          j        |          ||z
            }t#          ||          }t          j        |||         g          j        }
|
S )aY  
    For every interval in set 1, return the indices of k closest intervals
    from set 2 to the left from the interval (with smaller coordinate).
    Overlapping intervals from set 2 are not reported, unless they overlap by
    a single point.

    Parameters
    ----------
    starts1, ends1, starts2, ends2 : numpy.ndarray
        Interval coordinates. Warning: if provided as pandas.Series, indices
        will be ignored.

    direction : str ("left" or "right")
        Orientation of closest interval search

    tie_arr : numpy.ndarray or None
        Extra data describing intervals in set 2 to break ties when multiple
        intervals are located at the same distance. An interval with the
        *lowest* value is selected.

    k : int
        The number of neighbors to report.

    Returns
    -------
    ids: numpy.ndarray
        One Nx2 array containing the indices of pairs of closest intervals,
        reported for the neighbors in specified direction (by genomic
        coordinate). The two columns are the inteval ids from set 1, ids of
        the closest intevals from set 2.

    rZ   r   r   r1   r   r\   Nr[   )r^   r_   r`   ra   rb   rc   r   r    r   r   r   argsortr$   rD   re   rV   rC   rL   rf   rg   rU   )rN   rO   rP   rQ   	directiontie_arrkri   rj   rk   rl   ends2_sort_orderids2_endsortedends2_sortedleft_closest_endidxleft_closest_startidxint1_idsint2_sorted_idsstarts2_sort_orderids2_startsortedstarts2_sortedright_closest_startidxright_closest_endidxs                          r   _closest_intervals_nooverlapr     s   H /  c29%% 	M1   j!!GJuEj!!GJuE	q	B	q	B
(6
%
%
%C1uuf$$?!z%00!zG8U*;<<1b))*:;-. olGWMM "
+>+BA F F9RYr]],?BW,WXX&'<>QRRi/
 
  	 
Q9''?!#G!4!4!#Wg,>!?!?9Q++,>? !34!#!O!O!z*@1*DbII9IbMM/2HH
 
 ''=?STTi 1
 
  	 Jr   c
           	         |rt          j        dt                    }
nM|9|7| |}}t          | |||          }
|
|
dddf         |
dddf         k             }
nt          | |||          }
t	          |           }t          j        |          }t          | |	         ||	         ||d||rdn|          }t          | |	         ||	         ||d||rdn|          }t          | |	          ||	          ||d||rdn|          }t          | |	          ||	          ||d||rdn|          }||	         |dddf                  |dddf<   ||	         |dddf                  |dddf<   ||	          |dddf                  |dddf<   ||	          |dddf                  |dddf<   t          j        ||g          }t          j        ||g          }| |dddf                  ||dddf                  z
  dz   }||dddf                  ||dddf                  z
  dz   }t          j        |||
g          }t          j        ||t          j        |
j	        d                   g          }t	          |          dk    rt          j
        dt                    S |,t          j        |dddf         ||dddf         g          }n,t          j        |dddf         |||dddf         g          }||dd	f         }|dd
df         |dddf         k    }t          j        t           j        d|df                   d         }|dd
         }|dd         }|t          |t          j        |||z
                               }|S )a  
    For every interval in set 1, return the indices of k closest intervals from set 2.

    Parameters
    ----------
    starts1, ends1, starts2, ends2 : numpy.ndarray
        Interval coordinates. Warning: if provided as pandas.Series, indices
        will be ignored. If start2 and ends2 are None, find closest intervals
        within the same set.

    k : int
        The number of neighbors to report.

    tie_arr : numpy.ndarray or None
        Extra data describing intervals in set 2 to break ties when multiple intervals
        are located at the same distance. Intervals with *lower* tie_arr values will
        be given priority.

    ignore_overlaps : bool
        If True, ignore set 2 intervals that overlap with set 1 intervals.

    ignore_upstream, ignore_downstream : bool
        If True, ignore set 2 intervals upstream/downstream of set 1 intervals.

    direction : numpy.ndarray with dtype bool or None
        Strand vector to define the upstream/downstream orientation of the intervals.

    Returns
    -------
    closest_ids : numpy.ndarray
        An Nx2 array containing the indices of pairs of closest intervals.
        The 1st column contains ids from the 1st set, the 2nd column has ids
        from the 2nd set.

    r   r   Nr   r(   r\   )r   r   r   r[   r1   r   T)rH   )r   r   r   r   r!   rD   r   rd   rf   r   r2   r$   r)   r   rL   rU   )rN   rO   rP   rQ   r   r   ignore_overlapsignore_upstreamignore_downstreamr   rq   nall_idsids_left_upstreamids_right_downstreamids_right_upstreamids_left_downstreamleft_ids	right_ids
left_distsright_distsclosest_idsclosest_distsrm   interval1_run_border_maskinterval1_run_bordersinterval1_run_startsinterval1_run_endss                               r   closest_intervalsr   f  s   b  HhvS111
/ %'GG!+aaad"3{111a47H"HI'GG 	GAillG 5	i
%!!A   8	i 
'!!a   6
yj
%!!A   7
yj 
'!!a   &i01B111a41HIaaad!(!34HA4N!OA '
 34G14M N1&	z23Eaaad3KLqqq!t~02EFGGH 24HIJJI !!!Q$(5!!!Q$+@@1DJ)AAAqD/*U9QQQT?-CCaGK)Xy+>??KN	["(;+<Q+?"@"@A M ;1xc**** 
K1-}k!!!Q$>OPQQ
AAAAqD8IJ
 
 eRaRi(K !,CRCF 3{122q57I IHRU41JD+P%QRRSTU0"5.qrr2 Jq"47K"KLL	
 	
 	
K r   c                 *   | j         d         }| t          j        |t          j                  }t          j        | |f         }t          j        |d|z  f         }t          j        |          }||         }t          j        ||                   }||fS )Nr   r   r   )r   r   onesint64r   r   rF   )r;   r<   weightsr   borderscoverage_changeborders_ordercoverages           r   coverage_intervals_rler     s    QA'!28,,,eFDL!GeGR'\12OJw''Mm$Gy788HHr   c                    | j         d         }t          j        | |f         }t          j        || z
  || z
  f         }t          j        t          j        |           dt          j        |          z  f         }t          j        t          j        d|dz             dt          j        d|dz             z  f         }t          j        | ||g          }||         ||         }}t          j        dt                    }dt          j        |t          j	                  z  }	t          ||          D ]\  }
}t          j        |          dz
  }|dk    rt|                                |j         d         k    r&t          j        |t          j        |          f         }t          j        |           d         d         }||	|<   d||<   |dk     rd||	|         <   |	S )Nr   r   r(   r1   r   TF)r   r   r   	ones_likerD   r$   r   r   r   r   r#   absrE   
zeros_liker)   )r;   r<   r   r   lensborder_types
border_idsborder_order	occupancylevelsborder	border_idinterval_id	new_levels                 r   stack_intervalsr     s   QAeFDL!G5v-.D5f--rBL4F4F/FFGLryAE**B1a!e1D1D,DDEJ:ulG<==L!,/L1IZG$'''I"'!28,,,,F *55 	3 	3	fY''!+q==}})/!"444E)R]9-E-E"EF	),,Q/2I"+F;#'Ii q==-2If[)*Mr   )NN)F)FF)r   )Nr(   )NNr(   NFFFNr   )rera   numpyr   pandasr_   compileUr   r   r&   r/   r7   r>   rL   rS   rX   rr   rv   r   r   r   iinfor   maxr   r   r   r   r   r   r   r   <module>r      s   				          (RZ"$77 R R R R- - -" " "    0  ,4 4 4 4n5 5 5 5:  .P P P Pf! ! !&Z Z Z Zz"3 "3 "3 "3J?5 ?5 ?5 ?5J xrx!!%&. . . .2 @Ah h h h\ 
R R R Rj        r   