U
    e»{jO  ã                   @   sð   d Z ddlZddlZddlZddlmZ ddlZddlm	Z	 ddl
mZ ddlmZmZ d dd	„Zd!d
d„Zdd„ Zd"dd„Zd#dd„Zdd„ Zd$dd„Zd%dd„ZG dd„ dejƒZG dd„ dejƒZG dd„ dejƒZG dd„ dejƒZdS )&zÛ
This package contains functions for reading and writing QDP tables that are
not meant to be used directly, but instead are available as readers/writers in
`astropy.table`. See :ref:`astropy:table_io` for more details.
é    N)ÚIterable)ÚTable)ÚAstropyUserWarningé   )ÚbasicÚcorec                 C   sÜ   d}d}|}|dkrd}d|› d�}d|› d|› d|› d	�}d
|› d|› d|› d�}t  |¡}|  ¡ } | sndS | | ¡}	|	dkrŽtd| › �ƒ‚|	 ¡  ¡ D ]<\}
}|dkr¬qš|
dkrÎdt|j|d�ƒ› �  S |
  S qšdS )aÿ  Interpret a QDP file line

    Parameters
    ----------
    line : str
        a single line of the file

    Returns
    -------
    type : str
        Line type: "comment", "command", or "data"

    Examples
    --------
    >>> _line_type("READ SERR 3")
    'command'
    >>> _line_type(" \n    !some gibberish")
    'comment'
    >>> _line_type("   ")
    'comment'
    >>> _line_type(" 21345.45")
    'data,1'
    >>> _line_type(" 21345.45 1.53e-3 1e-3 .04 NO nan")
    'data,6'
    >>> _line_type(" 21345.45,1.53e-3,1e-3,.04,NO,nan", delimiter=',')
    'data,6'
    >>> _line_type(" 21345.45 ! a comment to disturb")
    'data,1'
    >>> _line_type("NO NO NO NO NO")
    'new'
    >>> _line_type("NO,NO,NO,NO,NO", delimiter=',')
    'new'
    >>> _line_type("N O N NOON OON O")
    Traceback (most recent call last):
        ...
    ValueError: Unrecognized QDP line...
    >>> _line_type(" some non-comment gibberish")
    Traceback (most recent call last):
        ...
    ValueError: Unrecognized QDP line...
    z'[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?zREAD [TS]ERR(\s+[0-9]+)+Nz\s+zNO(zNO)+ú(z|NO|[-+]?nan)(z|NO|[-+]?nan))*)z^\s*((?P<command>z
)|(?P<new>z)|(?P<data>z)?\s*(\!(?P<comment>.*))?\s*$ÚcommentzUnrecognized QDP line: Údataúdata,)Úsep)	ÚreÚcompileÚstripÚmatchÚ
ValueErrorÚ	groupdictÚitemsÚlenÚsplit)ÚlineÚ	delimiterZ_decimal_reZ_command_rer   Z_new_reZ_data_reZ_type_reZ_line_type_rer   Útype_Úval© r   úS/var/www/html/bwave/python/venv/lib/python3.8/site-packages/astropy/io/ascii/qdp.pyÚ
_line_type   s*    *

r   c                    s`   ‡ fdd„| D ƒ}d}|D ]<}|  d¡rt|dd… ƒ}|dkrF|}q||krtdƒ‚q||fS )a:  Read through the list of QDP file lines and label each line by type

    Parameters
    ----------
    lines : list
        List containing one file line in each entry

    Returns
    -------
    contents : list
        List containing the type for each line (see `line_type_and_data`)
    ncol : int
        The number of columns in the data lines. Must be the same throughout
        the file

    Examples
    --------
    >>> line0 = "! A comment"
    >>> line1 = "543 12 456.0"
    >>> lines = [line0, line1]
    >>> types, ncol = _get_type_from_list_of_lines(lines)
    >>> types[0]
    'comment'
    >>> types[1]
    'data,3'
    >>> ncol
    3
    >>> lines.append("23")
    >>> _get_type_from_list_of_lines(lines)
    Traceback (most recent call last):
        ...
    ValueError: Inconsistent number of columns
    c                    s   g | ]}t |ˆ d �‘qS )©r   )r   ©Ú.0r   r   r   r   Ú
<listcomp>{   s     z0_get_type_from_list_of_lines.<locals>.<listcomp>Nr   é   zInconsistent number of columns)Ú
startswithÚintr   )Úlinesr   ÚtypesZcurrent_ncolr   Úncolr   r   r   Ú_get_type_from_list_of_linesX   s    #

r'   c              	   C   sb   d| kr|   d¡}nJt| tƒrFt| ƒ�}dd„ | ¡ D ƒ}W 5 Q R X nt| tƒrV| }ntdƒ‚|S )NÚ
c                 S   s   g | ]}|  ¡ ‘qS r   )r   r   r   r   r   r    �   s     z(_get_lines_from_file.<locals>.<listcomp>zinvalid value of qdb_file)r   Ú
isinstanceÚstrÚopenÚ	readlinesr   r   )Úqdp_filer$   Úfobjr   r   r   Ú_get_lines_from_fileˆ   s    


r/   c                 C   sF  dd„ t |ƒD ƒ}| dkr$g  }}n"t | ¡} |  dg ¡}|  dg ¡}|dk	rzt|ƒt|ƒd  }|t|ƒ |krztdƒ‚d}t |ƒD ] }|d	 | }	|| d
kr¤q†d|	› �}
|dk	rÂ||	d	  }
|
› ||< |	|krð|
› d�||d	 < |d	7 }q†|	|kr†|
› d�||d	 < |
› d�||d < |d7 }q†q†t dd„ |D ƒ¡�rBt‚|S )aæ  Give list of column names from the READ SERR and TERR commands

    Parameters
    ----------
    err_specs : dict
        ``{'serr': [n0, n1, ...], 'terr': [n2, n3, ...]}``
        Error specifications for symmetric and two-sided errors
    ncols : int
        Number of data columns

    Other Parameters
    ----------------
    names : list of str
        Name of data columns (defaults to ['col1', 'col2', ...]), _not_
        including error columns.

    Returns
    -------
    colnames : list
        List containing the column names. Error columns will have the name
        of the main column plus ``_err`` for symmetric errors, and ``_perr``
        and ``_nerr`` for positive and negative errors respectively

    Examples
    --------
    >>> col_in = ['MJD', 'Rate']
    >>> cols = _interpret_err_lines(None, 2, names=col_in)
    >>> cols[0]
    'MJD'
    >>> err_specs = {'terr': [1], 'serr': [2]}
    >>> ncols = 5
    >>> cols = _interpret_err_lines(err_specs, ncols, names=col_in)
    >>> cols[0]
    'MJD'
    >>> cols[2]
    'MJD_nerr'
    >>> cols[4]
    'Rate_err'
    >>> _interpret_err_lines(err_specs, 6, names=col_in)
    Traceback (most recent call last):
        ...
    ValueError: Inconsistent number of input colnames
    c                 S   s   g | ]}d ‘qS ©Ú r   )r   Úir   r   r   r    Ã   s     z(_interpret_err_lines.<locals>.<listcomp>NÚserrÚterré   z%Inconsistent number of input colnamesr   r   r1   ÚcolÚ_errÚ_perrÚ_nerrc                 S   s   g | ]}|d k‘qS r0   r   ©r   Úcr   r   r   r    ê   s     )	ÚrangeÚcopyÚdeepcopyÚpopr   r   ÚnpÚanyÚAssertionError)Ú	err_specsZncolsÚnamesÚcolnamesÚ	serr_colsÚ	terr_colsZall_error_colsÚshiftr2   Zcol_numZcolname_rootr   r   r   Ú_interpret_err_lines–   s<    -



rI   c              
   C   sV  t | ƒ}t||d�\}}g }i }d}d}	d}
d}d}t||ƒD �]È\}}| ¡  d¡}|dkrl|	|d 7 }	q>|dkr¦|dkr„|	}
d}	|i kr˜t dt¡ ||d 7 }q>| d	¡�r°|i k�r|dk�r| ¡  	d¡D ]@}| ¡  	¡ }t
|ƒd
k ròqÔdd„ |dd… D ƒ||d  ¡ < qÔ|dk�r.t|||d�}|dk�r<g }g }| 	|¡D ]X}|dk�rh| tjj¡ n8z| t|ƒ¡ W n$ tk
�rž   | t|ƒ¡ Y nX �qJ| |¡ q>|dkr>|dk	r>t||d�}|
 ¡  	d¡|jd< |	 ¡  	d¡|jd< d}	| |¡ d}q>q>|dk	�rRt||d�}|
 ¡  	d¡|jd< |	 ¡  	d¡|jd< | |¡ |S )aè  Get all tables from a QDP file

    Parameters
    ----------
    qdp_file : str
        Input QDP file name

    Other Parameters
    ----------------
    input_colnames : list of str
        Name of data columns (defaults to ['col1', 'col2', ...]), _not_
        including error columns.
    delimiter : str
        Delimiter for the values in the table.

    Returns
    -------
    list of `~astropy.table.Table`
        List containing all the tables present inside the QDP file
    r   Nr1   ú!r	   r(   Úcommandz9This file contains multiple command blocks. Please verifyr
   é   c                 S   s   g | ]}t |ƒ‘qS r   )r#   r:   r   r   r   r    /  s     z-_get_tables_from_qdp_file.<locals>.<listcomp>r5   r   )rD   ÚNOÚnew)rD   ÚrowsÚinitial_commentsÚcomments)r/   r'   Úzipr   ÚlstripÚwarningsÚwarnr   r"   r   r   ÚlowerrI   Úappendr@   ÚmaÚmaskedr#   r   Úfloatr   Úmeta)r-   Úinput_colnamesr   r$   Úcontentsr&   Z
table_listrC   rE   Zcomment_textrP   Zcommand_linesZcurrent_rowsr   ÚdatatypeZclinerK   ÚvaluesÚvZ	new_tabler   r   r   Ú_get_tables_from_qdp_fileï   s~    þ$



ÿ



ra   c                 C   s¸   d}g }g }t | ƒD ]š\}}| d¡r>| || ¡ |d7 }q| d¡rŠ| || ¡ t| ƒ|d ksx| |d   d¡s€tdƒ‚|d7 }q| d¡r| |d   d¡stdƒ‚q||fS )	a,  Get which column names are error columns

    Examples
    --------
    >>> colnames = ['a', 'a_err', 'b', 'b_perr', 'b_nerr']
    >>> serr, terr = _understand_err_col(colnames)
    >>> np.allclose(serr, [1])
    True
    >>> np.allclose(terr, [2])
    True
    >>> serr, terr = _understand_err_col(['a', 'a_nerr'])
    Traceback (most recent call last):
    ...
    ValueError: Missing positive error...
    >>> serr, terr = _understand_err_col(['a', 'a_perr'])
    Traceback (most recent call last):
    ...
    ValueError: Missing negative error...
    r   r7   r   r8   r9   zMissing negative errorr5   zMissing positive error)Ú	enumerateÚendswithrW   r   r   )rE   rH   r3   r4   r2   r6   r   r   r   Ú_understand_err_col\  s    


"

rd   c                 C   s.   |dkrt  dt¡ d}t| ||d�}|| S )aµ  Read a table from a QDP file

    Parameters
    ----------
    qdp_file : str
        Input QDP file name

    Other Parameters
    ----------------
    names : list of str
        Name of data columns (defaults to ['col1', 'col2', ...]), _not_
        including error columns.

    table_id : int, default 0
        Number of the table to be read from the QDP file. This is useful
        when multiple tables present in the file. By default, the first is read.

    delimiter : str
        Any delimiter accepted by the `sep` argument of str.split()

    Returns
    -------
    tables : list of `~astropy.table.Table`
        List containing all the tables present inside the QDP file
    Nz9table_id not specified. Reading the first available tabler   )r\   r   )rT   rU   r   ra   )r-   rD   Útable_idr   Ztablesr   r   r   Ú_read_table_qdp„  s    þ  ÿrf   c              	   C   sö  ddl }| ¡ }d| jkr^| jd g kr^| jd D ]*}| ¡ }| d¡sPd| }t||d� q2|dkrvt| jƒ\}}n| dg ¡}| dg ¡}|g kr¼d 	d	d
„ |D ƒ¡}td|› �|d� |g krêd 	dd
„ |D ƒ¡}td|› �|d� d| jk�r@| jd g k�r@| jd D ].}| ¡ }| d¡�s0d| }t||d� �q| j}	tdd 	|	¡ |d� | D ]N}
g }|
D ],}t
j |¡�sˆt|ƒ}nd}| |¡ �qltd 	|¡|d� �q`| ¡ }| ¡  |dk	�rìt|dƒ�}t||d� W 5 Q R X | d¡S )a§  Write a table to a QDP file

    Parameters
    ----------
    table : :class:`~astropy.table.Table`
        Input table to be written
    filename : str
        Output QDP file name

    Other Parameters
    ----------------
    err_specs : dict
        Dictionary of the format {'serr': [1], 'terr': [2, 3]}, specifying
        which columns have symmetric and two-sided errors (see QDP format
        specification)
    r   NrP   rJ   )Úfiler3   r4   ú c                 S   s   g | ]}t |ƒ‘qS r   ©r*   ©r   r   r   r   r   r    Î  s     z$_write_table_qdp.<locals>.<listcomp>z
READ SERR c                 S   s   g | ]}t |ƒ‘qS r   ri   rj   r   r   r   r    Ñ  s     z
READ TERR rQ   rM   Úwr(   )ÚioÚStringIOr[   r   r"   Úprintrd   rE   r?   Újoinr@   rX   Z	is_maskedr*   rW   ÚgetvalueÚcloser+   r   )ÚtableÚfilenamerC   rl   r.   r   rF   rG   Z
col_stringrE   Úrowr_   r   ÚrepZfull_stringr   r   r   Ú_write_table_qdp¬  sP    


rv   c                   @   s   e Zd ZdZdZdS )ÚQDPSplitterz'
    Split on space for QDP tables
    rh   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   r   r   rw   ñ  s   rw   c                   @   s   e Zd ZdZeZdZdZdS )Ú	QDPHeaderzJ
    Header that uses the :class:`astropy.io.ascii.basic.QDPSplitter`
    rJ   N)rx   ry   rz   r{   rw   Úsplitter_classr	   Úwrite_commentr   r   r   r   r|   ù  s   r|   c                   @   s(   e Zd ZdZeZejdfgZdZ	dZ
dS )ÚQDPDatazH
    Data that uses the :class:`astropy.io.ascii.basic.CsvSplitter`
    rM   rJ   N)rx   ry   rz   r{   rw   r}   r   rY   Zfill_valuesr	   r~   r   r   r   r   r     s
   r   c                       sJ   e Zd ZdZdZdZdZdZeZ	e
Zd‡ fdd„	Zd	d
„ Zdd„ Z‡  ZS )ÚQDPa˜  Quick and Dandy Plot table.

    Example::

        ! Initial comment line 1
        ! Initial comment line 2
        READ TERR 1
        READ SERR 3
        ! Table 0 comment
        !a a(pos) a(neg) b be c d
        53000.5   0.25  -0.5   1  1.5  3.5 2
        54000.5   1.25  -1.5   2  2.5  4.5 3
        NO NO NO NO NO
        ! Table 1 comment
        !a a(pos) a(neg) b be c d
        54000.5   2.25  -2.5   NO  3.5  5.5 5
        55000.5   3.25  -3.5   4  4.5  6.5 nan

    The input table above contains some initial comments, the error commands,
    then two tables.
    This file format can contain multiple tables, separated by a line full
    of ``NO``s. Comments are exclamation marks, and missing values are single
    ``NO`` entries. The delimiter is usually whitespace, more rarely a comma.
    The QDP format differentiates between data and error columns. The table
    above has commands::

        READ TERR 1
        READ SERR 3

    which mean that after data column 1 there will be two error columns
    containing its positive and engative error bars, then data column 2 without
    error bars, then column 3, then a column with the symmetric error of column
    3, then the remaining data columns.

    As explained below, table headers are highly inconsistent. Possible
    comments containing column names will be ignored and columns will be called
    ``col1``, ``col2``, etc. unless the user specifies their names with the
    ``names=`` keyword argument,
    When passing column names, pass **only the names of the data columns, not
    the error columns.**
    Error information will be encoded in the names of the table columns.
    (e.g. ``a_perr`` and ``a_nerr`` for the positive and negative error of
    column ``a``, ``b_err`` the symmetric error of column ``b``.)

    When writing tables to this format, users can pass an ``err_specs`` keyword
    passing a dictionary ``{'serr': [3], 'terr': [1, 2]}``, meaning that data
    columns 1 and two will have two additional columns each with their positive
    and negative errors, and data column 3 will have an additional column with
    a symmetric error (just like the ``READ SERR`` and ``READ TERR`` commands
    above)

    Headers are just comments, and tables distributed by various missions
    can differ greatly in their use of conventions. For example, light curves
    distributed by the Swift-Gehrels mission have an extra space in one header
    entry that makes the number of labels inconsistent with the number of cols.
    For this reason, we ignore the comments that might encode the column names
    and leave the name specification to the user.

    Example::

        >               Extra space
        >                   |
        >                   v
        >!     MJD       Err (pos)       Err(neg)        Rate            Error
        >53000.123456   2.378e-05     -2.378472e-05     NO             0.212439

    These readers and writer classes will strive to understand which of the
    comments belong to all the tables, and which ones to each single table.
    General comments will be stored in the ``initial_comments`` meta of each
    table. The comments of each table will be stored in the ``comments`` meta.

    Example::

        t = Table.read(example_qdp, format='ascii.qdp', table_id=1, names=['a', 'b', 'c', 'd'])

    reads the second table (``table_id=1``) in file ``example.qdp`` containing
    the table above. There are four column names but seven data columns, why?
    Because the ``READ SERR`` and ``READ TERR`` commands say that there are
    three error columns.
    ``t.meta['initial_comments']`` will contain the initial two comment lines
    in the file, while ``t.meta['comments']`` will contain ``Table 1 comment``

    The table can be written to another file, preserving the same information,
    as::

        t.write(test_file, err_specs={'terr': [1], 'serr': [3]})

    Note how the ``terr`` and ``serr`` commands are passed to the writer.

    ZqdpTz.qdpzQuick and Dandy PlotterNc                    s&   t ƒ  ¡  || _|| _|| _|| _d S )N)ÚsuperÚ__init__re   rD   rC   r   )Úselfre   rD   rC   r   ©Ú	__class__r   r   r‚   r  s
    
zQDP.__init__c                 C   s*   | j j|dd�| _t| j| j| j| jd�S )Nr(   )Únewline)re   rD   r   )ZinputterÚ	get_linesr$   rf   re   rD   r   )rƒ   rr   r   r   r   Úready  s    üzQDP.readc                 C   s   |   |¡ t|| jd�}|S )N)rC   )Z_check_multidim_tablerv   rC   )rƒ   rr   r$   r   r   r   Úwrite‚  s    
z	QDP.write)NNNN)rx   ry   rz   r{   Z_format_nameZ_io_registry_can_writeZ_io_registry_suffixZ_descriptionr|   Zheader_classr   Z
data_classr‚   rˆ   r‰   Ú__classcell__r   r   r„   r   r€     s   [	r€   )N)N)N)NN)NNN)NN)r{   r=   r   rT   Úcollections.abcr   Únumpyr@   Zastropy.tabler   Zastropy.utils.exceptionsr   r1   r   r   r   r'   r/   rI   ra   rd   rf   rv   ZDefaultSplitterrw   ZCommentedHeaderHeaderr|   Z	BasicDatar   ZBasicr€   r   r   r   r   Ú<module>   s(   
D
0
Y
m(
(
E
