import torch


def delta2segment(rois, deltas, means=(0., 0.), stds=(1., 1.), max_t=None):
    """Apply deltas to shift/scale base segments.

    Typically the rois are anchor or proposed segments and the deltas are
    network outputs used to shift/scale those segments.
    This is the inverse function of :func:`segment2delta`.

    Args:
        rois (Tensor): Segments to be transformed. Has shape (N, 2)
        deltas (Tensor): Encoded offsets with respect to each roi.
            Has shape (N, 2 * num_classes). Note N = num_anchors * T when
            rois is a grid of anchors. Offset encoding follows [1]_.
        means (Sequence[float]): Denormalizing means for delta coordinates
        stds (Sequence[float]): Denormalizing standard deviation for delta
            coordinates
        max_t (int): Maximum time for segments. specifies T

    Returns:
        Tensor: Segments with shape (N, 2), where columns represent
            start, end.

    References:
        .. [1] https://arxiv.org/abs/1311.2524

    Example:
        >>> rois = torch.Tensor([[ 0.,  1.],
        >>>                      [ 0.,  1.],
        >>>                      [ 0.,  1.],
        >>>                      [ 5., 5.]])
        >>> deltas = torch.Tensor([[  0.,   0.],
        >>>                        [  1.,   1.],
        >>>                        [  0.,   2.],
        >>>                        [ 0.7, -0.5]])
        >>> delta2segment(rois, deltas, max_t=32)
        tensor([[0.0000, 1.0000],
                [0.1409, 2.8591],
                [0.0000, 4.1945],
                [5.0000, 5.0000]])
    """
    means = deltas.new_tensor(means).repeat(1, deltas.size(1) // 2)
    stds = deltas.new_tensor(stds).repeat(1, deltas.size(1) // 2)
    denorm_deltas = deltas * stds + means
    d_center = denorm_deltas[:, 0::2]
    d_interval = denorm_deltas[:, 1::2]
    # Compute center of each roi
    p_center = ((rois[:, 0] + rois[:, 1]) *
                0.5).unsqueeze(1).expand_as(d_center)
    # Compute interval of each roi
    p_interval = (rois[:, 1] - rois[:, 0]).unsqueeze(1).expand_as(d_interval)
    # Use exp(network energy) to enlarge/shrink each roi

    # clamp d_interval to avoid too large values
    d_interval = d_interval.clamp(max=1.)

    g_interval = p_interval * d_interval.exp()
    # Use network energy to shift the center of each roi
    g_center = p_center + p_interval * d_center
    # Convert center-xy/width/height to top-left, bottom-right
    start = g_center - g_interval * 0.5
    end = g_center + g_interval * 0.5
    if max_t is not None:
        start = start.clamp(min=0, max=max_t)
        end = end.clamp(min=0, max=max_t)
    segments = torch.stack([start, end], dim=-1).view_as(deltas)
    return segments