import torch


def calc_iou_many_to_one(boxes, ground_truth):
    """
    Param: boxes: shape([N, 4]), ground_truth: shape([4])
    Return: IoU of boxes over on ground truth box
    
    Each boxes should be like [x1, y1, x2, y2],
    and x1 <= x2, y1 <= y2
    """
    
    (gt_x1, gt_y1, gt_x2, gt_y2) = ground_truth
    boxes_x1s = boxes[:, 0]
    boxes_y1s = boxes[:, 1]
    boxes_x2s = boxes[:, 2]
    boxes_y2s = boxes[:, 3]
    
    assert (gt_x1 <= gt_x2) & (gt_y1 <= gt_y2)
    assert (boxes_x1s <= boxes_x2s).all() & (boxes_y1s <= boxes_y2s).all()
    
    inter_x1s = torch.max(boxes_x1s, gt_x1)
    inter_y1s = torch.max(boxes_y1s, gt_y1)
    inter_x2s = torch.min(boxes_x2s, gt_x2)
    inter_y2s = torch.min(boxes_y2s, gt_y2)
    
    assert (inter_x1s <= inter_x2s).all() & (inter_y1s <= inter_y2s).all()
        
    gt_area = (gt_x2 - gt_x1) * (gt_y2 - gt_y1)
    box_areas = (boxes_x2s - boxes_x1s) * (boxes_y2s - boxes_y1s)
    intersect_areas = (inter_x2s - inter_x1s) * (inter_y2s - inter_y1s)
    
    union_area = gt_area + box_areas - intersect_areas
    intersecion_area = intersect_areas

    smooth = 1e-6    
    return (intersecion_area + smooth) / (union_area + smooth)