def filter_changepoints(cp_blocks, coef, min_index_distance):
    """Filters change points that are too close.

    Given the ``cp_block`` from the output of ``find_neighbor_changepoints`` and the corresponding
    coefficients, finds the list of change points, one in each neighborhood list.

    The algorithm keeps all individual change points. If more than one change points are in the
    same block, with the maximum distance less than ``min_index_distance``, then only the one
    with the maximum absolute coefficient is retained. If more than one change points are in the
    same block, and the block covers a few ``max_index_distance`` length, then a greedy method
    is used to select change points. The principle is that, we perform one pass of the change
    points, within each ``max_index_distance``, we select one change point with the maximum
    absolute coefficients. If the next change point is with in ``max_index_distance`` of the
    previous one, the previous one is dropped. A back-tracking is also used to fill possible
    change points when a change point is dropped.

    Parameters
    ----------
    cp_blocks : `list`
        A list of list of change points, output from ``find_neighbor_changepoints``.
    coef : `numpy.array`
        The estimated coefficients for all potential change points.
        Note: ``coef`` is a 1-D array, which is the output from regression model.
    min_index_distance : `int`
        The minimum index distance between two selected change points.
        Note that this parameter is added to safe guard the extreme cases: if all
        significant change points are included in the same block, with insignificant
        change points connecting them, then we do not want to drop all and just leave
        one change points. With this parameter, we can leave more than one change point
        from each block, and keep them at least ``min_index_distance`` away.

    Returns
    -------
    changepoint_indices : `list`
        The indices of selected change points.
    """
    if min_index_distance <= 0:
        raise ValueError("`min_index_distance` is the minimum distance between change point"
                         "indices to consider them separate, and must be positive.")
    if min_index_distance == 1:
        return [cp for block in cp_blocks for cp in block]
    coef = coef.ravel()  # makes sure ``coef`` is 1-D array
    coef = coef[[i for block in cp_blocks for i in block]]  # Only needs the coefs that correspond to selected cp's.
    selected_changepoints = []
    if cp_blocks == [[]]:
        return []
    for i, block in enumerate(cp_blocks):
        if len(block) == 1:
            # if only one cp in a block, leaves it
            selected_changepoints.append(block[0])
        else:
            # needs coefs to decide
            coef_start = sum([len(block) for block in cp_blocks[:i]])
            coef_block = coef[coef_start: coef_start + len(block)]
            if block[-1] - block[0] < min_index_distance:
                # If block max distance is less than `min_index_distance`,
                # only one cp can be selected,
                # select the one with the greatest absolute coef
                selected_changepoints.append(block[abs(coef_block).argmax()])
            else:
                # For longer blocks, the change points with the maximum
                # absolute coefficients are selected, while keeping the
                # distance beyond ``min_index_distance``.
                # This prevents dropping too many change points in a long block.
                block_cps = []
                start = block[0]
                last_coef = 0
                while start <= block[-1]:
                    # gets the current ``min_index_distance`` sized sub-block of cps and coefs
                    current_sub_block = [cp for cp in block if start <= cp < start + min_index_distance]
                    if not current_sub_block:
                        start += min_index_distance
                        continue
                    current_cp = current_sub_block[
                        abs(coef_block[[block.index(cp) for cp in current_sub_block]]).argmax()]
                    current_cp_idx = block.index(current_cp)
                    current_coef = coef_block[current_cp_idx]
                    # the first change point, doesn't have to look back
                    if start == block[0]:
                        block_cps.append(current_cp)
                    else:
                        # check the distance from the last change point
                        # if distance is enough, we can fit the new change point
                        last_cp = block_cps[-1]
                        if current_cp - last_cp >= min_index_distance:
                            block_cps.append(current_cp)
                        # if the distance is not enough and the new cp has a greater absolute coef,
                        # we have to remove the previous one with less absolute coef
                        # however, we would like to see if another cp fits in between
                        elif abs(current_coef) > abs(last_coef):
                            # checks if an extra change point can fit between last_last_cp and current_cp
                            if len(block_cps) >= 2:
                                last_last_cp = block_cps[-2]
                            else:
                                # if no last_last_cp, there's not lower bound
                                last_last_cp = -min_index_distance
                            # gets the cps that can fit between last_last_cp and current_cp
                            back_fill_potential_cp = [
                                cp for cp in block if
                                last_last_cp + min_index_distance <= cp <= current_cp - min_index_distance]
                            if back_fill_potential_cp:
                                # if there is cp that can fit in between, gets the one with max absolute coef
                                potential_coef = coef[[block.index(cp) for cp in back_fill_potential_cp]]
                                back_fill_cp = back_fill_potential_cp[abs(potential_coef).argmax()]
                                # append the cp in between
                                block_cps.append(back_fill_cp)
                            # removes the last one and append the new one
                            block_cps.remove(last_cp)
                            block_cps.append(current_cp)
                    # updates last_coef for comparison
                    last_coef = current_coef
                    # search ahead
                    start += min_index_distance
                selected_changepoints += block_cps
    return selected_changepoints