B
    dDG                 @   s   d Z ddlZddlmZ ddlmZ ddlmZ ddlm	Z	 dZ
efdd	Zefd
dZedfddZdefddZefddZefddZdd Zdd ZefddZd&ddZdd Zd d! Zd"d# Zd$d% ZdS )'zG
This module provides additional utilities for use with ligo.segments.
    N)LIGOTimeGPS)
CacheEntry)segments)__version__z"Kipp Cannon <kipp.cannon@ligo.org>c             C   sd   t d}t }xL| D ]D}|| d\\}}||}||}|t|||  qW |S )a  
	Return a segmentlist describing the intervals spanned by the files
	whose names are given in the list filenames.  The segmentlist is
	constructed by parsing the file names, and the boundaries of each
	segment are coerced to type coltype.

	The file names are parsed using a generalization of the format
	described in Technical Note LIGO-T010150-00-E, which allows the
	start time and duration appearing in the file name to be
	non-integers.

	NOTE:  the output is a segmentlist as described by the file names;
	if the file names are not in time order, or describe overlaping
	segments, then thusly shall be the output of this function.  It is
	recommended that this function's output be coalesced before use.
	z-([\d.]+)-([\d.]+)\.[\w_+#]+\Zz.gz)	recompiler   segmentlistfindallstriprstripappendsegment)	filenamescoltypepatternlnamesd r   `/work/yifan.wang/ringdown/master-ringdown-env/lib/python3.7/site-packages/ligo/segments/utils.pyfromfilenames:   s    

r   c                s   t  fdd| D S )a  
	Construct a segmentlist representing the times spanned by the files
	named in the LAL cache contained in the file object cachefile.  The
	segmentlist will be created with segments whose boundaries are of
	type coltype, which should raise ValueError if it cannot convert
	its string argument.

	Example:

	>>> from lal import LIGOTimeGPS
	>>> cache_seglists = fromlalcache(open(filename), coltype = LIGOTimeGPS).coalesce()

	See also:

	lal.utils.CacheEntry
	c             3   s   | ]}t | d jV  qdS ))r   N)r   r   ).0r   )r   r   r   	<genexpr>k   s    zfromlalcache.<locals>.<genexpr>)r   r   )Z	cachefiler   r   )r   r   fromlalcacheZ   s    r   Tc             C   s  t dt j}t d}t d}t d}d}t }xv| D ]l}	|d|	}	|	sXq@yH||	\}
t|
d }tt	t
||
dd	 }||
d	 }d
}W n tk
r\   y<||	\}
tt	t
||
dd }||
d }d	}W nh tk
rV   y8||	\}
tt	t
||
dd }t|}d}W n tk
rP   P Y nX Y nX Y nX |rt||kr~td|	 |dkr|}n||krtd|	 || q@W |S )a  
	Read a segmentlist from the file object fileobj containing a segwizard
	compatible segment list.  Parsing stops on the first line that
	cannot be parsed (which is consumed).  The segmentlist will be
	created with segment whose boundaries are of type coltype, which
	should raise ValueError if it cannot convert its string argument.
	Two-column, three-column, and four-column segwizard files are
	recognized, but the entire file must be in the same format, which
	is decided by the first parsed line.  If strict is True and the
	file is in three- or four-column format, then each segment's
	duration is checked against that column in the input file.

	NOTE:  the output is a segmentlist as described by the file;  if
	the segments in the input file are not coalesced or out of order,
	then thusly shall be the output of this function.  It is
	recommended that this function's output be coalesced before use.
	z\s*([#;].*)?\Zz%\A\s*([\d.+-eE]+)\s+([\d.+-eE]+)\s*\Zz4\A\s*([\d.+-eE]+)\s+([\d.+-eE]+)\s+([\d.+-eE]+)\s*\Zz>\A\s*([\d]+)\s+([\d.+-eE]+)\s+([\d.+-eE]+)\s+([\d.+-eE]+)\s*\ZN r               z#segment '%s' has incorrect durationzsegment '%s' format mismatch)r   r   DOTALLr   r   subr	   intr   listmap
ValueErrorabsr   )fileobjr   strictZ
commentpatZtwocolsegpatZthreecolsegpatZfourcolsegpatformatr   linetokensnumsegdurationZthis_line_formatr   r   r   fromsegwizards   sN    




r/   c             C   sb   |r|  d xNt|D ]B\}}|  d|t||d t||d t|t|f  qW dS )a  
	Write the segmentlist seglist to the file object fileobj in a
	segwizard compatible format.  If header is True, then the output
	will begin with a comment line containing column names.  The
	segment boundaries will be coerced to type coltype and then passed
	to str() before output.
	z## seg	start    	stop     	duration
z%d	%s	%s	%s
r   r   N)write	enumeratestrr&   )r'   seglistheaderr   nr-   r   r   r   tosegwizard   s    
r6   c             C   sn   t d}t }xV| D ]N}y2||\}|ttt||dd  W q t	k
rd   P Y qX qW |S )a  
	Read a segmentlist from the file object fileobj containing TAMA
	locked-segments data.  Parsing stops on the first line that cannot
	be parsed (which is consumed).  The segmentlist will be created
	with segments whose boundaries are of type coltype, which should
	raise ValueError if it cannot convert its string argument.

	NOTE:  TAMA locked-segments files contain non-integer start and end
	times, so the default column type is set to LIGOTimeGPS.

	NOTE:  the output is a segmentlist as described by the file;  if
	the segments in the input file are not coalesced or out of order,
	then thusly shall be the output of this function.  It is
	recommended that this function's output be coalesced before use.
	z2\A\s*\S+\s+\S+\s+\S+\s+([\d.+-eE]+)\s+([\d.+-eE]+)r   r   )
r   r   r   r   r	   r   r   r#   r$   r%   )r'   r   Z
segmentpatr   r*   r+   r   r   r   fromtama   s    

&r7   c             C   s   t dgt|  }xt| D ]\}}|d}t|dkrZ||d }t ||||< qt|dkrnt||d dkrt j|d< n||d |d< |d dkrt j|d< n||d |d< t |d |d ||< qW |S )a  
	Parse a list of ranges expressed as strings in the form "value" or
	"first:last" into an equivalent ligo.segments.segmentlist.  In the
	latter case, an empty string for "first" and(or) "last" indicates a
	(semi)infinite range.  A typical use for this function is in
	parsing command line options or entries in configuration files.

	NOTE:  the output is a segmentlist as described by the strings;  if
	the segments in the input file are not coalesced or out of order,
	then thusly shall be the output of this function.  It is
	recommended that this function's output be coalesced before use.

	Example:

	>>> text = "0:10,35,100:"
	>>> from_range_strings(text.split(","))
	[segment(0, 10), segment(35, 35), segment(100, infinity)]
	N:r   r   r   r   )	r   r   lenr1   splitr   r%   NegInfinityPosInfinity)ranges	boundtypeZsegsirangepartsr   r   r   from_range_strings   s"    
rB   c             C   s  dgt |  }xt| D ]\}}|s6t|d ||< q|d tjkr\|d tjkr\d||< q|d tjkr|d tjk	rdt|d  ||< q|d tjk	r|d tjkrdt|d  ||< q|d tjk	r|d tjk	rdt|d t|d f ||< qt|qW |S )a  
	Turn a segment list into a list of range strings as could be parsed
	by from_range_strings().  A typical use for this function is in
	machine-generating configuration files or command lines for other
	programs.

	Example:

	>>> from ligo.segments import *
	>>> segs = segmentlist([segment(0, 10), segment(35, 35), segment(100, infinity())])
	>>> ",".join(to_range_strings(segs))
	'0:10,35,100:'
	Nr   r   r8   z:%sz%s:z%s:%s)r9   r1   r2   r   r;   r<   r%   )r3   r=   r?   r-   r   r   r   to_range_strings  s    
"rC   c             C   s   d dd |  D S )a  
	Return a string representation of a segmentlistdict object.  Each
	segmentlist in the dictionary is encoded using to_range_strings()
	with "," used to delimit segments.  The keys are converted to
	strings and paired with the string representations of their
	segmentlists using "=" as a delimiter.  Finally the key=value
	strings are combined using "/" to delimit them.

	Example:

	>>> from ligo.segments import *
	>>> segs = segmentlistdict({"H1": segmentlist([segment(0, 10), segment(35, 35), segment(100, infinity())]), "L1": segmentlist([segment(5, 15), segment(45, 60)])})
	>>> segmentlistdict_to_short_string(segs)
	'H1=0:10,35,100:/L1=5:15,45:60'

	This function, and its inverse segmentlistdict_from_short_string(),
	are intended to be used to allow small segmentlistdict objects to
	be encoded in command line options and config files.  For large
	segmentlistdict objects or when multiple sets of segmentlists are
	required, the LIGO Light Weight XML encoding available through the
	ligo.lw library should be used.
	/c             S   s*   g | ]"\}}d t |dt|f qS )z%s=%s,)r2   joinrC   )r   keyvaluer   r   r   
<listcomp>K  s    z3segmentlistdict_to_short_string.<locals>.<listcomp>)rF   items)seglistsr   r   r   segmentlistdict_to_short_string4  s    rL   c             C   sT   t  }xF|  dD ]4}| d\}}t| d|d|| < qW |S )a^  
	Parse a string representation of a set of named segmentlists into a
	segmentlistdict object.  The string encoding is that generated by
	segmentlistdict_to_short_string().  The optional boundtype argument
	will be passed to from_range_strings() when parsing the segmentlist
	objects from the string.

	Example:

	>>> segmentlistdict_from_short_string("H1=0:10,35,100:/L1=5:15,45:60")
	{'H1': [segment(0, 10), segment(35, 35), segment(100, infinity)], 'L1': [segment(5, 15), segment(45, 60)]}

	This function, and its inverse segmentlistdict_to_short_string(),
	are intended to be used to allow small segmentlistdict objects to
	be encoded in command line options and config files.  For large
	segmentlistdict objects or when multiple sets of segmentlists are
	required, the LIGO Light Weight XML encoding available through the
	ligo.lw library should be used.
	rD   =rE   )r>   )r   Zsegmentlistdictr
   r:   rB   )r   r>   r   tokenrG   r=   r   r   r   !segmentlistdict_from_short_stringN  s
    "rO   r   c             c   sz   t | } d}xht| rj|d }zxt| r4|d7 }q"W W d|| |krdt|||  |||  V  X |}|d7 }qW dS )a  
	Convert consecutive True values in a bit stream (boolean-castable
	iterable) to a stream of segments. Require minlen consecutive True
	samples to comprise a segment.

	Example:

	>>> list(from_bitstream((True, True, False, True, False), 0, 1))
	[segment(0, 2), segment(3, 4)]
	>>> list(from_bitstream([[], [[]], [[]], [], []], 1013968613, 0.125))
	[segment(1013968613.125, 1013968613.375)]
	r   r   N)iternextr   r   )Z	bitstreamstartdtZminlenr?   jr   r   r   from_bitstreami  s    
 rU   c             C   sT   t | d }||d d 8 }t | d d }tdd t||dD t| g@ S )a  
	Return a segmentlist identifying the S2 playground times within the
	interval defined by the segment extent.

	Example:

	>>> from ligo import segments
	>>> S2playground(segments.segment(874000000, 874010000))
	[segment(874000013, 874000613), segment(874006383, 874006983)]
	r   iw+i  r   c             s   s   | ]}t ||d  V  qdS )iX  N)r   r   )r   tr   r   r   r     s    zS2playground.<locals>.<genexpr>)r"   r   r   r@   )Zextentlohir   r   r   S2playground  s    rY   c             c   sD   d}| }x6|| ||   }}||kr&P t ||V  |d7 }q
W dS )a  
	Analogous to Python's range() builtin, this generator yields a
	sequence of continuous adjacent segments each of length "period"
	with the first starting at "start" and the last ending not after
	"stop".  Note that the segments generated do not form a coalesced
	list (they are not disjoint).  start, stop, and period can be any
	objects which support basic arithmetic operations.

	Example:

	>>> from ligo.segments import *
	>>> segmentlist(segmentlist_range(0, 15, 5))
	[segment(0, 5), segment(5, 10), segment(10, 15)]
	>>> segmentlist(segmentlist_range('', 'xxx', 'x'))
	[segment('', 'x'), segment('x', 'xx'), segment('xx', 'xxx')]
	r   N)r   r   )rR   stopZperiodr5   bar   r   r   segmentlist_range  s    r]   c             c   s0   x*|D ]"}| t |g@ |d  V  qW dS )a  
	An iterator that generates the results of taking the intersection
	of seglist1 with each segment in seglist2 in turn.  In each result,
	the segment start and stop values are adjusted to be with respect
	to the start of the corresponding segment in seglist2.  See also
	the segmentlist_range() function.

	This has use in applications that wish to convert ranges of values
	to ranges relative to epoch boundaries.  Below, a list of time
	intervals in hours is converted to a sequence of daily interval
	lists with times relative to midnight.

	Example:

	>>> from ligo.segments import *
	>>> x = segmentlist([segment(0, 13), segment(14, 20), segment(22, 36)])
	>>> for y in Fold(x, segmentlist_range(0, 48, 24)): print y
	...
	[segment(0, 13), segment(14, 20), segment(22, 24)]
	[segment(0, 12)]
	r   N)r   r   shift)Zseglist1Zseglist2r-   r   r   r   Fold  s    
r_   c                s   |dk rt  S dd   fdd}t  }d}x~|| D ]r\}}|dkrl|| |  krb|k rln n|}n8|dk r||  kr|| k rn n|t || ~||7 }q:W |dkst|S )a  
	Given a sequence of segmentlists, returns the intervals during
	which at least n of them intersect.  The input segmentlists must be
	coalesced, the output is coalesced.

	Example:

	>>> from ligo.segments import *
	>>> w = segmentlist([segment(0, 15)])
	>>> x = segmentlist([segment(5, 20)])
	>>> y = segmentlist([segment(10, 25)])
	>>> z = segmentlist([segment(15, 30)])
	>>> vote((w, x, y, z), 3)
	[segment(10, 20)]

	The sequence of segmentlists is only iterated over once, and the
	segmentlists within it are only iterated over once;  they can all
	be generators.  If there are a total of N segments in M segment
	lists and the final result has L segments the algorithm is O(N M) +
	O(L).
	r   c             S   s`   t | dd d}xBtt| d ddD ]*}| | d d |d d kr&| |S q&W ds\td S )Nc             S   s   | d d S )Nr   r   )_r   r   r   <lambda>       z'vote.<locals>.pop_min.<locals>.<lambda>)rG   r   r   F)minr@   r9   popAssertionError)r   valr?   r   r   r   pop_min  s
    zvote.<locals>.pop_minc       	   	   3   s0  g }x`| D ]X}t |}yt|}W n tk
r8   w
Y nX ||d d|f ||d dd f q
W |snd S |jddd d |d d }d}x|r  |\}}}||kr||7 }n||fV  |}|}|d k	ryt|}W n tk
r   wY nX ||d d|f ||d dd f qW ||fV  d S )Nr   rc   r   Tc             S   s   | d d S )Nr   r   )r`   r   r   r   ra     rb   z.vote.<locals>.vote_generator.<locals>.<lambda>)reverserG   )rP   rQ   StopIterationr   sort)	rK   queuer3   Zsegiterr-   boundvotesZ
this_bounddelta)rh   r   r   vote_generator  s:    


zvote.<locals>.vote_generatorr   )r   r   r   r   rf   )rK   r5   rp   resultrn   rm   ro   rR   r   )rh   r   vote  s    &$$rr   )r   )__doc__r   Zlalr   Z	lal.utilsr   Zligor   Zligo.segmentsr   
__author__r"   r   r   r/   r6   r7   rB   rC   rL   rO   rU   rY   r]   r_   rr   r   r   r   r   <module>   s(    = -$
&$