def correct_spurious_inversions(scaffolds, criterion="colinear"):
    """Invert bins based on orientation neighborhoods. Neighborhoods can be
    defined by three criteria:

    -a 'cis' neighborhood is a group of bins belonging to the same initial
    contig
    -a 'colinear' neighborhood is a 'cis' neighborhood where bins are
    ordered the same way they were on the initial contig
    -a 'contiguous' neighborhood is a 'colinear' neighborhood where all bins
    are exactly consecutive, i.e. the end position of each bin matches the
    starting position of the next bin

    This function looks for such neighborhoods and orients all bins in it
    according to the majority orientation.

    An example with three inversions, one for each criterion:

        >>> scaffolds = {
        ...     "scaffold1": [
        ...         ["contig1", 1, 100, 200, 1],
        ...         ["contig1", 2, 200, 300, 1],
        ...         ["contig1", 3, 300, 400, -1], # <-- inversion (contiguous)
        ...         ["contig1", 4, 400, 500, 1],
        ...         ["contig1", 10, 1500, 1605, 1],
        ...         ["contig1", 12, 1750, 1850, -1], # <-- inversion (colinear)
        ...         ["contig1", 23, 2100, 2499, 1],
        ...         ["contig1", 28, 2850, 3000, 1],
        ...         ["contig1", 0, 0, 100, -1], # <-- inversion (cis)
        ...         ["contig2", 554, 1850, 1900, -1],
        ...     ],
        ... }

    With the 'cis' criterion, pretty much all bins from "contig1" get inverted
    to the majority orientation (+):

        >>> sc_cis = correct_spurious_inversions(scaffolds, "cis")
        >>> for my_bin in sc_cis['scaffold1']:
        ...     print(my_bin)
        ...
        ['contig1', 1, 100, 200, 1]
        ['contig1', 2, 200, 300, 1]
        ['contig1', 3, 300, 400, 1]
        ['contig1', 4, 400, 500, 1]
        ['contig1', 10, 1500, 1605, 1]
        ['contig1', 12, 1750, 1850, 1]
        ['contig1', 23, 2100, 2499, 1]
        ['contig1', 28, 2850, 3000, 1]
        ['contig1', 0, 0, 100, 1]
        ['contig2', 554, 1850, 1900, -1]

    With the 'colinear' criterion, the bin ['contig1', 0, 0, 100, -1] is
    treated as a different neighborhood from the rest (as it is not colinear
    with the other bins from 'contig1') and remains untouched:

        >>> sc_colinear = correct_spurious_inversions(scaffolds, "colinear")
        >>> for my_bin in sc_colinear['scaffold1']:
        ...     print(my_bin)
        ...
        ['contig1', 1, 100, 200, 1]
        ['contig1', 2, 200, 300, 1]
        ['contig1', 3, 300, 400, 1]
        ['contig1', 4, 400, 500, 1]
        ['contig1', 10, 1500, 1605, 1]
        ['contig1', 12, 1750, 1850, 1]
        ['contig1', 23, 2100, 2499, 1]
        ['contig1', 28, 2850, 3000, 1]
        ['contig1', 0, 0, 100, -1]
        ['contig2', 554, 1850, 1900, -1]

    With the 'contiguous' criterion, the ['contig1', 12, 1750, 1850, -1] breaks
    with the contiguous region spanning from 100 to 400 bp on 'contig1' and
    so is treated as a different neighborhood as well:

        >>> sc_cont = correct_spurious_inversions(scaffolds, "contiguous")
        >>> for my_bin in sc_cont['scaffold1']:
        ...     print(my_bin)
        ...
        ['contig1', 1, 100, 200, 1]
        ['contig1', 2, 200, 300, 1]
        ['contig1', 3, 300, 400, 1]
        ['contig1', 4, 400, 500, 1]
        ['contig1', 10, 1500, 1605, 1]
        ['contig1', 12, 1750, 1850, -1]
        ['contig1', 23, 2100, 2499, 1]
        ['contig1', 28, 2850, 3000, 1]
        ['contig1', 0, 0, 100, -1]
        ['contig2', 554, 1850, 1900, -1]

    Note that 'contig2' remains untouched at all times since bins in it are
    never in the same neighborhood as those from 'contig1'.

    """

    scaffolds = format_info_frags(scaffolds)
    new_scaffolds = {}

    def is_cis(bin1, bin2):
        return bin1[0] == bin2[0]

    def is_contiguous(bin1, bin2):
        return is_cis(bin1, bin2) and bin1[3] == bin2[2]

    def is_colinear(bin1, bin2):
        return is_cis(bin1, bin2) and bin1[3] <= bin2[2]

    condition_callables = {
        "cis": is_cis,
        "colinear": is_colinear,
        "contiguous": is_contiguous,
    }
    block_test = condition_callables.get(criterion, "colinear")

    for name, scaffold in scaffolds.items():
        new_scaffold = []

        block_cumulative_ori = 0

        if len(scaffold) > 2:
            current_bin = scaffold[0]
            block_buffer = []
            for my_bin in scaffold:

                if not block_buffer:
                    new_bin = copy.deepcopy(my_bin)
                    block_buffer.append(new_bin)
                    block_cumulative_ori = my_bin[-1]
                    continue

                elif not block_test(current_bin, my_bin):
                    for my_buf_bin in block_buffer:
                        new_bin = copy.deepcopy(my_buf_bin)
                        if block_cumulative_ori >= 0:
                            new_bin[-1] = 1
                        else:
                            new_bin[-1] = -1
                        new_scaffold.append(new_bin)
                    block_cumulative_ori = my_bin[-1]
                    current_bin = copy.deepcopy(my_bin)
                    block_buffer = copy.deepcopy([my_bin])

                else:
                    block_cumulative_ori += my_bin[-1]
                    new_bin = copy.deepcopy(my_bin)
                    block_buffer.append(new_bin)
                    current_bin = my_bin

            for my_bin in block_buffer:
                new_bin = copy.deepcopy(my_bin)
                if block_cumulative_ori >= 0:
                    new_bin[-1] = 1
                else:
                    new_bin[-1] = -1
                new_scaffold.append(new_bin)
            new_scaffolds[name] = copy.deepcopy(new_scaffold)

        else:
            new_scaffolds[name] = copy.deepcopy(scaffold)

    return new_scaffolds
Invert bins based on orientation neighborhoods. Neighborhoods can be
    defined by three criteria:

    -a 'cis' neighborhood is a group of bins belonging to the same initial
    contig
    -a 'colinear' neighborhood is a 'cis' neighborhood where bins are
    ordered the same way they were on the initial contig
    -a 'contiguous' neighborhood is a 'colinear' neighborhood where all bins
    are exactly consecutive, i.e. the end position of each bin matches the
    starting position of the next bin

    This function looks for such neighborhoods and orients all bins in it
    according to the majority orientation.

    An example with three inversions, one for each criterion:

        >>> scaffolds = {
        ...     "scaffold1": [
        ...         ["contig1", 1, 100, 200, 1],
        ...         ["contig1", 2, 200, 300, 1],
        ...         ["contig1", 3, 300, 400, -1], # <-- inversion (contiguous)
        ...         ["contig1", 4, 400, 500, 1],
        ...         ["contig1", 10, 1500, 1605, 1],
        ...         ["contig1", 12, 1750, 1850, -1], # <-- inversion (colinear)
        ...         ["contig1", 23, 2100, 2499, 1],
        ...         ["contig1", 28, 2850, 3000, 1],
        ...         ["contig1", 0, 0, 100, -1], # <-- inversion (cis)
        ...         ["contig2", 554, 1850, 1900, -1],
        ...     ],
        ... }

    With the 'cis' criterion, pretty much all bins from "contig1" get inverted
    to the majority orientation (+):

        >>> sc_cis = correct_spurious_inversions(scaffolds, "cis")
        >>> for my_bin in sc_cis['scaffold1']:
        ...     print(my_bin)
        ...
        ['contig1', 1, 100, 200, 1]
        ['contig1', 2, 200, 300, 1]
        ['contig1', 3, 300, 400, 1]
        ['contig1', 4, 400, 500, 1]
        ['contig1', 10, 1500, 1605, 1]
        ['contig1', 12, 1750, 1850, 1]
        ['contig1', 23, 2100, 2499, 1]
        ['contig1', 28, 2850, 3000, 1]
        ['contig1', 0, 0, 100, 1]
        ['contig2', 554, 1850, 1900, -1]

    With the 'colinear' criterion, the bin ['contig1', 0, 0, 100, -1] is
    treated as a different neighborhood from the rest (as it is not colinear
    with the other bins from 'contig1') and remains untouched:

        >>> sc_colinear = correct_spurious_inversions(scaffolds, "colinear")
        >>> for my_bin in sc_colinear['scaffold1']:
        ...     print(my_bin)
        ...
        ['contig1', 1, 100, 200, 1]
        ['contig1', 2, 200, 300, 1]
        ['contig1', 3, 300, 400, 1]
        ['contig1', 4, 400, 500, 1]
        ['contig1', 10, 1500, 1605, 1]
        ['contig1', 12, 1750, 1850, 1]
        ['contig1', 23, 2100, 2499, 1]
        ['contig1', 28, 2850, 3000, 1]
        ['contig1', 0, 0, 100, -1]
        ['contig2', 554, 1850, 1900, -1]

    With the 'contiguous' criterion, the ['contig1', 12, 1750, 1850, -1] breaks
    with the contiguous region spanning from 100 to 400 bp on 'contig1' and
    so is treated as a different neighborhood as well:

        >>> sc_cont = correct_spurious_inversions(scaffolds, "contiguous")
        >>> for my_bin in sc_cont['scaffold1']:
        ...     print(my_bin)
        ...
        ['contig1', 1, 100, 200, 1]
        ['contig1', 2, 200, 300, 1]
        ['contig1', 3, 300, 400, 1]
        ['contig1', 4, 400, 500, 1]
        ['contig1', 10, 1500, 1605, 1]
        ['contig1', 12, 1750, 1850, -1]
        ['contig1', 23, 2100, 2499, 1]
        ['contig1', 28, 2850, 3000, 1]
        ['contig1', 0, 0, 100, -1]
        ['contig2', 554, 1850, 1900, -1]

    Note that 'contig2' remains untouched at all times since bins in it are
    never in the same neighborhood as those from 'contig1'.