B
    q\oB                 @   s~   d Z ddlZddlZddlZddlZddgZeejZ	e
ejde	ddZdd Zd	d
 ZdddZdddejfddZdS )z\
A module that provides functions for manipulating bit masks and data quality
(DQ) arrays.

    Nbitfield_to_boolean_maskinterpret_bit_flagsunsafe)dtypecastingc             C   s   | dk rdS t | ddkS )al  
    Verifies if the input number is a bit flag (i.e., an integer number that is
    an integer power of 2).

    Parameters
    ----------
    n : int
        A positive integer number. Non-positive integers are considered not to
        be "flags".

    Returns
    -------
    bool
        ``True`` if input ``n`` is a bit flag and ``False`` if it is not.

       F1)bincount)n r   5lib/python3.7/site-packages/astropy/nddata/bitmask.py_is_bit_flag   s    r   c             C   s2   t | tjrt | t p0t | tjo0t| tjS )N)
isinstancenumbersZIntegralboolnpZgeneric
issubdtypeinteger)r   r   r   r   _is_int-   s    r   c             C   s6  |dk	}t |}d}t| r2|r*t|  S t| S | dkrJ|rFtddS t| tr|rbtdt|  } |  dkr~dS | d}|dkrd}| d	d 	 } n|dkrt
d
d}x| d}| d}|dkr|dkrP ||krt
d| d}| d}|dks"|t| d	 k r*t
d| d	d  } qW d| krT| d} n.d| krj| d} n| dkr|t
d| g} t| d	k}n2t| drtdd | D stdntdttt| }	t|	t| krtd d}
x4|	D ],}t|s|st
d||
|7 }
qW |r2|
 }
|
S )a	  
    Converts input bit flags to a single integer value (bit mask) or `None`.

    When input is a list of flags (either a Python list of integer flags or a
    sting of comma- or '+'-separated list of flags), the returned bit mask
    is obtained by summing input flags.

    .. note::
        In order to flip the bits of the returned bit mask,
        for input of `str` type, prepend '~' to the input string. '~' must
        be prepended to the *entire string* and not to each bit flag! For
        input that is already a bit mask or a Python list of bit flags, set
        ``flip_bits`` for `True` in order to flip the bits of the returned
        bit mask.

    Parameters
    ----------
    bit_flags : int, str, list, None
        An integer bit mask or flag, `None`, a string of comma- or
        '+'-separated list of integer bit flags, or a Python list of integer
        bit flags. If ``bit_flags`` is a `str` and if it is prepended with '~',
        then the output bit mask will have its bits flipped (compared to simple
        sum of input flags). For input ``bit_flags`` that is already a bit mask
        or a Python list of bit flags, bit-flipping can be controlled through
        ``flip_bits`` parameter.

    flip_bits : bool, None
        Indicates whether or not to flip the bits of the returned bit mask
        obtained from input bit flags. This parameter must be set to `None`
        when input ``bit_flags`` is either `None` or a Python list of flags.

    Returns
    -------
    bitmask : int or None
        Returns an integer bit mask formed from the input bit value or `None`
        if input ``bit_flags`` parameter is `None` or an empty string.
        If input string value was prepended with '~' (or ``flip_bits`` was set
        to `True`), then returned value will have its bits flipped
        (inverse mask).

    Examples
    --------
        >>> from astropy.nddata.bitmask import interpret_bit_flags
        >>> "{0:016b}".format(0xFFFF & interpret_bit_flags(28))
        '0000000000011100'
        >>> "{0:016b}".format(0xFFFF & interpret_bit_flags('4,8,16'))
        '0000000000011100'
        >>> "{0:016b}".format(0xFFFF & interpret_bit_flags('~4,8,16'))
        '1111111111100011'
        >>> "{0:016b}".format(0xFFFF & interpret_bit_flags('~(4+8+16)'))
        '1111111111100011'
        >>> "{0:016b}".format(0xFFFF & interpret_bit_flags([4, 8, 16]))
        '0000000000011100'
        >>> "{0:016b}".format(0xFFFF & interpret_bit_flags([4, 8, 16], flip_bits=True))
        '1111111111100011'

    NFzRKeyword argument 'flip_bits' must be set to 'None' when input 'bit_flags' is None.zKeyword argument 'flip_bits' is not permitted for comma-separated string lists of bit flags. Prepend '~' to the string to indicate bit-flipping.) ZNONEZINDEF~r   Tr   z'Bitwise-NOT must precede bit flag list.()z(Unbalanced parantheses in bit flag list.zAIncorrect syntax (incorrect use of parenthesis) in bit flag list.,+r   zTEmpty bit flag lists not allowed when either bitwise-NOT or parenthesis are present.__iter__c             S   s   g | ]}t |qS r   )r   ).0flagr   r   r   
<listcomp>   s    z'interpret_bit_flags.<locals>.<listcomp>z+Each bit flag in a list must be an integer.z*Unsupported type for argument 'bit_flags'.z#Duplicate bit flags will be ignoredz>Input list contains invalid (not powers of two) bit flag: {:d})r   r   int	TypeErrorr   strstripupperfindlstrip
ValueErrorr
   rfindlensplithasattrallsetmapwarningswarnr   format)Z	bit_flags	flip_bitsZhas_flip_bitsZallow_non_flagsZbitflip_posZnlparZnrparZlpar_posZrpar_posZbitsetZbitmaskvr   r   r   r   4   sz    :










Fc             C   s   t | } t | jt js"tdt||d}|dkr\|rJt j| |d}nt j| |d}|S |t	@ }t j
|| jdd}t j| t jd}t j| ||dd |rt j||d |j|d	d	d
S )a#  
    bitfield_to_boolean_mask(bitfield, ignore_flags=None, flip_bits=None, good_mask_value=False, dtype=numpy.bool_)
    Converts an array of bit fields to a boolean (or integer) mask array
    according to a bit mask constructed from the supplied bit flags (see
    ``ignore_flags`` parameter).

    This function is particularly useful to convert data quality arrays to
    boolean masks with selective filtering of DQ flags.

    Parameters
    ----------
    bitfield : numpy.ndarray
        An array of bit flags. By default, values different from zero are
        interpreted as "bad" values and values equal to zero are considered
        as "good" values. However, see ``ignore_flags`` parameter on how to
        selectively ignore some bits in the ``bitfield`` array data.

    ignore_flags : int, str, list, None (Default = 0)
        An integer bit mask, a Python list of bit flags, a comma- or
        '+'-separated string list of integer bit flags that indicate what
        bits in the input ``bitfield`` should be *ignored* (i.e., zeroed), or
        `None`.

        | Setting ``ignore_flags`` to `None` effectively will make
          `bitfield_to_boolean_mask` interpret all ``bitfield`` elements
          as "good" regardless of their value.

        | When ``ignore_flags`` argument is an integer bit mask, it will be
          combined using bitwise-NOT and bitwise-AND with each element of the
          input ``bitfield`` array (``~ignore_flags & bitfield``). If the
          resultant bitfield element is non-zero, that element will be
          interpreted as a "bad" in the output boolean mask and it will be
          interpreted as "good" otherwise. ``flip_bits`` parameter may be used
          to flip the bits (``bitwise-NOT``) of the bit mask thus effectively
          changing the meaning of the ``ignore_flags`` parameter from "ignore"
          to "use only" these flags.

        .. note::

            Setting ``ignore_flags`` to 0 effectively will assume that all
            non-zero elements in the input ``bitfield`` array are to be
            interpreted as "bad".

        | When ``ignore_flags`` argument is a Python list of integer bit
          flags, these flags are added together to create an integer bit mask.
          Each item in the list must be a flag, i.e., an integer that is an
          integer power of 2. In order to flip the bits of the resultant
          bit mask, use ``flip_bits`` parameter.

        | Alternatively, ``ignore_flags`` may be a string of comma- or
          '+'-separated list of integer bit flags that should be added together
          to create an integer bit mask. For example, both ``'4,8'`` and
          ``'4+8'`` are equivalent and indicate that bit flags 4 and 8 in
          the input ``bitfield`` array should be ignored when generating
          boolean mask.

        .. note::

            ``'None'``, ``'INDEF'``, and empty (or all white space) strings
            are special values of string ``ignore_flags`` that are
            interpreted as `None`.

        .. note::

            Each item in the list must be a flag, i.e., an integer that is an
            integer power of 2. In addition, for convenience, an arbitrary
            **single** integer is allowed and it will be interpretted as an
            integer bit mask. For example, instead of ``'4,8'`` one could
            simply provide string ``'12'``.

        .. note::

            When ``ignore_flags`` is a `str` and when it is prepended with
            '~', then the meaning of ``ignore_flags`` parameters will be
            reversed: now it will be interpreted as a list of bit flags to be
            *used* (or *not ignored*) when deciding which elements of the
            input ``bitfield`` array are "bad". Following this convention,
            an ``ignore_flags`` string value of ``'~0'`` would be equivalent
            to setting ``ignore_flags=None``.

        .. warning::

            Because prepending '~' to a string ``ignore_flags`` is equivalent
            to setting ``flip_bits`` to `True`, ``flip_bits`` cannot be used
            with string ``ignore_flags`` and it must be set to `None`.

    flip_bits : bool, None (Default = None)
        Specifies whether or not to invert the bits of the bit mask either
        supplied directly through ``ignore_flags`` parameter or built from the
        bit flags passed through ``ignore_flags`` (only when bit flags are
        passed as Python lists of integer bit flags). Occasionally, it may be
        useful to *consider only specific bit flags* in the ``bitfield``
        array when creating a boolean mask as opposed to *ignoring* specific
        bit flags as ``ignore_flags`` behaves by default. This can be achieved
        by inverting/flipping the bits of the bit mask created from
        ``ignore_flags`` flags which effectively changes the meaning of the
        ``ignore_flags`` parameter from "ignore" to "use only" these flags.
        Setting ``flip_bits`` to `None` means that no bit flipping will be
        performed. Bit flipping for string lists of bit flags must be
        specified by prepending '~' to string bit flag lists
        (see documentation for ``ignore_flags`` for more details).

        .. warning::
            This parameter can be set to either `True` or `False` **ONLY** when
            ``ignore_flags`` is either an integer bit mask or a Python
            list of integer bit flags. When ``ignore_flags`` is either
            `None` or a string list of flags, ``flip_bits`` **MUST** be set
            to `None`.

    good_mask_value : int, bool (Default = False)
        This parameter is used to derive the values that will be assigned to
        the elements in the output boolean mask array that correspond to the
        "good" bit fields (that are 0 after zeroing bits specified by
        ``ignore_flags``) in the input ``bitfield`` array. When
        ``good_mask_value`` is non-zero or ``numpy.True_`` then values in the
        output boolean mask array corresponding to "good" bit fields in
        ``bitfield`` will be ``numpy.True_`` (if ``dtype`` is ``numpy.bool_``)
        or 1 (if ``dtype`` is of numerical type) and values of corresponding
        to "bad" flags will be ``numpy.False_`` (or 0). When
        ``good_mask_value`` is zero or ``numpy.False_`` then the values
        in the output boolean mask array corresponding to "good" bit fields
        in ``bitfield`` will be ``numpy.False_`` (if ``dtype`` is
        ``numpy.bool_``) or 0 (if ``dtype`` is of numerical type) and values
        of corresponding to "bad" flags will be ``numpy.True_`` (or 1).

    dtype : data-type (Default = ``numpy.bool_``)
        The desired data-type for the output binary mask array.

    Returns
    -------
    mask : numpy.ndarray
        Returns an array of the same dimensionality as the input ``bitfield``
        array whose elements can have two possible values,
        e.g., ``numpy.True_`` or ``numpy.False_`` (or 1 or 0 for integer
        ``dtype``) according to values of to the input ``bitfield`` elements,
        ``ignore_flags`` parameter, and the ``good_mask_value`` parameter.

    Examples
    --------
        >>> from astropy.nddata import bitmask
        >>> import numpy as np
        >>> dqbits = np.asarray([[0, 0, 1, 2, 0, 8, 12, 0],
        ...                      [10, 4, 0, 0, 0, 16, 6, 0]])
        >>> bitmask.bitfield_to_boolean_mask(dqbits, ignore_flags=0,
        ...                                  dtype=int)
        array([[0, 0, 1, 1, 0, 1, 1, 0],
               [1, 1, 0, 0, 0, 1, 1, 0]])
        >>> bitmask.bitfield_to_boolean_mask(dqbits, ignore_flags=0,
        ...                                  dtype=bool)
        array([[False, False,  True,  True, False,  True,  True, False],
               [ True,  True, False, False, False,  True,  True, False]]...)
        >>> bitmask.bitfield_to_boolean_mask(dqbits, ignore_flags=6,
        ...                                  good_mask_value=0, dtype=int)
        array([[0, 0, 1, 0, 0, 1, 1, 0],
               [1, 0, 0, 0, 0, 1, 0, 0]])
        >>> bitmask.bitfield_to_boolean_mask(dqbits, ignore_flags=~6,
        ...                                  good_mask_value=0, dtype=int)
        array([[0, 0, 0, 1, 0, 0, 1, 0],
               [1, 1, 0, 0, 0, 0, 1, 0]])
        >>> bitmask.bitfield_to_boolean_mask(dqbits, ignore_flags=6, dtype=int,
        ...                                  flip_bits=True, good_mask_value=0)
        array([[0, 0, 0, 1, 0, 0, 1, 0],
               [1, 1, 0, 0, 0, 0, 1, 0]])
        >>> bitmask.bitfield_to_boolean_mask(dqbits, ignore_flags='~(2+4)',
        ...                                  good_mask_value=0, dtype=int)
        array([[0, 0, 0, 1, 0, 0, 1, 0],
               [1, 1, 0, 0, 0, 0, 1, 0]])
        >>> bitmask.bitfield_to_boolean_mask(dqbits, ignore_flags=[2, 4],
        ...                                  flip_bits=True, good_mask_value=0,
        ...                                  dtype=int)
        array([[0, 0, 0, 1, 0, 0, 1, 0],
               [1, 1, 0, 0, 0, 0, 1, 0]])

    z-Input bitfield array must be of integer type.)r3   N)r   r   )r   r   )outr   )r5   F)r   Zsubokcopy)r   Zasarrayr   r   r   r"   r   Z	ones_likeZ
zeros_like_SUPPORTED_FLAGSbitwise_notZ
empty_likebool_Zbitwise_andZlogical_notZastype)ZbitfieldZignore_flagsr3   Zgood_mask_valuer   Zignore_maskmaskr   r   r   r      s$     2

)N)__doc__sysr0   r   Znumpyr   __all__Zmaximum_sctypeZuintZ_MAX_UINT_TYPEr!   r8   r7   r   r   r   r9   r   r   r   r   r   <module>   s   
 