def lapmod(n, cc, ii, kk, fast=True, return_cost=True,
           fp_version=FP_DYNAMIC):
    """Solve sparse linear assignment problem using Jonker-Volgenant algorithm.

    n: number of rows of the assignment cost matrix
    cc: 1D array of all finite elements of the assignement cost matrix
    ii: 1D array of indices of the row starts in cc. The following must hold:
            ii[0] = 0 and ii[n+1] = len(cc).
    kk: 1D array of the column indices so that:
            cost[i, kk[ii[i] + k]] == cc[ii[i] + k].
        Indices within one row must be sorted.
    extend_cost: whether or not extend a non-square matrix [default: False]
    cost_limit: an upper limit for a cost of a single assignment
                [default: np.inf]
    return_cost: whether or not to return the assignment cost

    Returns (opt, x, y) where:
      opt: cost of the assignment
      x: vector of columns assigned to rows
      y: vector of rows assigned to columns
    or (x, y) if return_cost is not True.

    When extend_cost and/or cost_limit is set, all unmatched entries will be
    marked by -1 in x/y.
    """
    # log = logging.getLogger('lapmod')

    check_cost(n, cc, ii, kk)

    if fast is True:
        # log.debug('[----CR & RT & ARR & augmentation ----]')
        x, y = _lapmod(n, cc, ii, kk, fp_version=fp_version)
    else:
        cc = np.ascontiguousarray(cc, dtype=np.float64)
        ii = np.ascontiguousarray(ii, dtype=np.int32)
        kk = np.ascontiguousarray(kk, dtype=np.int32)
        x = np.empty((n,), dtype=np.int32)
        y = np.empty((n,), dtype=np.int32)
        v = np.empty((n,), dtype=np.float64)
        free_rows = np.empty((n,), dtype=np.int32)
        # log.debug('[----Column reduction & reduction transfer----]')
        n_free_rows = _pycrrt(n, cc, ii, kk, free_rows, x, y, v)
        # log.debug(
        #     'free, x, y, v: %s %s %s %s', free_rows[:n_free_rows], x, y, v)
        if n_free_rows == 0:
            # log.info('Reduction solved it.')
            if return_cost is True:
                return get_cost(n, cc, ii, kk, x), x, y
            else:
                return x, y
        for it in range(2):
            # log.debug('[---Augmenting row reduction (iteration: %d)---]', it)
            n_free_rows = _pyarr(
                    n, cc, ii, kk, n_free_rows, free_rows, x, y, v)
            # log.debug(
            #   'free, x, y, v: %s %s %s %s', free_rows[:n_free_rows], x, y, v)
            if n_free_rows == 0:
                # log.info('Augmenting row reduction solved it.')
                if return_cost is True:
                    return get_cost(n, cc, ii, kk, x), x, y
                else:
                    return x, y
        # log.info('[----Augmentation----]')
        _pya(n, cc, ii, kk, n_free_rows, free_rows, x, y, v)
        # log.debug('x, y, v: %s %s %s', x, y, v)
    if return_cost is True:
        return get_cost(n, cc, ii, kk, x), x, y
    else:
        return x, y
Solve sparse linear assignment problem using Jonker-Volgenant algorithm.

    n: number of rows of the assignment cost matrix
    cc: 1D array of all finite elements of the assignement cost matrix
    ii: 1D array of indices of the row starts in cc. The following must hold:
            ii[0] = 0 and ii[n+1] = len(cc).
    kk: 1D array of the column indices so that:
            cost[i, kk[ii[i] + k]] == cc[ii[i] + k].
        Indices within one row must be sorted.
    extend_cost: whether or not extend a non-square matrix [default: False]
    cost_limit: an upper limit for a cost of a single assignment
                [default: np.inf]
    return_cost: whether or not to return the assignment cost

    Returns (opt, x, y) where:
      opt: cost of the assignment
      x: vector of columns assigned to rows
      y: vector of rows assigned to columns
    or (x, y) if return_cost is not True.

    When extend_cost and/or cost_limit is set, all unmatched entries will be
    marked by -1 in x/y.