def merge_regions(regions):
    """Coalesce regions.

    Scans a sorted list of region starting and ending positions looking
    for the outer-most start and end positions to coalesce overlapping
    and contained regions into a smaller list of larger regions.

    Parameters
    ----------
    regions : list of tuples
        List of (start, end) position integers.

    Returns
    -------
    regions : list of tuples
        List of merged (start, end) position integers.

    Examples
    --------
    >>> # Empty list
    >>> merge_regions([])
    []

    >>> # Only one region
    >>> merge_regions([(10,20)])
    [(10, 20)]

    >>> # Discard contained region at left
    >>> merge_regions([(10,20), (10,15)])
    [(10, 20)]

    >>> # Discard contained region at right
    >>> merge_regions([(10,20), (15,20)])
    [(10, 20)]

    >>> # Discard contained region exact match
    >>> merge_regions([(10,20), (10,20)])
    [(10, 20)]

    >>> # Discard contained region fully contained
    >>> merge_regions([(10,20), (11,19)])
    [(10, 20)]

    >>> # Extend region by overlap right
    >>> merge_regions([(10,20), (15,25)])
    [(10, 25)]

    >>> # Extend region by overlap left
    >>> merge_regions([(10,20), (5,15)])
    [(5, 20)]

    >>> # Extend immediately adjacent region by extension
    >>> merge_regions([(10,20), (21,30)])
    [(10, 30)]

    >>> # No overlap
    >>> merge_regions([(40,50), (25,30)])
    [(25, 30), (40, 50)]

    >>> # Single position region : discard contained region
    >>> merge_regions([(40,50), (40,40)])
    [(40, 50)]

    >>> # Single position region : discard contained region
    >>> merge_regions([(40,50), (50,50)])
    [(40, 50)]

    >>> # Single position region : discard contained region
    >>> merge_regions([(40,50), (41,41)])
    [(40, 50)]

    >>> # Single position region : discard contained region
    >>> merge_regions([(40,50), (49,49)])
    [(40, 50)]

    >>> # Single position region : extend immediately adjacent region by extension
    >>> merge_regions([(10,10), (11,21)])
    [(10, 21)]

    >>> # Single position region : extend immediately adjacent region by extension
    >>> merge_regions([(10,20), (21,21)])
    [(10, 21)]

    >>> # Single position region : merge two immediately adjacent single-position regions
    >>> merge_regions([(20,20), (21,21)])
    [(20, 21)]

    >>> # Single position region : no overlap
    >>> merge_regions([(40,50), (60,60)])
    [(40, 50), (60, 60)]

    >>> # Single position region : no overlap
    >>> merge_regions([(40,40), (50,60)])
    [(40, 40), (50, 60)]

    >>> # Single position region : no overlap
    >>> merge_regions([(40,40), (50,50)])
    [(40, 40), (50, 50)]
    """
    if len(regions) == 0:
        return regions
    regions = sorted(regions)
    merged_regions = list()
    merged_regions.append(regions[0])
    for region in regions[1:]:
        last_merged_region = merged_regions[-1]
        last_merged_region_start, last_merged_region_end = last_merged_region
        region_start, region_end = region
        if region_start >= last_merged_region_start and region_end <= last_merged_region_end:
            pass # discard region contained in the last region
        elif region_start <= (last_merged_region_end + 1) and region_end > last_merged_region_end:
            merged_regions[-1] = (last_merged_region_start, region_end) # extend last region by overlapping or adjacent region
        else:
            merged_regions.append(region) # add non-overlapping region to sorted list
    return merged_regions