text:
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import numpy as np

from .sentinels import ParameterNotSpecified
from ..deps import OPT_DEPS

__all__ = ['convolve', 'convolve_median', 'interpolate', 'interpolate_median',
           'grid_rebin', 'convert_to_native_bytorder']

if not OPT_DEPS['NUMBA']:  # pragma: no cover
    __doctest_skip__ = ['*']

def convert_to_native_bytorder(array):
    """Convert a numpy array with endian dtype to an array of native dtype.

    Parameters
    ----------
    array : `numpy.ndarray`-like
        The array to convert. Must be convertable to a numpy.ndarray.

    Returns
    -------
    converted_array : `numpy.ndarray`
        The converted array. If the array already was in native dtype the array
        is simple returned without making a copy.

    Notes
    -----
    This function is essential when using ``Numba`` because it cannot process
    small/big endian arrays. At least it seems like that.
    """
    array = np.asarray(array)
    return array.astype(array.dtype.type, copy=False)

def grid_rebin(data, old_grid, new_grid):
    """Rebin the data from a given grid to a new grid preserving the \
            total number of counts.

    The function assumes that the grid points represent the center of the
    grid bin and that the bin extends half the distance to the next bin.

    Parameters
    ----------
    data : `numpy.ndarray`
        The data to rebin.

    old_grid : `numpy.ndarray`
        The grid points for the data. Must have the same shape as the data.

    new_grid : `numpy.ndarray`
        The new grid to rebin.

    Returns
    -------
    rebinned_data : `numpy.ndarray`
        The data rebinned from ``old_grid`` to ``new_grid``. It has the
        same shape as ``new_grid``.

    Examples
    --------
    Downsampling some data is possible::

        >>> grid_rebin(np.ones(10),
        ...            np.arange(10),
        ...            np.array([-3,-1,1,3,5,7,9,11]))
        array([ 0. ,  0.5,  2. ,  2. ,  2. ,  2. ,  1.5,  0. ])

    as well as upsamling::

        >>> grid_rebin(np.ones(5), np.arange(0, 10, 2), np.arange(-1, 10))
        array([ 0.25,  0.5 ,  0.5 ,  0.5 ,  0.5 ,  0.5 ,  0.5 ,  0.5 ,  0.5 ,
                0.5 ,  0.25])

    Notes
    -----
    This function is currently implemented in an inefficient manner, for very
    large ``data`` this may become very slow.
    """
    if not OPT_DEPS['NUMBA']:  # pragma: no cover
        raise ImportError('grid_rebin requires numba to be installed.')

    data = convert_to_native_bytorder(data)
    old_grid = convert_to_native_bytorder(old_grid)
    new_grid = convert_to_native_bytorder(new_grid)

    if data.shape != old_grid.shape:
        raise ValueError('data ({0}) and old grid ({1}) must have the same '
                         'size'.format(data.size, old_grid.size))

    if data.ndim > 1:
        raise ValueError('grid rebin is only possible for one-dimensional '
                         'data.')

    if new_grid.ndim > 1:
        raise ValueError('grid rebin is only possible for one-dimensional '
                         'grids.')

    return _rebin_count_conservation(data, old_grid, new_grid)

def interpolate(data, kernel, mask=ParameterNotSpecified):
    """Interpolation of the masked values of some data by convolution.

    .. note::
        Requires ``Numba``.

    Parameters
    ----------
    data : `numpy.ndarray`, `numpy.ma.MaskedArray`, `~nddata.nddata.NDData`
        The data to interpolate.

    kernel : `numpy.ndarray`, `astropy.convolution.Kernel`
        The kernel (or footprint) for the interpolation. The sum of the
        ``kernel`` must not be 0 (or very close to it). Each axis of the kernel
        must be odd.

    mask : `numpy.ndarray`, optional
        Masked values in the ``data``. Elements where the mask is equivalent to
        1 (also ``True``) are interpreted as masked and are ignored during the
        interpolation. If not given use the mask of the data or if it has no
        mask either assume all the data is unmasked.

    Returns
    -------
    interpolated : `numpy.ndarray`
        The interpolated array.

    See also
    --------
    interpolate_median : Numba-based utility to interpolate masked values \
        using median convolution.
    convolve : Numba-based utility to convolve using weighted summation.
    convolve_median : Numba-based utility to convolve using the median.

    Notes
    -----
    1. If the ``data`` parameter has a ``mask`` attribute then ``data.data``
       is interpreted as ``data`` and ``array.mask`` as ``mask`` parameter.
       This allows using `~numpy.ma.MaskedArray` objects as ``data`` parameter.

    2. If an explicit ``mask`` is given (even if it is ``None``) an implicit
       mask is ignored.

    3. No border handling is possible, if the kernel extends beyond the
       image these _outside_ values are treated as if they were masked.

    4. Since the kernel is internally normalized (in order to allow ignoring
       the masked values) the sum of the kernel must not be 0 because each
       masked element in the interpolated image would be ``Inf`` since it is
       divided by the sum of the kernel. Even a kernel sum close to zero should
       be avoided because otherwise floating value precision might become
       significant.

    5. Only implemented for 1D, 2D and 3D data.

    Examples
    --------
    Interpolate masked values for a 1D masked array:

        >>> from nddata.utils.numbautils import interpolate
        >>> import numpy as np

        >>> data = np.ma.array([1,1000,2], mask=[0, 1, 0])
        >>> interpolate(data, [1,1,1])
        array([ 1. ,  1.5,  2. ])

    Interpolate a 2D list with an astropy kernel::

        >>> from astropy.convolution.kernels import Box2DKernel
        >>> kernel = Box2DKernel(3)
        >>> data = [[1,1,1],[1,1000,1],[1,1,3]]
        >>> mask = [[0,0,0],[0,1,0],[0,0,0]]
        >>> interpolate(data, kernel, mask)
        array([[ 1.  ,  1.  ,  1.  ],
               [ 1.  ,  1.25,  1.  ],
               [ 1.  ,  1.  ,  3.  ]])

    Interpolate a 3D NDData instance::

        >>> from nddata.nddata import NDData
        >>> data = np.ones((3,3,3))
        >>> data[1,1,1] = 1000
        >>> mask = np.zeros((3,3,3))
        >>> mask[1,1,1] = 1
        >>> data = NDData(data, mask=mask)
        >>> kernel = np.ones((3,3,3))
        >>> interpolate(data, kernel)
        array([[[ 1.,  1.,  1.],
                [ 1.,  1.,  1.],
                [ 1.,  1.,  1.]],
        <BLANKLINE>
               [[ 1.,  1.,  1.],
                [ 1.,  1.,  1.],
                [ 1.,  1.,  1.]],
        <BLANKLINE>
               [[ 1.,  1.,  1.],
                [ 1.,  1.,  1.],
                [ 1.,  1.,  1.]]])

    If no mask is given the interpolation just returns the input::

        >>> interpolate([1,2,3], [1,1,1])
        array([1, 2, 3])

    Given an implicit and explicit mask the explicit mask is always used::

        >>> interpolate(np.ma.array([1,2,3], mask=[1,1,1]), [1,1,1],
        ...             mask=[0,1,0])
        array([ 1.,  2.,  3.])
    """
    return _process(data, kernel, mask, 'interpolation')

def convolve(data, kernel, mask=ParameterNotSpecified, rescale=True,
             var=False):
    """Convolution of some data by ignoring masked values.

    .. note::
        Requires ``Numba``.

    Parameters
    ----------
    data : `numpy.ndarray`, `numpy.ma.MaskedArray`, `~nddata.nddata.NDData`
        The data to convolve.

    kernel : `numpy.ndarray`, `astropy.convolution.Kernel`
        The kernel (or footprint) for the convolution. The sum of the
        ``kernel`` must not be 0 (or very close to it). Each axis of the kernel
        must be odd.

    mask : `numpy.ndarray`, optional
        Masked values in the ``data``. Elements where the mask is equivalent to
        1 (also ``True``) are interpreted as masked and are ignored during the
        convolution. If not given use the mask of the data or if it has no mask
        either assume all the data is unmasked.

    rescale_kernel : `bool`, optional
        In order to allow for ignoring masked values the kernel must be
        normalized (divided by it's sum). If this is ``True`` the result is
        multiplied with the sum again, if ``False`` the result of the
        convolution will be as if each kernel element was divided by the sum
        of the kernel.
        Default is ``True``.

    var : `bool`, optional
        Also calculate the weighted variance. This can be quite slow!
        Default is ``False``.

    Returns
    -------
    convolved : `numpy.ndarray`
        The convolved array.

    variance : `numpy.ndarray`, optional
        The variance. Only returned if the parameter ``var`` was true.

    See also
    --------
    interpolate : Numba-based utility to interpolate masked values using \
        weighted convolution.
    interpolate_median : Numba-based utility to interpolate masked values \
        using median convolution.
    convolve_median : Numba-based utility to convolve using the median.

    numpy.convolve : Fast one-dimensional convolution without masks.
    scipy.ndimage.convolve : Fast n-dimensional convolution without masks.
    astropy.convolution.convolve : Convolution that excludes ``NaN`` from the \
        data, without masks.

    Notes
    -----
    1. If the ``data`` parameter has a ``mask`` attribute then ``data.data``
       is interpreted as ``data`` and ``array.mask`` as ``mask`` parameter.
       This allows using `~numpy.ma.MaskedArray` objects as ``data`` parameter.

    2. If an explicit ``mask`` is given (even if it is ``None``) an implicit
       mask is ignored.

    3. No border handling is possible, if the kernel extends beyond the
       image these _outside_ values are treated as if they were masked.

    4. Since the kernel is internally normalized (in order to allow ignoring
       the masked values) the sum of the kernel must not be 0 because each
       element in the convolved image would be ``Inf`` since it is divided by
       the sum of the kernel. Even a kernel sum close to zero should be avoided
       because otherwise floating value precision might become significant.

    5. Only implemented for 1D, 2D and 3D data.

    .. warning::
        Weighted summation (and also the standard deviation) are calculated
        with naive formulas. These might not be appropriate in all cases!

    Examples
    --------
    Convolution of a 1D masked array::

        >>> from nddata.utils.numbautils import convolve
        >>> import numpy as np

        >>> data = np.ma.array([1,1000,2,1], mask=[0, 1, 0, 0])
        >>> convolve(data, [1,1,1])
        array([ 3. ,  4.5,  4.5,  4.5])
        >>> convolve(data, [1,1,1], rescale=False)
        array([ 1. ,  1.5,  1.5,  1.5])

    The ``rescale`` parameter determines if a **sum** convolution (in case it
    is not given or ``True``) or a **mean** convolution (if ``False``) is
    performed.

    Convolution of a 2D list given an explicit mask and an astropy Kernel::

        >>> from astropy.convolution.kernels import Box2DKernel
        >>> data = [[1,1,1],[1,1000,1],[1,1,3]]
        >>> kernel = Box2DKernel(3)
        >>> mask = [[0,0,0],[0,1,0],[0,0,0]]
        >>> convolve(data, kernel, mask, rescale=False)
        array([[ 1.        ,  1.        ,  1.        ],
               [ 1.        ,  1.25      ,  1.4       ],
               [ 1.        ,  1.4       ,  1.66666667]])

    Convolution of a 3D NDData instance::

        >>> from nddata.nddata import NDData
        >>> data = np.ones((3,3,3))
        >>> data[1,1,1] = 1000
        >>> data[2,2,2] = 5
        >>> mask = np.zeros((3,3,3))
        >>> mask[1,1,1] = 1
        >>> data = NDData(data, mask=mask)
        >>> kernel = np.ones((3,3,3))
        >>> convolve(data, kernel, rescale=False)
        array([[[ 1.        ,  1.        ,  1.        ],
                [ 1.        ,  1.        ,  1.        ],
                [ 1.        ,  1.        ,  1.        ]],
        <BLANKLINE>
               [[ 1.        ,  1.        ,  1.        ],
                [ 1.        ,  1.15384615,  1.23529412],
                [ 1.        ,  1.23529412,  1.36363636]],
        <BLANKLINE>
               [[ 1.        ,  1.        ,  1.        ],
                [ 1.        ,  1.23529412,  1.36363636],
                [ 1.        ,  1.36363636,  1.57142857]]])

    If neither an explicit nor implicit mask is given it assumes all values are
    unmasked::

        >>> convolve([1,2,3], [1,1,1])
        array([ 4.5,  6. ,  7.5])

    An implicit mask can be ignored by setting the mask to ``None``::

        >>> convolve(np.ma.array([1,2,3], mask=[1,1,1]), [1,1,1], mask=None)
        array([ 4.5,  6. ,  7.5])

    Given an implicit and explicit mask the explicit mask is always used::

        >>> convolve(np.ma.array([1,2,3], mask=[1,1,1]), [1,1,1], mask=[0,1,0])
        array([ 3.,  6.,  9.])

    Rescaling is also possible (in that case it's the mean of the convolved
    elements rather than the rescaled sum)::

        >>> convolve([1,2,3], [1,1,1], rescale=False)
        array([ 1.5,  2. ,  2.5])

    It is also possible to calculate the variance (without degrees of freedom
    correction)::

        >>> convolve([0, 100, 40, 40], [1, 2, 1], mask=[0, 0, 1, 0],
        ...          rescale=False, var=True)
        (array([ 33.33333333,  66.66666667,  70.        ,  40.        ]),
         array([ 2222.22222222,  2222.22222222,   900.        ,     0.        \
]))

        >>> from nddata.utils.numbautils import convolve
        >>> convolve([0, 100, 40, 40], [1, 2, 1], mask=[0, 0, 1, 0],
        ...          rescale=True, var=True)
        (array([ 133.33333333,  266.66666667,  280.        ,  160.        ]),
         array([ 35555.55555556,  35555.55555556,  14400.        ,      \
0.        ]))

    .. warning::
        If ``var=True`` the function returns 2 results instead of one!
    """
    result = _process(data, kernel, mask, 'convolution')
    if var:
        variance = _process(data, kernel, mask, 'convolution', expected=result)
    else:
        variance = None

    if rescale:
        kernel_sum = np.sum(getattr(kernel, 'array', kernel))
        result *= kernel_sum
        if variance is not None:
            variance *= kernel_sum**2

    if variance is None:
        return result
    else:
        return result, variance

def interpolate_median(data, kernel, mask=ParameterNotSpecified):
    """Interpolation of masked values in the data based on median convolution.

    .. note::
        Requires ``Numba``.

    Parameters
    ----------
    data : `numpy.ndarray`, `numpy.ma.MaskedArray`, `~nddata.nddata.NDData`
        The data to interpolate.

    kernel : `numpy.ndarray`, `astropy.convolution.Kernel`
        The kernel for the interpolation. One difference from normal
        interpolation is that the actual values of the kernel do not matter,
        except when it is zero then it won't use this element for the median
        computation.
        Each axis of the kernel must be odd.

    mask : `numpy.ndarray`, optional
        Masked values in the ``data``. Elements where the mask is equivalent to
        1 (also ``True``) are interpreted as masked and are ignored during the
        interpolation. If not given use the mask of the data or if it has no
        mask either assume all the data is unmasked.

    Returns
    -------
    interpolated : `numpy.ndarray`
        The interpolated array.

    See also
    --------
    interpolate : Numba-based utility to interpolate masked values using \
        weighted convolution.
    convolve : Numba-based utility to convolve using weighted summation.
    convolve_median : Numba-based utility to convolve using the median.

    Notes
    -----
    1. If the ``data`` parameter has a ``mask`` attribute then ``data.data``
       is interpreted as ``data`` and ``array.mask`` as ``mask`` parameter.
       This allows using `~numpy.ma.MaskedArray` objects as ``data`` parameter.

    2. If an explicit ``mask`` is given (even if it is ``None``) an implicit
       mask is ignored.

    3. No border handling is possible, if the kernel extends beyond the
       image these _outside_ values are treated as if they were masked.

    4. Only implemented for 1D, 2D and 3D data.

    Examples
    --------
    It works almost like `interpolate` but based on the ``median`` instead of
    the **sum** or **mean** of the elements within the kernel::

        >>> from nddata.utils.numbautils import interpolate_median
        >>> import numpy as np

        >>> data = np.ma.array([1,1000,2,1], mask=[0, 1, 0, 0])
        >>> interpolate_median(data, [1,1,1])
        array([ 1. ,  1.5,  2. ,  1. ])

    Support for two dimensional arrays and masks is also implemented::

        >>> data = np.arange(9).reshape(3, 3)
        >>> data[1, 1] = 100
        >>> mask = np.zeros((3, 3), dtype=bool)
        >>> mask[1, 1] = 1
        >>> interpolate_median(data, np.ones((3,3)), mask)
        array([[ 0.,  1.,  2.],
               [ 3.,  4.,  5.],
               [ 6.,  7.,  8.]])

    And also for three dimensional arrays::

        >>> data = np.arange(27).reshape(3, 3, 3)
        >>> data[0, 0, 0] = 10000
        >>> mask = np.zeros((3, 3, 3))
        >>> mask[0, 0, 0] = 1
        >>> interpolate_median(data, np.ones((3, 3, 3)), mask)
        array([[[  9.,   1.,   2.],
                [  3.,   4.,   5.],
                [  6.,   7.,   8.]],
        <BLANKLINE>
               [[  9.,  10.,  11.],
                [ 12.,  13.,  14.],
                [ 15.,  16.,  17.]],
        <BLANKLINE>
               [[ 18.,  19.,  20.],
                [ 21.,  22.,  23.],
                [ 24.,  25.,  26.]]])

    Kernel elements of zero can also be used to use only specified elements for
    the interpolation::

        >>> data = np.ma.array([1,1000,2,1], mask=[0, 1, 0, 0])
        >>> interpolate_median(data, [1,0,0])
        array([ 1.,  1.,  2.,  1.])

    This kernel uses only the left element for the interpolation.

    .. note::
        The median calculation is done by a jitted version based on
        Insertionsort which becomes inefficient for large kernels (more than
        hundred or several hundred elements).
    """
    return _process(data, kernel, mask, 'interpolation', True)

def convolve_median(data, kernel, mask=ParameterNotSpecified, mad=False):
    """Median based convolution of some data by ignoring masked values.

    .. note::
        Requires ``Numba``.

    Parameters
    ----------
    data : `numpy.ndarray`, `numpy.ma.MaskedArray`, `~nddata.nddata.NDData`
        The data to convolve.

    kernel : `numpy.ndarray`, `astropy.convolution.Kernel`
        The kernel for the convolution. One difference from normal convolution
        is that the actual values of the kernel do not matter, except when it
        is zero then it won't use the element for the median computation.
        Each axis of the kernel must be odd.

    mask : `numpy.ndarray`, optional
        Masked values in the ``data``. Elements where the mask is equivalent to
        1 (also ``True``) are interpreted as masked and are ignored during the
        convolution. If not given use the mask of the data or if it has no mask
        either assume all the data is unmasked.

    mad : `bool` or ``"robust"``, optional
        Also calculate the median absolute deviation. If ``"robust"`` is chosen
        multiply the result with approximatly 1.4826.
        Default is ``False``.

    Returns
    -------
    convolved : `numpy.ndarray`
        The convolved array.

    mad : `numpy.ndarray`, optional
        The median absolute deviation of the convolved array. Only returned if
        ``mad`` was True.

    See also
    --------
    interpolate : Numba-based utility to interpolate masked values using \
        weighted convolution.
    interpolate_median : Numba-based utility to interpolate masked values \
        using median convolution.
    convolve : Numba-based utility to convolve using weighted summation.

    scipy.ndimage.median_filter : Fast n-dimensional convolution \
        without masks.

    Notes
    -----
    1. If the ``data`` parameter has a ``mask`` attribute then ``data.data``
       is interpreted as ``data`` and ``array.mask`` as ``mask`` parameter.
       This allows using `~numpy.ma.MaskedArray` objects as ``data`` parameter.

    2. If an explicit ``mask`` is given (even if it is ``None``) an implicit
       mask is ignored.

    3. No border handling is possible, if the kernel extends beyond the
       image these _outside_ values are treated as if they were masked.

    4. Only implemented for 1D, 2D and 3D data.

    Examples
    --------
    It works almost like `convolve` but based on the ``median`` instead of the
    **sum** or **mean** of the elements within the kernel::

        >>> from nddata.utils.numbautils import convolve_median
        >>> import numpy as np

        >>> data = np.ma.array([1,1000,2,1], mask=[0, 1, 0, 0])
        >>> convolve_median(data, [1,1,1])
        array([ 1. ,  1.5,  1.5,  1.5])

    Support for two dimensional arrays and masks is also implemented::

        >>> data = np.arange(9).reshape(3, 3)
        >>> data[1, 1] = 100
        >>> mask = np.zeros((3, 3), dtype=bool)
        >>> mask[1, 1] = 1
        >>> convolve_median(data, np.ones((3,3)), mask)
        array([[ 1.,  2.,  2.],
               [ 3.,  4.,  5.],
               [ 6.,  6.,  7.]])

    And also for three dimensional arrays::

        >>> data = np.arange(27).reshape(3, 3, 3)
        >>> data[0, 0, 0] = 10000
        >>> mask = np.zeros((3, 3, 3))
        >>> mask[0, 0, 0] = 1
        >>> convolve_median(data, np.ones((3, 3, 3)), mask)
        array([[[  9. ,   9. ,   7.5],
                [  9. ,   9. ,   9. ],
                [  9.5,  10. ,  10.5]],
        <BLANKLINE>
               [[ 12. ,  12. ,  12. ],
                [ 13. ,  13.5,  13.5],
                [ 14. ,  14.5,  15. ]],
        <BLANKLINE>
               [[ 15.5,  16. ,  16.5],
                [ 17. ,  17.5,  18. ],
                [ 18.5,  19. ,  19.5]]])

    Explictly using kernel elements to zero excludes those elements for the
    convolution::

        >>> data = np.ma.array([1,1000,2,1], mask=[0, 1, 0, 0])
        >>> convolve_median(data, [1,0,0])
        array([ nan,   1.,  nan,   2.])

    Here only the left element is used for the convolution. For the first
    element the left one is outside the data and for the third element the
    convolution element is masked so both of them result in ``NaN``.

    .. note::
        The median calculation is done by a jitted version based on
        Insertionsort which becomes inefficient for large kernels (more than
        hundred or several hundred elements).

    The function can optionally determine the median absolute deviation of the
    convolved array::

        >>> convolve_median([0, 100, 40, 40], [1, 2, 1], mask=[0, 0, 0, 1],
        ...                 mad=True)
        (array([ 50.,  40.,  70.,  40.]), array([ 50.,  40.,  30.,   0.]))

    or even calculate the robust median absolute deviation::

        >>> convolve_median([0, 100, 40, 40], [1, 2, 1], mask=[0, 0, 0, 1],
        ...                 mad='robust')
        (array([ 50.,  40.,  70.,  40.]),
         array([ 74.13011093,  59.30408874,  44.47806656,   0.        ]))

    which is just the normal deviation multiplied by the constant factor of
    approximatly ``1.4826``.

    .. warning::
        If ``mad=True`` the function returns 2 results instead of one!
    """
    result = _process(data, kernel, mask, 'convolution', True)
    if mad:
        mad_result = _process(data, kernel, mask, 'convolution', True,
                              expected=result)
        if mad == 'robust':
            mad_result *= 1.482602218505602
        return result, mad_result
    else:
        return result

def _process(data, kernel, mask, mode, median=False, expected=None):
    """Convolution and Interpolation processing is much the same before the
    actual heavy lifting is performed. This function combines the processing
    and then invokes the appropriate numba-function.
    """
    if not OPT_DEPS['NUMBA']:  # pragma: no cover
        raise ImportError('{0} requires numba to be installed.'.format(mode))

    if mode not in ['convolution', 'interpolation']:
        raise ValueError('unknown type of mode {0}.'.format(mode))

    # Try to just extract a mask from the data. If it fails with an
    # AttributeError and no mask was specified create an empty boolean array.
    try:
        mask2 = data.mask
    except AttributeError:
        mask2 = None
    else:
        # In case we sucessfully used the mask of the data we either have a
        # numpy.ma.MaskedArray or a NDData instance so we set the data to
        # the data saved as the data attribute.
        data = data.data

    data = convert_to_native_bytorder(data)

    # Only in case no explicit mask was given use the one extracted from the
    # data.
    if mask is ParameterNotSpecified:
        mask = mask2

    # In case we have no mask (None) create an empyt one for convolution. In
    # case of interpolation we can exit early because there is nothing to
    # interpolate.
    if mask is None:
        if mode == 'convolution':
            mask = np.zeros(data.shape, dtype=bool)
        else:
            return data
    # Check if the shape is the same. There might be cases where the
    # array contained a mask attribute but the mask has a different shape
    # than the data!
    else:
        mask = np.asarray(mask)
        if data.shape != mask.shape:
            raise ValueError('data and mask must have the same shape.')

    # It is possible that the kernel is an astropy kernel, in that case it has
    # an attribute "array" and we use that one:
    kernel = convert_to_native_bytorder(getattr(kernel, 'array', kernel))

    # Evaluate how many dimensions the array has, this is needed to find the
    # appropriate convolution or interpolation function.
    ndim = data.ndim

    # kernel must have the same number of dimensions
    if kernel.ndim != ndim:
        raise ValueError('data and kernel must have the same number of '
                         'dimensions.')

    # the kernel also needs to be odd in every dimension.
    if any(i % 2 == 0 for i in kernel.shape):
        raise ValueError('kernel must have an odd shape in each dimension.')

    # Use the dictionary containing the appropriate functions.
    # Median convolution and interpolation has it's own mapping:
    if median:
        if expected is None:
            to_func = MODE_DIM_FUNC_MEDIAN_MAP
        else:
            to_func = MODE_DIM_FUNC_MEDIAN_STD_MAP
    else:
        if expected is None:
            to_func = MODE_DIM_FUNC_MAP
        else:
            to_func = MODE_DIM_FUNC_MAP_STD

    # No need to prepare the expected argument since it was calculated during
    # another convolution so it must be a numpy array with right dimensions and
    # so on.

    # We already checked that the mode is ok therefore any KeyError must happen
    # because the dimension was not supported.
    try:
        if expected is None:
            return to_func[mode][ndim](data, kernel, mask)
        else:
            # in case of interpolation just use the convolution error function.
            # this shouldn't be possible to reach without directly invoking
            # this private function but if I later want to add it it's
            # already here :-)
            return to_func['convolution'][ndim](data, kernel, mask, expected)
    except KeyError:
        raise ValueError('data must not have more than 3 dimensions.')

if OPT_DEPS['NUMBA']:  # pragma: no cover
    from numba import njit

    @njit
    def _rebin_count_conservation(data, old_grid, new_grid):
        """Rebin the data from a given grid to a new grid preserving the \
                total number of counts.

        The function assumes that the grid points represent the center of the
        grid bin and that the bin extends half the distance to the next bin.

        Parameters
        ----------
        data : `numpy.ndarray`
            The data to rebin.

        old_grid : `numpy.ndarray`
            The grid points for the data. Must have the same shape as the data.

        new_grid : `numpy.ndarray`
            The new grid to rebin.

        Returns
        -------
        rebinned_data : `numpy.ndarray`
            The data rebinned from ``old_grid`` to ``new_grid``. It has the
            same shape as ``new_grid``.

        Notes
        -----
        This function is very inefficient it compares each ``old_grid`` point
        to each ``new_grid`` point. Resulting in an overall O(n*m) runtime
        behaviour where n is the number of elements in the old_grid and m the
        number of elements in the new grid. This could be done more efficiently
        but "better now than never".
        """
        # Get the borders and how big each grid bin is
        old_grid_borders, old_grid_extends = _create_borders(old_grid)
        new_grid_borders, new_grid_extends = _create_borders(new_grid)
        # Create an array for the result
        new_data = np.zeros(new_grid.size, dtype=np.float64)
        # Go through the old grid
        for old_idx in range(old_grid.size):
            # Go through the new grid
            for new_idx in range(new_grid.size):
                # Get the bounds for the new and old grid bin
                lower_old = old_grid_borders[old_idx]
                lower_new = new_grid_borders[new_idx]
                upper_old = old_grid_borders[old_idx + 1]
                upper_new = new_grid_borders[new_idx + 1]
                # If the upper boundary of the old grid point is below the
                # lower end of the new grid end we have no overlap.
                if upper_old < lower_new:
                    overlap = 0
                # Same if the lower old end is above the new upper end.
                elif lower_old > upper_new:
                    overlap = 0
                # If the old grid bin is completly inside the new one
                elif lower_old >= lower_new and upper_old <= upper_new:
                    overlap = 1
                # If the new grid bin is completly inside the old one
                elif lower_new >= lower_old and upper_new <= upper_old:
                    overlap = (upper_new - lower_new) / old_grid_extends[old_idx]
                # Partial overlap:
                elif lower_old < lower_new:
                    overlap = (upper_old - lower_new) / old_grid_extends[old_idx]
                else:
                    overlap = (upper_new - lower_old) / old_grid_extends[old_idx]
                new_data[new_idx] += overlap * data[old_idx]

        return new_data

    @njit
    def _create_borders(grid):
        """Create borders and the size between borders for a grid.

        Parameters
        ----------
        grid : `numpy.ndarray`
            The grid for which to create the borders.

        Returns
        -------
        borders : `numpy.ndarray`
            The borders for each grid bin. Has one data point more than
            the grid has points.

        grid_widths : `numpy.ndarray`
            The width of each ``grid`` point.
        """
        borders = np.empty(grid.size + 1, dtype=np.float64)

        # First point needs a special treatement.
        difference = grid[1] - grid[0]
        half_difference = difference / 2
        borders[0] = grid[0] - half_difference

        for i in range(grid.size - 1):
            difference = grid[i+1] - grid[i]
            half_difference = difference / 2
            borders[i+1] = grid[i] + half_difference

        # Last point too needs a special case too.
        # Unfortunatly numba doesn't compile the loop as
        # efficiently if I use the last calculated "half_difference"
        # so it's faster to calculate it again.
        difference = grid[-1] - grid[-2]
        half_difference = difference / 2
        borders[-1] = grid[-1] + half_difference

        return borders, np.diff(borders)

    @njit
    def _interpolate_mask_1d(image, kernel, mask):
        """Determine the weighted average for each masked value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            if mask[i]:
                iimin = max(i - wkx, 0)
                iimax = min(i + wkx + 1, nx)
                num = 0.
                div = 0.
                for ii in range(iimin, iimax, 1):
                    iii = wkx + ii - i
                    if not mask[ii]:
                        num += kernel[iii] * image[ii]
                        div += kernel[iii]
                if div:
                    result[i] = num / div
                else:
                    result[i] = np.nan
            else:
                result[i] = image[i]
        return result

    @njit
    def _interpolate_mask_2d(image, kernel, mask):
        """Determine the weighted average for each masked value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        wkx = nkx // 2
        wky = nky // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            for j in range(0, ny, 1):
                if mask[i, j]:
                    iimin = max(i - wkx, 0)
                    iimax = min(i + wkx + 1, nx)
                    jjmin = max(j - wky, 0)
                    jjmax = min(j + wky + 1, ny)
                    num = 0.
                    div = 0.
                    for ii in range(iimin, iimax, 1):
                        iii = wkx + ii - i
                        for jj in range(jjmin, jjmax, 1):
                            if not mask[ii, jj]:
                                jjj = wky + jj - j
                                num += kernel[iii, jjj] * image[ii, jj]
                                div += kernel[iii, jjj]
                    if div:
                        result[i, j] = num / div
                    else:
                        result[i, j] = np.nan
                else:
                    result[i, j] = image[i, j]
        return result

    @njit
    def _interpolate_mask_3d(image, kernel, mask):
        """Determine the weighted average for each masked value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nz = image.shape[2]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        nkz = kernel.shape[2]
        wkx = nkx // 2
        wky = nky // 2
        wkz = nkz // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            for j in range(0, ny, 1):
                for k in range(0, nz, 1):
                    if mask[i, j, k]:
                        iimin = max(i - wkx, 0)
                        iimax = min(i + wkx + 1, nx)
                        jjmin = max(j - wky, 0)
                        jjmax = min(j + wky + 1, ny)
                        kkmin = max(k - wkz, 0)
                        kkmax = min(k + wkz + 1, nz)
                        num = 0.
                        div = 0.
                        for ii in range(iimin, iimax, 1):
                            iii = wkx + ii - i
                            for jj in range(jjmin, jjmax, 1):
                                jjj = wky + jj - j
                                for kk in range(kkmin, kkmax, 1):
                                    if not mask[ii, jj, kk]:
                                        kkk = wkz + kk - k
                                        num += (kernel[iii, jjj, kkk] *
                                                image[ii, jj, kk])
                                        div += kernel[iii, jjj, kkk]
                        if div:
                            result[i, j, k] = num / div
                        else:
                            result[i, j, k] = np.nan
                    else:
                        result[i, j, k] = image[i, j, k]
        return result

    @njit
    def _convolve_with_mask_1d(image, kernel, mask):
        """Determine the weighted average for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            num = 0.
            div = 0.
            for ii in range(iimin, iimax, 1):
                if not mask[ii]:
                    iii = wkx + ii - i
                    num += kernel[iii] * image[ii]
                    div += kernel[iii]
            if div:
                result[i] = num / div
            else:
                result[i] = np.nan
        return result

    @njit
    def _convolve_with_mask_2d(image, kernel, mask):
        """Determine the weighted average for each value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        wkx = nkx // 2
        wky = nky // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                num = 0.
                div = 0.
                for ii in range(iimin, iimax, 1):
                    iii = wkx + ii - i
                    for jj in range(jjmin, jjmax, 1):
                        if not mask[ii, jj]:
                            jjj = wky + jj - j
                            num += kernel[iii, jjj] * image[ii, jj]
                            div += kernel[iii, jjj]
                if div:
                    result[i, j] = num / div
                else:
                    result[i, j] = np.nan
        return result

    @njit
    def _convolve_with_mask_3d(image, kernel, mask):
        """Determine the weighted average for each value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nz = image.shape[2]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        nkz = kernel.shape[2]
        wkx = nkx // 2
        wky = nky // 2
        wkz = nkz // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                for k in range(0, nz, 1):
                    kkmin = max(k - wkz, 0)
                    kkmax = min(k + wkz + 1, nz)
                    num = 0.
                    div = 0.
                    for ii in range(iimin, iimax, 1):
                        iii = wkx + ii - i
                        for jj in range(jjmin, jjmax, 1):
                            jjj = wky + jj - j
                            for kk in range(kkmin, kkmax, 1):
                                if not mask[ii, jj, kk]:
                                    kkk = wkz + kk - k
                                    num += (kernel[iii, jjj, kkk] *
                                            image[ii, jj, kk])
                                    div += kernel[iii, jjj, kkk]
                    if div:
                        result[i, j, k] = num / div
                    else:
                        result[i, j, k] = np.nan
        return result

    @njit
    def _convolve_with_mask_std_1d(image, kernel, mask, mean):
        """Determine the variance for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            num = 0.
            div = 0.
            for ii in range(iimin, iimax, 1):
                if not mask[ii]:
                    iii = wkx + ii - i
                    # Difference to normal convolution.
                    diff = (image[ii] - mean[i]) ** 2
                    num += diff * kernel[iii]
                    div += kernel[iii]
            if div:
                result[i] = num / div
            else:
                result[i] = np.nan
        return result

    @njit
    def _convolve_with_mask_std_2d(image, kernel, mask, mean):
        """Determine the variance for each value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        wkx = nkx // 2
        wky = nky // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                num = 0.
                div = 0.
                for ii in range(iimin, iimax, 1):
                    iii = wkx + ii - i
                    for jj in range(jjmin, jjmax, 1):
                        if not mask[ii, jj]:
                            jjj = wky + jj - j
                            # Difference to normal convolution.
                            diff = (image[ii, jj] - mean[i, j]) ** 2
                            num += diff * kernel[iii, jjj]
                            div += kernel[iii, jjj]
                if div:
                    result[i, j] = num / div
                else:
                    result[i, j] = np.nan
        return result

    @njit
    def _convolve_with_mask_std_3d(image, kernel, mask, mean):
        """Determine the variance for each value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nz = image.shape[2]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        nkz = kernel.shape[2]
        wkx = nkx // 2
        wky = nky // 2
        wkz = nkz // 2

        result = np.zeros(image.shape, dtype=np.float64)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                for k in range(0, nz, 1):
                    kkmin = max(k - wkz, 0)
                    kkmax = min(k + wkz + 1, nz)
                    num = 0.
                    div = 0.
                    for ii in range(iimin, iimax, 1):
                        iii = wkx + ii - i
                        for jj in range(jjmin, jjmax, 1):
                            jjj = wky + jj - j
                            for kk in range(kkmin, kkmax, 1):
                                if not mask[ii, jj, kk]:
                                    kkk = wkz + kk - k
                                    # Difference to normal convolution.
                                    diff = (image[ii, jj, kk] -
                                            mean[i, j, k]) ** 2
                                    num += diff * kernel[iii, jjj, kkk]
                                    div += kernel[iii, jjj, kkk]
                    if div:
                        result[i, j, k] = num / div
                    else:
                        result[i, j, k] = np.nan
        return result

    MODE_DIM_FUNC_MAP = {'convolution':   {1: _convolve_with_mask_1d,
                                           2: _convolve_with_mask_2d,
                                           3: _convolve_with_mask_3d},

                         'interpolation': {1: _interpolate_mask_1d,
                                           2: _interpolate_mask_2d,
                                           3: _interpolate_mask_3d},
                         }

    MODE_DIM_FUNC_MAP_STD = {'convolution':   {1: _convolve_with_mask_std_1d,
                                               2: _convolve_with_mask_std_2d,
                                               3: _convolve_with_mask_std_3d}
                             }

    @njit
    def insertionsort(items):
        """An insertion sort algorithm based on ``Numba``.

        Parameters
        ----------
        items : `numpy.ndarray`
            The items to be sorted.

        Returns
        -------
        nothing : `None`
            Sorting happens in-place!

        Notes
        -----
        The insertionsort is used to determine the median for the median-based
        convolution and interpolation. Most kernels tend to be small and
        insertionsort has low constant costs and outperforms normal median
        calculation in these cases.

        On my computer they break even only for more than 500 elements:

        .. code::

            from nddata.utils.numbautils import insertionsort
            from numba import njit
            import numpy as np

            @njit
            def numba_median(items):
                return np.median(items)

            # Let them be compiled for the timings
            numba_median(np.random.random(3))
            insertionsort(np.random.random(3))

            for i in [10, 20, 50, 100, 200, 300, 500, 1000]:
                data = np.random.random(i)
                print('-'*20 + str(i))
                # Copy the data so the constant costs are equal
                %timeit data = np.random.random(i); np.median(data)
                %timeit data = np.random.random(i); numba_median(data)
                %timeit data = np.random.random(i); insertionsort(data)
            --------------------10
            10000 loops, best of 3: 175 us per loop
            10000 loops, best of 3: 21.9 us per loop
            100000 loops, best of 3: 18.9 us per loop
            --------------------20
            10000 loops, best of 3: 174 us per loop
            10000 loops, best of 3: 22.9 us per loop
            100000 loops, best of 3: 19.4 us per loop
            --------------------50
            10000 loops, best of 3: 172 us per loop
            10000 loops, best of 3: 24.1 us per loop
            10000 loops, best of 3: 21.1 us per loop
            --------------------100
            10000 loops, best of 3: 173 us per loop
            10000 loops, best of 3: 26.5 us per loop
            10000 loops, best of 3: 26.6 us per loop
            --------------------200
            10000 loops, best of 3: 180 us per loop
            10000 loops, best of 3: 33.1 us per loop
            10000 loops, best of 3: 45.5 us per loop
            --------------------300
            10000 loops, best of 3: 186 us per loop
            10000 loops, best of 3: 39.2 us per loop
            10000 loops, best of 3: 72.8 us per loop
            --------------------500
            1000 loops, best of 3: 202 us per loop
            10000 loops, best of 3: 50.6 us per loop
            10000 loops, best of 3: 154 us per loop
            --------------------1000
            1000 loops, best of 3: 221 us per loop
            10000 loops, best of 3: 80 us per loop
            1000 loops, best of 3: 512 us per loop

        The constant costs of creating a random array do affect all these
        timings and obfuscate that for small arrays (size < 100) the
        insertionsort beats even the numba-jitted version of np.median.
        Therefore I chose the insertionsort appropach in the hope that noone
        would use large kernels for median filtering.
        """
        for i in range(items.size):
            tmp = items[i]
            k = i
            while k > 0 and tmp < items[k - 1]:
                items[k] = items[k - 1]
                k -= 1
            items[k] = tmp

    @njit
    def median_from_sorted(items):
        """Calculate the median from a sorted sequence.

        Parameters
        ----------
        items : `numpy.ndarray`
            The items to be sorted.

        Returns
        -------
        median : number
            The middle element if the items contain an odd number of elements
            or the average of the two middle elements if the length of items
            was even.

        Notes
        -----
        This function is meant to be complementary to the interpolation and
        convolution based on the median. The sorting is done by the
        ``insertionsort`` function (which seems to be faster than np.median)
        and this function calculates the median from this sorted array.

        .. note::
            The sorting must be done before it is not done inside this
            function.
        """
        # Median is the middle element (odd length)
        # or the mean of the two middle elements (even length)
        halfsize = items.size // 2
        if items.size % 2:
            return items[halfsize]
        else:
            return 0.5 * (items[halfsize - 1] + items[halfsize])

    @njit
    def _interpolate_median_mask_1d(image, kernel, mask):
        """Determine the median for each masked value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            if mask[i]:
                iimin = max(i - wkx, 0)
                iimax = min(i + wkx + 1, nx)
                elements = 0
                for ii in range(iimin, iimax, 1):
                    iii = wkx + ii - i
                    if not mask[ii] and kernel[iii]:
                        tmp[elements] = image[ii]
                        elements += 1
                if elements:
                    insertionsort(tmp[:elements])
                    median = median_from_sorted(tmp[:elements])
                    result[i] = median
                else:
                    result[i] = np.nan
            else:
                result[i] = image[i]
        return result

    @njit
    def _interpolate_median_mask_2d(image, kernel, mask):
        """Determine the median for each masked value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        wkx = nkx // 2
        wky = nky // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            for j in range(0, ny, 1):
                if mask[i, j]:
                    iimin = max(i - wkx, 0)
                    iimax = min(i + wkx + 1, nx)
                    jjmin = max(j - wky, 0)
                    jjmax = min(j + wky + 1, ny)
                    elements = 0
                    for ii in range(iimin, iimax, 1):
                        iii = wkx + ii - i
                        for jj in range(jjmin, jjmax, 1):
                            jjj = wky + jj - j
                            if not mask[ii, jj] and kernel[iii, jjj]:
                                tmp[elements] = image[ii, jj]
                                elements += 1
                    if elements:
                        insertionsort(tmp[:elements])
                        median = median_from_sorted(tmp[:elements])
                        result[i, j] = median
                    else:
                        result[i, j] = np.nan
                else:
                    result[i, j] = image[i, j]
        return result

    @njit
    def _interpolate_median_mask_3d(image, kernel, mask):
        """Determine the median for each masked value."""
        nx = image.shape[0]
        ny = image.shape[1]
        nz = image.shape[2]
        nkx = kernel.shape[0]
        nky = kernel.shape[1]
        nkz = kernel.shape[2]
        wkx = nkx // 2
        wky = nky // 2
        wkz = nkz // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            for j in range(0, ny, 1):
                for k in range(0, nz, 1):
                    if mask[i, j, k]:
                        iimin = max(i - wkx, 0)
                        iimax = min(i + wkx + 1, nx)
                        jjmin = max(j - wky, 0)
                        jjmax = min(j + wky + 1, ny)
                        kkmin = max(k - wkz, 0)
                        kkmax = min(k + wkz + 1, nz)
                        elements = 0
                        for ii in range(iimin, iimax, 1):
                            iii = wkx + ii - i
                            for jj in range(jjmin, jjmax, 1):
                                jjj = wky + jj - j
                                for kk in range(kkmin, kkmax, 1):
                                    kkk = wkz + kk - k
                                    if (not mask[ii, jj, kk] and
                                            kernel[iii, jjj, kkk]):
                                        tmp[elements] = image[ii, jj, kk]
                                        elements += 1
                        if elements:
                            insertionsort(tmp[:elements])
                            median = median_from_sorted(tmp[:elements])
                            result[i, j, k] = median
                        else:
                            result[i, j, k] = np.nan
                    else:
                        result[i, j, k] = image[i, j, k]
        return result

    @njit
    def _convolve_median_with_mask_1d(image, kernel, mask):
        """Determine the median for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            elements = 0
            for ii in range(iimin, iimax, 1):
                iii = wkx + ii - i
                if not mask[ii] and kernel[iii]:
                    tmp[elements] = image[ii]
                    elements += 1
            if elements:
                insertionsort(tmp[:elements])
                median = median_from_sorted(tmp[:elements])
                result[i] = median
            else:
                result[i] = np.nan
        return result

    @njit
    def _convolve_median_with_mask_2d(image, kernel, mask):
        """Determine the median for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2
        ny = image.shape[1]
        nky = kernel.shape[1]
        wky = nky // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                elements = 0
                for ii in range(iimin, iimax, 1):
                    iii = wkx + ii - i
                    for jj in range(jjmin, jjmax, 1):
                        jjj = wky + jj - j
                        if not mask[ii, jj] and kernel[iii, jjj]:
                            tmp[elements] = image[ii, jj]
                            elements += 1
                if elements:
                    insertionsort(tmp[:elements])
                    median = median_from_sorted(tmp[:elements])
                    result[i, j] = median
                else:
                    result[i, j] = np.nan
        return result

    @njit
    def _convolve_median_with_mask_3d(image, kernel, mask):
        """Determine the median for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2
        ny = image.shape[1]
        nky = kernel.shape[1]
        wky = nky // 2
        nz = image.shape[2]
        nkz = kernel.shape[2]
        wkz = nkz // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                for k in range(0, nz, 1):
                    kkmin = max(k - wkz, 0)
                    kkmax = min(k + wkz + 1, nz)

                    elements = 0

                    for ii in range(iimin, iimax, 1):
                        iii = wkx + ii - i
                        for jj in range(jjmin, jjmax, 1):
                            jjj = wky + jj - j
                            for kk in range(kkmin, kkmax, 1):
                                kkk = wkz + kk - k
                                if (not mask[ii, jj, kk] and
                                        kernel[iii, jjj, kkk]):
                                    tmp[elements] = image[ii, jj, kk]
                                    elements += 1
                    if elements:
                        insertionsort(tmp[:elements])
                        median = median_from_sorted(tmp[:elements])
                        result[i, j, k] = median
                    else:
                        result[i, j, k] = np.nan
        return result

    @njit
    def _convolve_median_with_mask_std_1d(image, kernel, mask, mean):
        """Determine the median absolute deviation for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            elements = 0
            for ii in range(iimin, iimax, 1):
                iii = wkx + ii - i
                if not mask[ii] and kernel[iii]:
                    # Difference to normal convolution.
                    tmp[elements] = abs(image[ii] - mean[i])
                    elements += 1
            if elements:
                insertionsort(tmp[:elements])
                median = median_from_sorted(tmp[:elements])
                result[i] = median
            else:
                result[i] = np.nan
        return result

    @njit
    def _convolve_median_with_mask_std_2d(image, kernel, mask, mean):
        """Determine the median absolute deviation for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2
        ny = image.shape[1]
        nky = kernel.shape[1]
        wky = nky // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                elements = 0
                for ii in range(iimin, iimax, 1):
                    iii = wkx + ii - i
                    for jj in range(jjmin, jjmax, 1):
                        jjj = wky + jj - j
                        if not mask[ii, jj] and kernel[iii, jjj]:
                            # Difference to normal convolution.
                            tmp[elements] = abs(image[ii, jj] - mean[i, j])
                            elements += 1
                if elements:
                    insertionsort(tmp[:elements])
                    median = median_from_sorted(tmp[:elements])
                    result[i, j] = median
                else:
                    result[i, j] = np.nan
        return result

    @njit
    def _convolve_median_with_mask_std_3d(image, kernel, mask, mean):
        """Determine the median absolute deviation for each value."""
        nx = image.shape[0]
        nkx = kernel.shape[0]
        wkx = nkx // 2
        ny = image.shape[1]
        nky = kernel.shape[1]
        wky = nky // 2
        nz = image.shape[2]
        nkz = kernel.shape[2]
        wkz = nkz // 2

        result = np.zeros(image.shape, dtype=np.float64)
        tmp = np.zeros(kernel.size, dtype=image.dtype)

        for i in range(0, nx, 1):
            iimin = max(i - wkx, 0)
            iimax = min(i + wkx + 1, nx)
            for j in range(0, ny, 1):
                jjmin = max(j - wky, 0)
                jjmax = min(j + wky + 1, ny)
                for k in range(0, nz, 1):
                    kkmin = max(k - wkz, 0)
                    kkmax = min(k + wkz + 1, nz)

                    elements = 0

                    for ii in range(iimin, iimax, 1):
                        iii = wkx + ii - i
                        for jj in range(jjmin, jjmax, 1):
                            jjj = wky + jj - j
                            for kk in range(kkmin, kkmax, 1):
                                kkk = wkz + kk - k
                                if (not mask[ii, jj, kk] and
                                        kernel[iii, jjj, kkk]):
                                    # Difference to normal convolution.
                                    tmp[elements] = abs(image[ii, jj, kk] -
                                                        mean[i, j, k])
                                    elements += 1
                    if elements:
                        insertionsort(tmp[:elements])
                        median = median_from_sorted(tmp[:elements])
                        result[i, j, k] = median
                    else:
                        result[i, j, k] = np.nan
        return result

    MODE_DIM_FUNC_MEDIAN_MAP = {
        'convolution':   {1: _convolve_median_with_mask_1d,
                          2: _convolve_median_with_mask_2d,
                          3: _convolve_median_with_mask_3d},

        'interpolation': {1: _interpolate_median_mask_1d,
                          2: _interpolate_median_mask_2d,
                          3: _interpolate_median_mask_3d},
        }

    MODE_DIM_FUNC_MEDIAN_STD_MAP = {
        'convolution':   {1: _convolve_median_with_mask_std_1d,
                          2: _convolve_median_with_mask_std_2d,
                          3: _convolve_median_with_mask_std_3d},
        }


text:
#ifdef __cplusplus
extern "C"
{
#endif

struct things {
    char c;
    short s;
    int i;
};

typedef enum func{
   mykernel,
   mykernel1,
   mykernel2,
   mykernel3,
   mykernel4
}func;

extern const void* getKernelFunc(enum func f);

#ifdef __cplusplus
}
#endif

redpajama_set_name: RedPajamaGithub


text:
#include <qpa/qplatformintegrationplugin.h>
#include "qopenwfdintegration.h"

QT_BEGIN_NAMESPACE

class QOpenWFDIntegrationPlugin : public QPlatformIntegrationPlugin
{
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformIntegrationFactoryInterface.5.2")
public:
    QPlatformIntegration *create(const QString&, const QStringList&);
};

QPlatformIntegration* QOpenWFDIntegrationPlugin::create(const QString& system, const QStringList& paramList)
{
    Q_UNUSED(paramList);
    if (!system.compare(QLatin1String("openwfd"), Qt::CaseInsensitive))
        return new QOpenWFDIntegration;

    return 0;
}

QT_END_NAMESPACE


text:
@class NSArray;

@interface WFAnnouncementPrefs : NSObject
{
    BOOL enabled;
    BOOL fadeBackgroundMusic;
    float volume;
    NSArray *announcements;
    int voice;
    BOOL _metric;
}

@property(nonatomic, getter=isMetric) BOOL metric; // @synthesize metric=_metric;
@property(copy, nonatomic) NSArray *announcements; // @synthesize announcements;
@property(nonatomic) int voice; // @synthesize voice;
@property(nonatomic) BOOL fadeBackgroundMusic; // @synthesize fadeBackgroundMusic;
@property(nonatomic) float volume; // @synthesize volume;
@property(nonatomic) BOOL enabled; // @synthesize enabled;
- (void).cxx_destruct;
- (void)setVoiceNumber:(id)arg1;
- (id)voiceNumber;
- (id)debugDescription;
- (void)encodeToDictionary:(id)arg1;
- (void)removeAnnouncement:(id)arg1;
- (void)addAnnouncements:(id)arg1;
- (void)addAnnouncement:(id)arg1;
- (void)clearAnnouncements;
- (id)initWithDefaultsDictionary:(id)arg1;
- (id)init;

@end


text:
package sql

import (
	"sync"

	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
)

// PipelineQueue maintains a set of planNodes running with pipelined execution.
// It uses a DependencyAnalyzer to determine dependencies between plans. Using
// this knowledge, the queue provides the following guarantees about the execution
// of plans:
// 1. No two plans will ever be run concurrently if they are dependent of one another.
// 2. If two dependent plans are added to the queue, the plan added first will be
//    executed before the plan added second.
// 3. No plans will begin execution once an error has been seen until Wait is
//    called to drain the plans and reset the error state.
//
// The queue performs all computation on pointers to planNode interfaces. This is
// because it wants to operate on unique objects, and equality of interfaces does
// not necessarily imply pointer equality.
type PipelineQueue struct {
	// analyzer is a DependencyAnalyzer that computes when certain plans are dependent
	// on one another. It determines if it is safe to run plans concurrently.
	analyzer DependencyAnalyzer

	// plans is a set of all running and pending plans, with their corresponding "done"
	// channels. These channels are closed when the plan has finished executing.
	plans map[*planNode]doneChan

	// err is the first error seen since the last call to PipelineQueue.Wait. Referred to
	// as the current "pipeline batch's error".
	err error

	mu           syncutil.Mutex
	runningGroup sync.WaitGroup
}

// doneChan is a channel that is closed when a plan finishes execution.
type doneChan chan struct{}

// MakePipelineQueue creates a new empty PipelineQueue that uses the provided
// DependencyAnalyzer to determine plan dependencies.
func MakePipelineQueue(analyzer DependencyAnalyzer) PipelineQueue {
	return PipelineQueue{
		plans:    make(map[*planNode]doneChan),
		analyzer: analyzer,
	}
}

// Add inserts a new plan in the queue and executes the provided function when
// appropriate, obeying the guarantees made by the PipelineQueue.
//
// Add should not be called concurrently with Wait. See Wait's comment for more
// details.
func (pq *PipelineQueue) Add(plan *planNode, exec func() error) {
	prereqs, finishLocked := pq.insertInQueue(plan)
	pq.runningGroup.Add(1)
	go func() {
		defer pq.runningGroup.Done()

		// Block on the execution of each prerequisite plan blocking us.
		for _, prereq := range prereqs {
			<-prereq
		}

		// Don't bother executing if an error has already been set.
		if abort := func() bool {
			pq.mu.Lock()
			defer pq.mu.Unlock()
			if pq.err != nil {
				finishLocked()
				return true
			}
			return false
		}(); abort {
			return
		}

		// Execute the plan.
		err := exec()

		pq.mu.Lock()
		defer pq.mu.Unlock()

		if pq.err == nil {
			// If we have not already seen an error since the last Wait, set the
			// error state.
			pq.err = err
		}

		finishLocked()
	}()
}

// insertInQueue inserts the planNode in the queue. It returns a list of the "done"
// channels of prerequisite blocking the new plan from executing. It also returns a
// function to call when the new plan has finished executing. This function must be
// called while pq.mu is held.
func (pq *PipelineQueue) insertInQueue(newPlan *planNode) ([]doneChan, func()) {
	pq.mu.Lock()
	defer pq.mu.Unlock()

	// Determine the set of prerequisite plans.
	prereqs := pq.prereqsForPlanLocked(newPlan)

	// Insert newPlan in running set.
	newDoneChan := make(doneChan)
	pq.plans[newPlan] = newDoneChan
	finish := func() {
		// Remove the current plan from the running set and signal to dependent
		// plans that we're done by closing our done channel.
		delete(pq.plans, newPlan)
		close(newDoneChan)
	}
	return prereqs, finish
}

// prereqsForPlanLocked determines the set of plans currently running and pending
// that a new plan is dependent on, It returns a slice of doneChans for each plan
// in this set. Returns a nil slice if the plan has no prerequisites and can be run
// immediately.
func (pq *PipelineQueue) prereqsForPlanLocked(newPlan *planNode) []doneChan {
	// Add all plans from the plan set that this new plan is dependent on.
	var prereqs []doneChan
	for plan, doneChan := range pq.plans {
		if !pq.analyzer.Independent(plan, newPlan) {
			prereqs = append(prereqs, doneChan)
		}
	}
	return prereqs
}

// Len returns the number of plans in the PipelineQueue.
func (pq *PipelineQueue) Len() int {
	pq.mu.Lock()
	defer pq.mu.Unlock()
	return len(pq.plans)
}

// Wait blocks until the PipelineQueue finishes executing all plans. It then
// returns the error of the last batch of pipelined execution before reseting
// the error to allow for future use.
//
// Wait can not be called concurrently with Add. If we need to lift this
// restriction, consider replacing the sync.WaitGroup with a syncutil.RWMutex,
// which will provide the desired starvation and ordering properties. Those
// being that once Wait is called, future Adds will not be reordered ahead
// of Waits attempts to drain all running and pending plans.
func (pq *PipelineQueue) Wait() error {
	pq.runningGroup.Wait()

	// There is no race condition between waiting on the WaitGroup and locking
	// the mutex because PipelineQueue.Wait cannot be called concurrently with
	// Add. We lock only because Err may be called concurrently.
	pq.mu.Lock()
	defer pq.mu.Unlock()
	err := pq.err
	pq.err = nil
	return err
}

// Err returns the PipelineQueue's error.
func (pq *PipelineQueue) Err() error {
	pq.mu.Lock()
	defer pq.mu.Unlock()
	return pq.err
}

// DependencyAnalyzer determines if plans are independent of one another, where
// independent plans are defined by whether their execution could be safely reordered
// without having an effect on their runtime semantics or on their results. This
// means that DependencyAnalyzer can be used to test whether it is safe for multiple
// statements to be run concurrently by the PipelineQueue.
type DependencyAnalyzer interface {
	// Independent determines if the provided planNodes are independent from one
	// another. Implementations of Independent are always commutative.
	Independent(*planNode, *planNode) bool
}

// dependencyAnalyzerFunc is an implementation of DependencyAnalyzer that defers
// to a function for all dependency decisions.
type dependencyAnalyzerFunc func(*planNode, *planNode) bool

func (f dependencyAnalyzerFunc) Independent(p1 *planNode, p2 *planNode) bool {
	return f(p1, p2)
}

// NoDependenciesAnalyzer is a DependencyAnalyzer that performs no analysis on
// planNodes and asserts that all plans are independent.
var NoDependenciesAnalyzer DependencyAnalyzer = dependencyAnalyzerFunc(func(
	_ *planNode, _ *planNode,
) bool {
	return true
})


text:
.class public Lcom/android/internal/telephony/CallerInfoAsyncQuery;
.super Ljava/lang/Object;
.source "CallerInfoAsyncQuery.java"

# annotations
.annotation system Ldalvik/annotation/MemberClasses;
    value = {
        Lcom/android/internal/telephony/CallerInfoAsyncQuery$1;,
        Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;,
        Lcom/android/internal/telephony/CallerInfoAsyncQuery$QueryPoolException;,
        Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;,
        Lcom/android/internal/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;
    }
.end annotation

# static fields
.field private static final DBG:Z = false

.field private static final ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION:Z = true

.field private static final EVENT_ADD_LISTENER:I = 0x2

.field private static final EVENT_EMERGENCY_NUMBER:I = 0x4

.field private static final EVENT_END_OF_QUEUE:I = 0x3

.field private static final EVENT_NEW_QUERY:I = 0x1

.field private static final EVENT_VOICEMAIL_NUMBER:I = 0x5

.field private static final LOG_TAG:Ljava/lang/String; = "CallerInfoAsyncQuery"

# instance fields
.field private mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

# direct methods
.method private constructor <init>()V
    .locals 0

    .prologue
    .line 328
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    .line 329
    return-void
.end method

.method static synthetic access$000(Lcom/android/internal/telephony/CallerInfoAsyncQuery;)V
    .locals 0
    .parameter "x0"

    .prologue
    .line 50
    invoke-direct {p0}, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->release()V

    return-void
.end method

.method private allocate(Landroid/content/Context;Landroid/net/Uri;)V
    .locals 2
    .parameter "context"
    .parameter "contactRef"

    .prologue
    .line 496
    if-eqz p1, :cond_0

    if-nez p2, :cond_1

    .line 497
    :cond_0
    new-instance v0, Lcom/android/internal/telephony/CallerInfoAsyncQuery$QueryPoolException;

    const-string v1, "Bad context or query uri."

    invoke-direct {v0, v1}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$QueryPoolException;-><init>(Ljava/lang/String;)V

    throw v0

    .line 499
    :cond_1
    new-instance v0, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    const/4 v1, 0x0

    invoke-direct {v0, p0, p1, v1}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;-><init>(Lcom/android/internal/telephony/CallerInfoAsyncQuery;Landroid/content/Context;Lcom/android/internal/telephony/CallerInfoAsyncQuery$1;)V

    iput-object v0, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    .line 500
    iget-object v0, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    #setter for: Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->mQueryContext:Landroid/content/Context;
    invoke-static {v0, p1}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->access$302(Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;Landroid/content/Context;)Landroid/content/Context;

    .line 501
    iget-object v0, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    #setter for: Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->mQueryUri:Landroid/net/Uri;
    invoke-static {v0, p2}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->access$402(Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;Landroid/net/Uri;)Landroid/net/Uri;

    .line 502
    return-void
.end method

.method private release()V
    .locals 2

    .prologue
    const/4 v1, 0x0

    .line 508
    iget-object v0, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    #setter for: Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->mQueryContext:Landroid/content/Context;
    invoke-static {v0, v1}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->access$302(Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;Landroid/content/Context;)Landroid/content/Context;

    .line 509
    iget-object v0, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    #setter for: Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->mQueryUri:Landroid/net/Uri;
    invoke-static {v0, v1}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->access$402(Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;Landroid/net/Uri;)Landroid/net/Uri;

    .line 510
    iget-object v0, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    #setter for: Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->mCallerInfo:Lcom/android/internal/telephony/CallerInfo;
    invoke-static {v0, v1}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->access$502(Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;Lcom/android/internal/telephony/CallerInfo;)Lcom/android/internal/telephony/CallerInfo;

    .line 511
    iput-object v1, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    .line 512
    return-void
.end method

.method private static sanitizeUriToString(Landroid/net/Uri;)Ljava/lang/String;
    .locals 4
    .parameter "uri"

    .prologue
    .line 515
    if-eqz p0, :cond_1

    .line 516
    invoke-virtual {p0}, Landroid/net/Uri;->toString()Ljava/lang/String;

    move-result-object v1

    .line 517
    .local v1, uriString:Ljava/lang/String;
    const/16 v2, 0x2f

    invoke-virtual {v1, v2}, Ljava/lang/String;->lastIndexOf(I)I

    move-result v0

    .line 518
    .local v0, indexOfLastSlash:I
    if-lez v0, :cond_0

    .line 519
    new-instance v2, Ljava/lang/StringBuilder;

    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V

    const/4 v3, 0x0

    invoke-virtual {v1, v3, v0}, Ljava/lang/String;->substring(II)Ljava/lang/String;

    move-result-object v3

    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v2

    const-string v3, "/xxxxxxx"

    invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v2

    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v1

    .line 524
    .end local v0           #indexOfLastSlash:I
    .end local v1           #uriString:Ljava/lang/String;
    :cond_0
    :goto_0
    return-object v1

    :cond_1
    const-string v1, ""

    goto :goto_0
.end method

.method public static startQuery(ILandroid/content/Context;Landroid/net/Uri;Lcom/android/internal/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;Ljava/lang/Object;)Lcom/android/internal/telephony/CallerInfoAsyncQuery;
    .locals 9
    .parameter "token"
    .parameter "context"
    .parameter "contactRef"
    .parameter "listener"
    .parameter "cookie"

    .prologue
    const/4 v4, 0x0

    .line 338
    new-instance v8, Lcom/android/internal/telephony/CallerInfoAsyncQuery;

    invoke-direct {v8}, Lcom/android/internal/telephony/CallerInfoAsyncQuery;-><init>()V

    .line 339
    .local v8, c:Lcom/android/internal/telephony/CallerInfoAsyncQuery;
    invoke-direct {v8, p1, p2}, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->allocate(Landroid/content/Context;Landroid/net/Uri;)V

    .line 344
    new-instance v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;

    invoke-direct {v2, v4}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;-><init>(Lcom/android/internal/telephony/CallerInfoAsyncQuery$1;)V

    .line 345
    .local v2, cw:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;
    iput-object p3, v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->listener:Lcom/android/internal/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;

    .line 346
    iput-object p4, v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->cookie:Ljava/lang/Object;

    .line 347
    const/4 v0, 0x1

    iput v0, v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->event:I

    .line 349
    iget-object v0, v8, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    move v1, p0

    move-object v3, p2

    move-object v5, v4

    move-object v6, v4

    move-object v7, v4

    invoke-virtual/range {v0 .. v7}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->startQuery(ILjava/lang/Object;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V

    .line 351
    return-object v8
.end method

.method public static startQuery(ILandroid/content/Context;Ljava/lang/String;Lcom/android/internal/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;Ljava/lang/Object;)Lcom/android/internal/telephony/CallerInfoAsyncQuery;
    .locals 14
    .parameter "token"
    .parameter "context"
    .parameter "number"
    .parameter "listener"
    .parameter "cookie"

    .prologue
    .line 379
    invoke-static/range {p2 .. p2}, Landroid/telephony/PhoneNumberUtils;->isUriNumber(Ljava/lang/String;)Z

    move-result v1

    if-eqz v1, :cond_1

    .line 384
    sget-object v4, Landroid/provider/ContactsContract$Data;->CONTENT_URI:Landroid/net/Uri;

    .line 400
    .local v4, contactRef:Landroid/net/Uri;
    const-string/jumbo v6, "upper(data1)=? AND mimetype=\'vnd.android.cursor.item/sip_address\'"

    .line 403
    .local v6, selection:Ljava/lang/String;
    const/4 v1, 0x1

    new-array v7, v1, [Ljava/lang/String;

    const/4 v1, 0x0

    invoke-virtual/range {p2 .. p2}, Ljava/lang/String;->toUpperCase()Ljava/lang/String;

    move-result-object v2

    aput-object v2, v7, v1

    .line 442
    .local v7, selectionArgs:[Ljava/lang/String;
    :cond_0
    :goto_0
    new-instance v9, Lcom/android/internal/telephony/CallerInfoAsyncQuery;

    invoke-direct {v9}, Lcom/android/internal/telephony/CallerInfoAsyncQuery;-><init>()V

    .line 443
    .local v9, c:Lcom/android/internal/telephony/CallerInfoAsyncQuery;
    invoke-direct {v9, p1, v4}, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->allocate(Landroid/content/Context;Landroid/net/Uri;)V

    .line 446
    new-instance v3, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;

    const/4 v1, 0x0

    invoke-direct {v3, v1}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;-><init>(Lcom/android/internal/telephony/CallerInfoAsyncQuery$1;)V

    .line 447
    .local v3, cw:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;
    move-object/from16 v0, p3

    iput-object v0, v3, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->listener:Lcom/android/internal/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;

    .line 448
    move-object/from16 v0, p4

    iput-object v0, v3, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->cookie:Ljava/lang/Object;

    .line 449
    move-object/from16 v0, p2

    iput-object v0, v3, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->number:Ljava/lang/String;

    .line 452
    move-object/from16 v0, p2

    invoke-static {v0, p1}, Landroid/telephony/PhoneNumberUtils;->isLocalEmergencyNumber(Ljava/lang/String;Landroid/content/Context;)Z

    move-result v1

    if-eqz v1, :cond_2

    .line 453
    const/4 v1, 0x4

    iput v1, v3, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->event:I

    .line 464
    :goto_1
    iget-object v1, v9, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    const/4 v5, 0x0

    const/4 v8, 0x0

    move v2, p0

    invoke-virtual/range {v1 .. v8}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->startQuery(ILjava/lang/Object;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V

    .line 471
    return-object v9

    .line 407
    .end local v3           #cw:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;
    .end local v4           #contactRef:Landroid/net/Uri;
    .end local v6           #selection:Ljava/lang/String;
    .end local v7           #selectionArgs:[Ljava/lang/String;
    .end local v9           #c:Lcom/android/internal/telephony/CallerInfoAsyncQuery;
    :cond_1
    sget-object v1, Landroid/provider/ContactsContract$PhoneLookup;->CONTENT_FILTER_URI:Landroid/net/Uri;

    invoke-static/range {p2 .. p2}, Landroid/net/Uri;->encode(Ljava/lang/String;)Ljava/lang/String;

    move-result-object v2

    invoke-static {v1, v2}, Landroid/net/Uri;->withAppendedPath(Landroid/net/Uri;Ljava/lang/String;)Landroid/net/Uri;

    move-result-object v4

    .line 408
    .restart local v4       #contactRef:Landroid/net/Uri;
    const/4 v6, 0x0

    .line 409
    .restart local v6       #selection:Ljava/lang/String;
    const/4 v7, 0x0

    .line 412
    .restart local v7       #selectionArgs:[Ljava/lang/String;
    invoke-static {}, Lcom/sec/android/app/CscFeature;->getInstance()Lcom/sec/android/app/CscFeature;

    move-result-object v1

    const-string v2, "CscFeature_Contact_EnableDynCallerIdMatchingDigitWithAutoSim"

    invoke-virtual {v1, v2}, Lcom/sec/android/app/CscFeature;->getEnableStatus(Ljava/lang/String;)Z

    move-result v1

    if-eqz v1, :cond_0

    .line 414
    invoke-static {p1}, Landroid/telephony/PhoneNumberUtils;->getLTNContactsMatchLength(Landroid/content/Context;)I

    move-result v11

    .line 416
    .local v11, mMatchLen:I
    const-string v1, "CallerInfoAsyncQuery"

    new-instance v2, Ljava/lang/StringBuilder;

    invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V

    const-string v5, "mMatchLen= "

    invoke-virtual {v2, v5}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v2

    invoke-virtual {v2, v11}, Ljava/lang/StringBuilder;->append(I)Ljava/lang/StringBuilder;

    move-result-object v2

    invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v2

    invoke-static {v1, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    .line 418
    invoke-virtual/range {p2 .. p2}, Ljava/lang/String;->length()I

    move-result v12

    .line 420
    .local v12, nLen:I
    const/4 v10, 0x0

    .line 421
    .local v10, mIsSpecialNum:Z
    invoke-static/range {p2 .. p2}, Landroid/telephony/PhoneNumberUtils;->isLTNSpecialNumber(Ljava/lang/String;)Z

    move-result v10

    .line 423
    if-le v12, v11, :cond_0

    if-nez v10, :cond_0

    .line 425
    const-string v1, "content://com.android.contacts/phone_lookup_n"

    invoke-static {v1}, Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;

    move-result-object v13

    .line 426
    .local v13, phoneLookup:Landroid/net/Uri;
    invoke-virtual {v13}, Landroid/net/Uri;->buildUpon()Landroid/net/Uri$Builder;

    move-result-object v1

    invoke-static {v11}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;

    move-result-object v2

    invoke-virtual {v1, v2}, Landroid/net/Uri$Builder;->appendEncodedPath(Ljava/lang/String;)Landroid/net/Uri$Builder;

    move-result-object v1

    move-object/from16 v0, p2

    invoke-virtual {v1, v0}, Landroid/net/Uri$Builder;->appendPath(Ljava/lang/String;)Landroid/net/Uri$Builder;

    move-result-object v1

    invoke-virtual {v1}, Landroid/net/Uri$Builder;->build()Landroid/net/Uri;

    move-result-object v4

    goto/16 :goto_0

    .line 454
    .end local v10           #mIsSpecialNum:Z
    .end local v11           #mMatchLen:I
    .end local v12           #nLen:I
    .end local v13           #phoneLookup:Landroid/net/Uri;
    .restart local v3       #cw:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;
    .restart local v9       #c:Lcom/android/internal/telephony/CallerInfoAsyncQuery;
    :cond_2
    invoke-static/range {p2 .. p2}, Landroid/telephony/PhoneNumberUtils;->isVoiceMailNumber(Ljava/lang/String;)Z

    move-result v1

    if-eqz v1, :cond_3

    .line 455
    const/4 v1, 0x5

    iput v1, v3, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->event:I

    goto :goto_1

    .line 457
    :cond_3
    const/4 v1, 0x1

    iput v1, v3, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->event:I

    goto :goto_1
.end method

# virtual methods
.method public addQueryListener(ILcom/android/internal/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;Ljava/lang/Object;)V
    .locals 8
    .parameter "token"
    .parameter "listener"
    .parameter "cookie"

    .prologue
    const/4 v3, 0x0

    .line 483
    new-instance v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;

    invoke-direct {v2, v3}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;-><init>(Lcom/android/internal/telephony/CallerInfoAsyncQuery$1;)V

    .line 484
    .local v2, cw:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;
    iput-object p2, v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->listener:Lcom/android/internal/telephony/CallerInfoAsyncQuery$OnQueryCompleteListener;

    .line 485
    iput-object p3, v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->cookie:Ljava/lang/Object;

    .line 486
    const/4 v0, 0x2

    iput v0, v2, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;->event:I

    .line 488
    iget-object v0, p0, Lcom/android/internal/telephony/CallerInfoAsyncQuery;->mHandler:Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;

    move v1, p1

    move-object v4, v3

    move-object v5, v3

    move-object v6, v3

    move-object v7, v3

    invoke-virtual/range {v0 .. v7}, Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;->startQuery(ILjava/lang/Object;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V

    .line 489
    return-void
.end method


text:
(function(){
    var ports = [];

    module.exports = {
        register: function(port){
            var _self = this;
            port = +port;
            if(!_self.isRegistered(port)){
                ports.push(port);
            }
        },

        isRegistered: function(port){
            var registered = false;
            port = +port;
            ports.forEach(function(p, i){
                if(registered){
                    return;
                }
                if(port === p){
                    registered = true;
                }
            });
            return registered;
        }
    };
}());