def iter_linear_fit(xy, uv, wxy=None, wuv=None, fitgeom='general', center=None, nclip=3, sigma=(3.0, 'rmse'), clip_accum=False):
    '\n    Compute linear transformation parameters that "best" (in the sense of\n    minimizing residuals) transform ``uv`` source position to ``xy``\n    sources iteratively using sigma-clipping.\n\n    More precisely, this functions attempts to find a ``2x2`` matrix ``F`` and\n    a shift vector ``s`` that minimize the residuals between the *transformed*\n    reference source coordinates ``uv``\n\n    .. math::\n        \\mathbf{xy}\'_k = \\mathbf{F}\\cdot(\\mathbf{uv}_k-\\mathbf{c})+\\\n        \\mathbf{s} + \\mathbf{c}\n        :label: ilf1\n\n    and the "observed" source positions ``xy``:\n\n    .. math::\n        \\epsilon^2 = \\Sigma_k w_k \\|\\mathbf{xy}_k-\\mathbf{xy}\'_k\\|^2.\n        :label: ilf2\n\n    In the above equations, :math:`\\mathbf{F}` is a ``2x2`` matrix while\n    :math:`\\mathbf{xy}_k` and :math:`\\mathbf{uv}_k` are the position\n    coordinates of the ``k``-th source (row in input ``xy`` and ``uv`` arrays).\n\n    One of the two catalogs (``xy`` or ``uv``) contains what we refer to as\n    "image" source positions and the other one as "reference" source positions.\n    The meaning assigned to ``xy`` and ``uv`` parameters are up to the\n    caller of this function.\n\n    Parameters\n    ----------\n    xy: numpy.ndarray\n        A ``(N, 2)``-shaped array of source positions (one 2-coordinate\n        position per line).\n\n    uv: numpy.ndarray\n        A ``(N, 2)``-shaped array of source positions (one 2-coordinate\n        position per line). This array *must have* the same length (shape)\n        as the ``xy`` array.\n\n    wxy: numpy.ndarray, None, optional\n        A 1-dimensional array of weights of the same length (``N``)\n        as ``xy`` array indicating how much a given coordinate should be\n        weighted in the fit. If not provided or set to `None`, all positions\n        will be contribute equally to the fit if ``wuv`` is also set to `None`.\n        See ``Notes`` section for more details.\n\n    wuv: numpy.ndarray, None, optional\n        A 1-dimensional array of weights of the same length (``N``)\n        as ``xy`` array indicating how much a given coordinate should be\n        weighted in the fit. If not provided or set to `None`, all positions\n        will be contribute equally to the fit if ``wxy`` is also set to `None`.\n        See ``Notes`` section for more details.\n\n    fitgeom: {\'shift\', \'rscale\', \'general\'}, optional\n        The fitting geometry to be used in fitting the matched object lists.\n        This parameter is used in fitting the shifts (offsets), rotations\n        and/or scale changes from the matched object lists. The \'general\'\n        fit geometry allows for independent scale and rotation for each axis.\n\n    center: tuple, list, numpy.ndarray, None, optional\n        A list-like container with two ``X``- and ``Y``-positions of the center\n        (origin) of rotations in the ``uv`` and ``xy`` coordinate frames.\n        If not provided, ``center`` is estimated as a (weighted) mean position\n        in the ``uv`` frame.\n\n    nclip: int, None, optional\n        Number (a non-negative integer) of clipping iterations in fit.\n        Clipping will be turned off if ``nclip`` is either `None` or 0.\n\n    sigma: float, tuple of the form (float, str), optional\n        When a tuple is provided, first value (a positive number)\n        indicates the number of "fit error estimates" to use for clipping.\n        The second value (a string) indicates the statistic to be\n        used for "fit error estimate". Currently the following values are\n        supported: ``\'rmse\'``, ``\'mae\'``, and ``\'std\'``\n        - see ``Notes`` section for more details.\n\n        When ``sigma`` is a single number, it must be a positive number and\n        the default error estimate ``\'rmse\'`` is assumed.\n\n        This parameter is ignored when ``nclip`` is either `None` or 0.\n\n    clip_accum: bool, optional\n        Indicates whether or not to reset the list of "bad" (clipped out)\n        sources after each clipping iteration. When set to `True` the list\n        only grows with each iteration as "bad" positions never re-enter the\n        pool of available position for the fit. By default the list of\n        "bad" source positions is purged at each iteration.\n\n    Returns\n    -------\n    fit: dict\n        - ``\'shift\'``: A ``numpy.ndarray`` with two components of the\n          computed shift.\n        - ``\'shift_ld\'``: A ``numpy.ndarray`` with two components of the\n          computed shift of type ``numpy.longdouble``.\n        - ``\'matrix\'``: A ``2x2`` ``numpy.ndarray`` with the computed\n          generalized rotation matrix.\n        - ``\'matrix_ld\'``: A ``2x2`` ``numpy.ndarray`` with the computed\n          generalized rotation matrix of type ``numpy.longdouble``.\n        - ``\'proper_rot\'``: Rotation angle (degree) as if the rotation is\n          proper.\n        - ``\'rot\'``: A tuple of ``(rotx, roty)`` - the rotation angles with\n          regard to the ``X`` and ``Y`` axes.\n        - ``\'<rot>\'``: *Arithmetic mean* of the angles of rotation around\n          ``X`` and ``Y`` axes.\n        - ``\'scale\'``: A tuple of ``(sx, sy)`` - scale change in the direction\n          of the ``X`` and ``Y`` axes.\n        - ``\'<scale>\'``: *Geometric mean* of scales ``sx`` and ``sy``.\n        - ``\'skew\'``: Computed skew.\n        - ``\'proper\'``: a boolean indicating whether the rotation is proper.\n        - ``\'fitgeom\'``: Fit geometry (allowed transformations) used for\n          fitting data (to minimize residuals). This is copy of the input\n          argument ``fitgeom``.\n        - ``\'center\'``: Center of rotation\n        - ``\'center_ld\'``: Center of rotation as a ``numpy.longdouble``.\n        - ``\'fitmask\'``: A boolean array indicating which source positions\n          where used for fitting (`True`) and which were clipped out\n          (`False`). **NOTE** For weighted fits, positions with zero\n          weights are automatically excluded from the fits.\n        - ``\'eff_nclip\'``: Effective number of clipping iterations\n        - ``\'rmse\'``: Root-Mean-Square Error\n        - ``\'mae\'``: Mean Absolute Error\n        - ``\'std\'``: Standard Deviation of the residuals\n        - ``\'resids\'``: An array of residuals of the fit.\n          **NOTE:** Only the residuals for the "valid" points are reported\n          here. Therefore the length of this array may be smaller than the\n          length of input arrays of positions.\n\n    Notes\n    -----\n    **Weights**\n\n    Weights can be provided for both "image" source positions and "reference"\n    source positions. When no weights are given, all positions are weighted\n    equally. When only one set of positions have weights (i.e., either ``wxy``\n    or ``wuv`` is not `None`) then weights in :eq:`ilf2` are set to be equal\n    to the provided set of weights. When weights for *both* "image" source\n    positions and "reference" source positions are provided, then the\n    combined weight that is used in :eq:`ilf2` is computed as:\n\n    .. math::\n        1/w = 1/w_{xy} + 1/w_{uv}.\n\n    **Statistics for clipping**\n\n    Several statistics are available for clipping iterations and all of them\n    are reported in the returned ``fit`` dictionary regardless of the\n    setting in ``sigma``:\n\n    .. math::\n        \\mathrm{RMSE} = \\sqrt{\\Sigma_k w_k \\|\\mathbf{r}_k\\|^2}\n\n    .. math::\n        \\mathrm{MAE} = \\sqrt{\\Sigma_k w_k \\|\\mathbf{r}_k\\|}\n\n    .. math::\n        \\mathrm{STD} = \\sqrt{\\Sigma_k w_k \\|\\mathbf{r}_k - \\\n                       \\mathbf{\\overline{r}}\\|^2}/(1-V_2)\n\n    where :math:`\\mathbf{r}_k=\\mathbf{xy}_k-\\mathbf{xy}\'_k`,\n    :math:`\\Sigma_k w_k = 1`, and :math:`V_2=\\Sigma_k w_k^2`.\n\n    '
    if (fitgeom == 'general'):
        linear_fit = fit_general
    elif (fitgeom == 'rscale'):
        linear_fit = fit_rscale
    elif (fitgeom == 'shift'):
        linear_fit = fit_shifts
    else:
        raise ValueError("Unsupported 'fitgeom' value: '{}'".format(fitgeom))
    minobj_per_fitgeom = {'shift': 1, 'rscale': 2, 'general': 3}
    minobj = minobj_per_fitgeom[fitgeom]
    xy = np.array(xy, dtype=np.longdouble)
    uv = np.array(uv, dtype=np.longdouble)
    if ((len(xy.shape) != 2) or (xy.shape[1] != 2) or (uv.shape != xy.shape)):
        raise ValueError("Input coordinate arrays 'xy' and 'uv' must be of shape (N, 2) where N is the number of coordinate points.")
    wmask = np.ones(len(xy), dtype=np.bool_)
    if (wxy is not None):
        wxy = np.asarray(wxy)
        if ((len(wxy.shape) != 1) or (wxy.shape[0] != xy.shape[0])):
            raise ValueError("Weights 'wxy' must be a 1-dimensional vector of lengths equal to the number of input points.")
        wmask *= (wxy > 0.0)
    if (wuv is not None):
        wuv = np.asarray(wuv)
        if ((len(wuv.shape) != 1) or (wuv.shape[0] != xy.shape[0])):
            raise ValueError("Weights 'wuv' must be a 1-dimensional vector of lengths equal to the number of input points.")
        wmask *= (wuv > 0.0)
    mask = wmask
    if ((sigma is None) and (nclip is not None) and (nclip > 0)):
        raise ValueError("Argument 'sigma' cannot be None when 'nclip' is a positive number.")
    if isinstance(sigma, numbers.Number):
        sigstat = 'rmse'
        nsigma = float(sigma)
    elif (sigma is not None):
        nsigma = float(sigma[0])
        sigstat = sigma[1]
        if (sigstat not in ['rmse', 'mae', 'std']):
            raise ValueError('Unsupported sigma statistics value.')
    if ((sigma is not None) and (nsigma <= 0.0)):
        raise ValueError('The value of sigma for clipping iterations must be positive.')
    if (nclip is None):
        nclip = 0
    else:
        if (nclip < 0):
            raise ValueError("Argument 'nclip' must be non-negative.")
        nclip = int(nclip)
    if (np.count_nonzero(mask) == minobj):
        log.warning("The number of sources for the fit is smaller than the minimum number of sources necessary for the requested 'fitgeom'.")
        log.warning('Resetting number of clipping iterations to 0.')
        nclip = 0
    if (center is None):
        center_ld = uv[mask].mean(axis=0, dtype=np.longdouble)
        center = center_ld.astype(np.double)
    else:
        center_ld = np.longdouble(center)
    xy[mask] -= center_ld
    uv[mask] -= center_ld
    log.info("Performing '{:s}' fit".format(fitgeom))
    wmxy = (None if (wxy is None) else wxy[mask])
    wmuv = (None if (wuv is None) else wuv[mask])
    fit = linear_fit(xy[mask], uv[mask], wmxy, wmuv)
    effective_nclip = 0
    for n in range(nclip):
        resids = fit['resids']
        cutoff = (nsigma * fit[sigstat])
        nonclipped = (np.linalg.norm(resids, axis=1) < cutoff)
        if ((np.count_nonzero(nonclipped) < minobj) or nonclipped.all()):
            break
        effective_nclip += 1
        prev_mask = mask
        if (not clip_accum):
            mask = np.array(wmask)
        mask[prev_mask] *= nonclipped
        wmxy = (None if (wxy is None) else wxy[mask])
        wmuv = (None if (wuv is None) else wuv[mask])
        fit = linear_fit(xy[mask], uv[mask], wmxy, wmuv)
    fit['center'] = center
    fit['center_ld'] = center_ld
    fit['fitmask'] = mask
    fit['eff_nclip'] = effective_nclip
    return fit
Compute linear transformation parameters that "best" (in the sense of
minimizing residuals) transform ``uv`` source position to ``xy``
sources iteratively using sigma-clipping.

More precisely, this functions attempts to find a ``2x2`` matrix ``F`` and
a shift vector ``s`` that minimize the residuals between the *transformed*
reference source coordinates ``uv``

.. math::
    \mathbf{xy}'_k = \mathbf{F}\cdot(\mathbf{uv}_k-\mathbf{c})+\
    \mathbf{s} + \mathbf{c}
    :label: ilf1

and the "observed" source positions ``xy``:

.. math::
    \epsilon^2 = \Sigma_k w_k \|\mathbf{xy}_k-\mathbf{xy}'_k\|^2.
    :label: ilf2

In the above equations, :math:`\mathbf{F}` is a ``2x2`` matrix while
:math:`\mathbf{xy}_k` and :math:`\mathbf{uv}_k` are the position
coordinates of the ``k``-th source (row in input ``xy`` and ``uv`` arrays).

One of the two catalogs (``xy`` or ``uv``) contains what we refer to as
"image" source positions and the other one as "reference" source positions.
The meaning assigned to ``xy`` and ``uv`` parameters are up to the
caller of this function.

Parameters
----------
xy: numpy.ndarray
    A ``(N, 2)``-shaped array of source positions (one 2-coordinate
    position per line).

uv: numpy.ndarray
    A ``(N, 2)``-shaped array of source positions (one 2-coordinate
    position per line). This array *must have* the same length (shape)
    as the ``xy`` array.

wxy: numpy.ndarray, None, optional
    A 1-dimensional array of weights of the same length (``N``)
    as ``xy`` array indicating how much a given coordinate should be
    weighted in the fit. If not provided or set to `None`, all positions
    will be contribute equally to the fit if ``wuv`` is also set to `None`.
    See ``Notes`` section for more details.

wuv: numpy.ndarray, None, optional
    A 1-dimensional array of weights of the same length (``N``)
    as ``xy`` array indicating how much a given coordinate should be
    weighted in the fit. If not provided or set to `None`, all positions
    will be contribute equally to the fit if ``wxy`` is also set to `None`.
    See ``Notes`` section for more details.

fitgeom: {'shift', 'rscale', 'general'}, optional
    The fitting geometry to be used in fitting the matched object lists.
    This parameter is used in fitting the shifts (offsets), rotations
    and/or scale changes from the matched object lists. The 'general'
    fit geometry allows for independent scale and rotation for each axis.

center: tuple, list, numpy.ndarray, None, optional
    A list-like container with two ``X``- and ``Y``-positions of the center
    (origin) of rotations in the ``uv`` and ``xy`` coordinate frames.
    If not provided, ``center`` is estimated as a (weighted) mean position
    in the ``uv`` frame.

nclip: int, None, optional
    Number (a non-negative integer) of clipping iterations in fit.
    Clipping will be turned off if ``nclip`` is either `None` or 0.

sigma: float, tuple of the form (float, str), optional
    When a tuple is provided, first value (a positive number)
    indicates the number of "fit error estimates" to use for clipping.
    The second value (a string) indicates the statistic to be
    used for "fit error estimate". Currently the following values are
    supported: ``'rmse'``, ``'mae'``, and ``'std'``
    - see ``Notes`` section for more details.

    When ``sigma`` is a single number, it must be a positive number and
    the default error estimate ``'rmse'`` is assumed.

    This parameter is ignored when ``nclip`` is either `None` or 0.

clip_accum: bool, optional
    Indicates whether or not to reset the list of "bad" (clipped out)
    sources after each clipping iteration. When set to `True` the list
    only grows with each iteration as "bad" positions never re-enter the
    pool of available position for the fit. By default the list of
    "bad" source positions is purged at each iteration.

Returns
-------
fit: dict
    - ``'shift'``: A ``numpy.ndarray`` with two components of the
      computed shift.
    - ``'shift_ld'``: A ``numpy.ndarray`` with two components of the
      computed shift of type ``numpy.longdouble``.
    - ``'matrix'``: A ``2x2`` ``numpy.ndarray`` with the computed
      generalized rotation matrix.
    - ``'matrix_ld'``: A ``2x2`` ``numpy.ndarray`` with the computed
      generalized rotation matrix of type ``numpy.longdouble``.
    - ``'proper_rot'``: Rotation angle (degree) as if the rotation is
      proper.
    - ``'rot'``: A tuple of ``(rotx, roty)`` - the rotation angles with
      regard to the ``X`` and ``Y`` axes.
    - ``'<rot>'``: *Arithmetic mean* of the angles of rotation around
      ``X`` and ``Y`` axes.
    - ``'scale'``: A tuple of ``(sx, sy)`` - scale change in the direction
      of the ``X`` and ``Y`` axes.
    - ``'<scale>'``: *Geometric mean* of scales ``sx`` and ``sy``.
    - ``'skew'``: Computed skew.
    - ``'proper'``: a boolean indicating whether the rotation is proper.
    - ``'fitgeom'``: Fit geometry (allowed transformations) used for
      fitting data (to minimize residuals). This is copy of the input
      argument ``fitgeom``.
    - ``'center'``: Center of rotation
    - ``'center_ld'``: Center of rotation as a ``numpy.longdouble``.
    - ``'fitmask'``: A boolean array indicating which source positions
      where used for fitting (`True`) and which were clipped out
      (`False`). **NOTE** For weighted fits, positions with zero
      weights are automatically excluded from the fits.
    - ``'eff_nclip'``: Effective number of clipping iterations
    - ``'rmse'``: Root-Mean-Square Error
    - ``'mae'``: Mean Absolute Error
    - ``'std'``: Standard Deviation of the residuals
    - ``'resids'``: An array of residuals of the fit.
      **NOTE:** Only the residuals for the "valid" points are reported
      here. Therefore the length of this array may be smaller than the
      length of input arrays of positions.

Notes
-----
**Weights**

Weights can be provided for both "image" source positions and "reference"
source positions. When no weights are given, all positions are weighted
equally. When only one set of positions have weights (i.e., either ``wxy``
or ``wuv`` is not `None`) then weights in :eq:`ilf2` are set to be equal
to the provided set of weights. When weights for *both* "image" source
positions and "reference" source positions are provided, then the
combined weight that is used in :eq:`ilf2` is computed as:

.. math::
    1/w = 1/w_{xy} + 1/w_{uv}.

**Statistics for clipping**

Several statistics are available for clipping iterations and all of them
are reported in the returned ``fit`` dictionary regardless of the
setting in ``sigma``:

.. math::
    \mathrm{RMSE} = \sqrt{\Sigma_k w_k \|\mathbf{r}_k\|^2}

.. math::
    \mathrm{MAE} = \sqrt{\Sigma_k w_k \|\mathbf{r}_k\|}

.. math::
    \mathrm{STD} = \sqrt{\Sigma_k w_k \|\mathbf{r}_k - \
                   \mathbf{\overline{r}}\|^2}/(1-V_2)

where :math:`\mathbf{r}_k=\mathbf{xy}_k-\mathbf{xy}'_k`,
:math:`\Sigma_k w_k = 1`, and :math:`V_2=\Sigma_k w_k^2`.
def iter_linear_fit(xy, uv, wxy=None, wuv=None, fitgeom='general', center=None, nclip=3, sigma=(3.0, 'rmse'), clip_accum=False):
    '\n    Compute linear transformation parameters that "best" (in the sense of\n    minimizing residuals) transform ``uv`` source position to ``xy``\n    sources iteratively using sigma-clipping.\n\n    More precisely, this functions attempts to find a ``2x2`` matrix ``F`` and\n    a shift vector ``s`` that minimize the residuals between the *transformed*\n    reference source coordinates ``uv``\n\n    .. math::\n        \\mathbf{xy}\'_k = \\mathbf{F}\\cdot(\\mathbf{uv}_k-\\mathbf{c})+\\\n        \\mathbf{s} + \\mathbf{c}\n        :label: ilf1\n\n    and the "observed" source positions ``xy``:\n\n    .. math::\n        \\epsilon^2 = \\Sigma_k w_k \\|\\mathbf{xy}_k-\\mathbf{xy}\'_k\\|^2.\n        :label: ilf2\n\n    In the above equations, :math:`\\mathbf{F}` is a ``2x2`` matrix while\n    :math:`\\mathbf{xy}_k` and :math:`\\mathbf{uv}_k` are the position\n    coordinates of the ``k``-th source (row in input ``xy`` and ``uv`` arrays).\n\n    One of the two catalogs (``xy`` or ``uv``) contains what we refer to as\n    "image" source positions and the other one as "reference" source positions.\n    The meaning assigned to ``xy`` and ``uv`` parameters are up to the\n    caller of this function.\n\n    Parameters\n    ----------\n    xy: numpy.ndarray\n        A ``(N, 2)``-shaped array of source positions (one 2-coordinate\n        position per line).\n\n    uv: numpy.ndarray\n        A ``(N, 2)``-shaped array of source positions (one 2-coordinate\n        position per line). This array *must have* the same length (shape)\n        as the ``xy`` array.\n\n    wxy: numpy.ndarray, None, optional\n        A 1-dimensional array of weights of the same length (``N``)\n        as ``xy`` array indicating how much a given coordinate should be\n        weighted in the fit. If not provided or set to `None`, all positions\n        will be contribute equally to the fit if ``wuv`` is also set to `None`.\n        See ``Notes`` section for more details.\n\n    wuv: numpy.ndarray, None, optional\n        A 1-dimensional array of weights of the same length (``N``)\n        as ``xy`` array indicating how much a given coordinate should be\n        weighted in the fit. If not provided or set to `None`, all positions\n        will be contribute equally to the fit if ``wxy`` is also set to `None`.\n        See ``Notes`` section for more details.\n\n    fitgeom: {\'shift\', \'rscale\', \'general\'}, optional\n        The fitting geometry to be used in fitting the matched object lists.\n        This parameter is used in fitting the shifts (offsets), rotations\n        and/or scale changes from the matched object lists. The \'general\'\n        fit geometry allows for independent scale and rotation for each axis.\n\n    center: tuple, list, numpy.ndarray, None, optional\n        A list-like container with two ``X``- and ``Y``-positions of the center\n        (origin) of rotations in the ``uv`` and ``xy`` coordinate frames.\n        If not provided, ``center`` is estimated as a (weighted) mean position\n        in the ``uv`` frame.\n\n    nclip: int, None, optional\n        Number (a non-negative integer) of clipping iterations in fit.\n        Clipping will be turned off if ``nclip`` is either `None` or 0.\n\n    sigma: float, tuple of the form (float, str), optional\n        When a tuple is provided, first value (a positive number)\n        indicates the number of "fit error estimates" to use for clipping.\n        The second value (a string) indicates the statistic to be\n        used for "fit error estimate". Currently the following values are\n        supported: ``\'rmse\'``, ``\'mae\'``, and ``\'std\'``\n        - see ``Notes`` section for more details.\n\n        When ``sigma`` is a single number, it must be a positive number and\n        the default error estimate ``\'rmse\'`` is assumed.\n\n        This parameter is ignored when ``nclip`` is either `None` or 0.\n\n    clip_accum: bool, optional\n        Indicates whether or not to reset the list of "bad" (clipped out)\n        sources after each clipping iteration. When set to `True` the list\n        only grows with each iteration as "bad" positions never re-enter the\n        pool of available position for the fit. By default the list of\n        "bad" source positions is purged at each iteration.\n\n    Returns\n    -------\n    fit: dict\n        - ``\'shift\'``: A ``numpy.ndarray`` with two components of the\n          computed shift.\n        - ``\'shift_ld\'``: A ``numpy.ndarray`` with two components of the\n          computed shift of type ``numpy.longdouble``.\n        - ``\'matrix\'``: A ``2x2`` ``numpy.ndarray`` with the computed\n          generalized rotation matrix.\n        - ``\'matrix_ld\'``: A ``2x2`` ``numpy.ndarray`` with the computed\n          generalized rotation matrix of type ``numpy.longdouble``.\n        - ``\'proper_rot\'``: Rotation angle (degree) as if the rotation is\n          proper.\n        - ``\'rot\'``: A tuple of ``(rotx, roty)`` - the rotation angles with\n          regard to the ``X`` and ``Y`` axes.\n        - ``\'<rot>\'``: *Arithmetic mean* of the angles of rotation around\n          ``X`` and ``Y`` axes.\n        - ``\'scale\'``: A tuple of ``(sx, sy)`` - scale change in the direction\n          of the ``X`` and ``Y`` axes.\n        - ``\'<scale>\'``: *Geometric mean* of scales ``sx`` and ``sy``.\n        - ``\'skew\'``: Computed skew.\n        - ``\'proper\'``: a boolean indicating whether the rotation is proper.\n        - ``\'fitgeom\'``: Fit geometry (allowed transformations) used for\n          fitting data (to minimize residuals). This is copy of the input\n          argument ``fitgeom``.\n        - ``\'center\'``: Center of rotation\n        - ``\'center_ld\'``: Center of rotation as a ``numpy.longdouble``.\n        - ``\'fitmask\'``: A boolean array indicating which source positions\n          where used for fitting (`True`) and which were clipped out\n          (`False`). **NOTE** For weighted fits, positions with zero\n          weights are automatically excluded from the fits.\n        - ``\'eff_nclip\'``: Effective number of clipping iterations\n        - ``\'rmse\'``: Root-Mean-Square Error\n        - ``\'mae\'``: Mean Absolute Error\n        - ``\'std\'``: Standard Deviation of the residuals\n        - ``\'resids\'``: An array of residuals of the fit.\n          **NOTE:** Only the residuals for the "valid" points are reported\n          here. Therefore the length of this array may be smaller than the\n          length of input arrays of positions.\n\n    Notes\n    -----\n    **Weights**\n\n    Weights can be provided for both "image" source positions and "reference"\n    source positions. When no weights are given, all positions are weighted\n    equally. When only one set of positions have weights (i.e., either ``wxy``\n    or ``wuv`` is not `None`) then weights in :eq:`ilf2` are set to be equal\n    to the provided set of weights. When weights for *both* "image" source\n    positions and "reference" source positions are provided, then the\n    combined weight that is used in :eq:`ilf2` is computed as:\n\n    .. math::\n        1/w = 1/w_{xy} + 1/w_{uv}.\n\n    **Statistics for clipping**\n\n    Several statistics are available for clipping iterations and all of them\n    are reported in the returned ``fit`` dictionary regardless of the\n    setting in ``sigma``:\n\n    .. math::\n        \\mathrm{RMSE} = \\sqrt{\\Sigma_k w_k \\|\\mathbf{r}_k\\|^2}\n\n    .. math::\n        \\mathrm{MAE} = \\sqrt{\\Sigma_k w_k \\|\\mathbf{r}_k\\|}\n\n    .. math::\n        \\mathrm{STD} = \\sqrt{\\Sigma_k w_k \\|\\mathbf{r}_k - \\\n                       \\mathbf{\\overline{r}}\\|^2}/(1-V_2)\n\n    where :math:`\\mathbf{r}_k=\\mathbf{xy}_k-\\mathbf{xy}\'_k`,\n    :math:`\\Sigma_k w_k = 1`, and :math:`V_2=\\Sigma_k w_k^2`.\n\n    '
    if (fitgeom == 'general'):
        linear_fit = fit_general
    elif (fitgeom == 'rscale'):
        linear_fit = fit_rscale
    elif (fitgeom == 'shift'):
        linear_fit = fit_shifts
    else:
        raise ValueError("Unsupported 'fitgeom' value: '{}'".format(fitgeom))
    minobj_per_fitgeom = {'shift': 1, 'rscale': 2, 'general': 3}
    minobj = minobj_per_fitgeom[fitgeom]
    xy = np.array(xy, dtype=np.longdouble)
    uv = np.array(uv, dtype=np.longdouble)
    if ((len(xy.shape) != 2) or (xy.shape[1] != 2) or (uv.shape != xy.shape)):
        raise ValueError("Input coordinate arrays 'xy' and 'uv' must be of shape (N, 2) where N is the number of coordinate points.")
    wmask = np.ones(len(xy), dtype=np.bool_)
    if (wxy is not None):
        wxy = np.asarray(wxy)
        if ((len(wxy.shape) != 1) or (wxy.shape[0] != xy.shape[0])):
            raise ValueError("Weights 'wxy' must be a 1-dimensional vector of lengths equal to the number of input points.")
        wmask *= (wxy > 0.0)
    if (wuv is not None):
        wuv = np.asarray(wuv)
        if ((len(wuv.shape) != 1) or (wuv.shape[0] != xy.shape[0])):
            raise ValueError("Weights 'wuv' must be a 1-dimensional vector of lengths equal to the number of input points.")
        wmask *= (wuv > 0.0)
    mask = wmask
    if ((sigma is None) and (nclip is not None) and (nclip > 0)):
        raise ValueError("Argument 'sigma' cannot be None when 'nclip' is a positive number.")
    if isinstance(sigma, numbers.Number):
        sigstat = 'rmse'
        nsigma = float(sigma)
    elif (sigma is not None):
        nsigma = float(sigma[0])
        sigstat = sigma[1]
        if (sigstat not in ['rmse', 'mae', 'std']):
            raise ValueError('Unsupported sigma statistics value.')
    if ((sigma is not None) and (nsigma <= 0.0)):
        raise ValueError('The value of sigma for clipping iterations must be positive.')
    if (nclip is None):
        nclip = 0
    else:
        if (nclip < 0):
            raise ValueError("Argument 'nclip' must be non-negative.")
        nclip = int(nclip)
    if (np.count_nonzero(mask) == minobj):
        log.warning("The number of sources for the fit is smaller than the minimum number of sources necessary for the requested 'fitgeom'.")
        log.warning('Resetting number of clipping iterations to 0.')
        nclip = 0
    if (center is None):
        center_ld = uv[mask].mean(axis=0, dtype=np.longdouble)
        center = center_ld.astype(np.double)
    else:
        center_ld = np.longdouble(center)
    xy[mask] -= center_ld
    uv[mask] -= center_ld
    log.info("Performing '{:s}' fit".format(fitgeom))
    wmxy = (None if (wxy is None) else wxy[mask])
    wmuv = (None if (wuv is None) else wuv[mask])
    fit = linear_fit(xy[mask], uv[mask], wmxy, wmuv)
    effective_nclip = 0
    for n in range(nclip):
        resids = fit['resids']
        cutoff = (nsigma * fit[sigstat])
        nonclipped = (np.linalg.norm(resids, axis=1) < cutoff)
        if ((np.count_nonzero(nonclipped) < minobj) or nonclipped.all()):
            break
        effective_nclip += 1
        prev_mask = mask
        if (not clip_accum):
            mask = np.array(wmask)
        mask[prev_mask] *= nonclipped
        wmxy = (None if (wxy is None) else wxy[mask])
        wmuv = (None if (wuv is None) else wuv[mask])
        fit = linear_fit(xy[mask], uv[mask], wmxy, wmuv)
    fit['center'] = center
    fit['center_ld'] = center_ld
    fit['fitmask'] = mask
    fit['eff_nclip'] = effective_nclip
    return fit