<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def bound_weights(weights, minimum=None, maximum=None):
 """ Bound a weight list so that all outcomes fit within specified bounds. The probability distribution within the ``minimum`` and ``maximum`` values remains the same. Weights in the list with outcomes outside of ``minimum`` and ``maximum`` are removed. If weights are removed from either end, attach weights at the modified edges at the same weight (y-axis) position they had interpolated in the original list. If neither ``minimum`` nor ``maximum`` are set, ``weights`` will be returned unmodified. If both are set, ``minimum`` must be less than ``maximum``. Args: weights (list):
 the list of weights where each weight is a ``tuple`` of form ``(float, float)`` corresponding to ``(outcome, weight)``. Must be sorted in increasing order of outcomes minimum (float):
 Lowest allowed outcome for the weight list maximum (float):
 Highest allowed outcome for the weight list Returns: list: A list of 2-tuples of form ``(float, float)``, the bounded weight list. Raises: ValueError: if ``maximum < minimum`` Example: [(1, 1), (2, 2), (3, 1)] """

    # Copy weights to avoid side-effects
    bounded_weights = weights[:]
    # Remove weights outside of minimum and maximum
    if minimum is not None and maximum is not None:
        if maximum < minimum:
            raise ValueError
        bounded_weights = [bw for bw in bounded_weights
                           if minimum <= bw[0] <= maximum]
    elif minimum is not None:
        bounded_weights = [bw for bw in bounded_weights
                           if minimum <= bw[0]]
    elif maximum is not None:
        bounded_weights = [bw for bw in bounded_weights
                           if bw[0] <= maximum]
    else:
        # Both minimum and maximum are None - the bound list is the same
        # as the original
        return bounded_weights
    # If weights were removed, attach new endpoints where they would have
    # appeared in the original curve
    if (bounded_weights[0][0] > weights[0][0] and
            bounded_weights[0][0] != minimum):
        bounded_weights.insert(0, (minimum, _linear_interp(weights, minimum)))
    if (bounded_weights[-1][0] < weights[-1][0] and
            bounded_weights[-1][0] != maximum):
        bounded_weights.append((maximum, _linear_interp(weights, maximum)))
    return bounded_weights