def get_free_intervals(target_task, swap_schedule, access_of_target_tensor, key=0, asc=True):
    target_task.tensor.update_swap_time()
    if ((target_task.back_boundary - target_task.front_boundary) < target_task.time_cost):
        return []
    intervals = []
    for task in swap_schedule:
        if (target_task.front_boundary <= task.start_time < task.end_time <= target_task.back_boundary):
            intervals.append((task.start_time, task.end_time))
        elif (task.start_time < target_task.front_boundary < task.end_time < target_task.back_boundary):
            intervals.append((target_task.front_boundary, task.end_time))
        elif (target_task.front_boundary < task.start_time < target_task.back_boundary < task.end_time):
            intervals.append((task.start_time, target_task.back_boundary))
        elif (task.start_time < target_task.front_boundary < target_task.back_boundary < task.end_time):
            return []
    intervals = sorted(intervals, key=(lambda x: x[0]))
    occupied_intervals = []
    i = 0
    while (i < len(intervals)):
        interval = intervals[i]
        l = interval[0]
        r = interval[1]
        flag = False
        while ((i < (len(intervals) - 1)) and (intervals[(i + 1)][0] <= r)):
            r = max(r, intervals[(i + 1)][1])
            flag = True
            i += 1
        occupied_intervals.append((l, r))
        if (not flag):
            i += 1
    not_occupied_intervals = []
    s = target_task.front_boundary
    for interval in occupied_intervals:
        if (s < interval[0]):
            not_occupied_intervals.append((s, interval[0]))
        s = interval[1]
    if (s < target_task.back_boundary):
        not_occupied_intervals.append((s, target_task.back_boundary))
    if (len(not_occupied_intervals) == 0):
        return []
    i = 0
    j = 0
    not_occupied_intervals = sorted(not_occupied_intervals, key=(lambda x: x[key]), reverse=False)
    while (j < len(access_of_target_tensor)):
        if (i >= len(not_occupied_intervals)):
            break
        access = access_of_target_tensor[j]
        (start, end) = not_occupied_intervals[i]
        if (start < access.start_time < end <= access.end_time):
            not_occupied_intervals[i] = (start, access.start_time)
            i += 1
        elif (start < access.start_time < access.end_time < end):
            not_occupied_intervals[i] = (start, access.start_time)
            not_occupied_intervals.insert((i + 1), (access.end_time, end))
            i += 1
            j += 1
        elif (start == access.start_time < end < access.end_time):
            not_occupied_intervals.pop(i)
            j += 1
        elif (access.start_time <= start < access.end_time < end):
            not_occupied_intervals[i] = (access.end_time, end)
            j += 1
        elif (access.start_time <= start < end <= access.end_time):
            not_occupied_intervals.pop(i)
        else:
            j += 1
    if (not asc):
        not_occupied_intervals = sorted(not_occupied_intervals, key=(lambda x: x[key]), reverse=(not asc))
    return not_occupied_intervals
def get_free_intervals(target_task, swap_schedule, access_of_target_tensor, key=0, asc=True):
    target_task.tensor.update_swap_time()
    if ((target_task.back_boundary - target_task.front_boundary) < target_task.time_cost):
        return []
    intervals = []
    for task in swap_schedule:
        if (target_task.front_boundary <= task.start_time < task.end_time <= target_task.back_boundary):
            intervals.append((task.start_time, task.end_time))
        elif (task.start_time < target_task.front_boundary < task.end_time < target_task.back_boundary):
            intervals.append((target_task.front_boundary, task.end_time))
        elif (target_task.front_boundary < task.start_time < target_task.back_boundary < task.end_time):
            intervals.append((task.start_time, target_task.back_boundary))
        elif (task.start_time < target_task.front_boundary < target_task.back_boundary < task.end_time):
            return []
    intervals = sorted(intervals, key=(lambda x: x[0]))
    occupied_intervals = []
    i = 0
    while (i < len(intervals)):
        interval = intervals[i]
        l = interval[0]
        r = interval[1]
        flag = False
        while ((i < (len(intervals) - 1)) and (intervals[(i + 1)][0] <= r)):
            r = max(r, intervals[(i + 1)][1])
            flag = True
            i += 1
        occupied_intervals.append((l, r))
        if (not flag):
            i += 1
    not_occupied_intervals = []
    s = target_task.front_boundary
    for interval in occupied_intervals:
        if (s < interval[0]):
            not_occupied_intervals.append((s, interval[0]))
        s = interval[1]
    if (s < target_task.back_boundary):
        not_occupied_intervals.append((s, target_task.back_boundary))
    if (len(not_occupied_intervals) == 0):
        return []
    i = 0
    j = 0
    not_occupied_intervals = sorted(not_occupied_intervals, key=(lambda x: x[key]), reverse=False)
    while (j < len(access_of_target_tensor)):
        if (i >= len(not_occupied_intervals)):
            break
        access = access_of_target_tensor[j]
        (start, end) = not_occupied_intervals[i]
        if (start < access.start_time < end <= access.end_time):
            not_occupied_intervals[i] = (start, access.start_time)
            i += 1
        elif (start < access.start_time < access.end_time < end):
            not_occupied_intervals[i] = (start, access.start_time)
            not_occupied_intervals.insert((i + 1), (access.end_time, end))
            i += 1
            j += 1
        elif (start == access.start_time < end < access.end_time):
            not_occupied_intervals.pop(i)
            j += 1
        elif (access.start_time <= start < access.end_time < end):
            not_occupied_intervals[i] = (access.end_time, end)
            j += 1
        elif (access.start_time <= start < end <= access.end_time):
            not_occupied_intervals.pop(i)
        else:
            j += 1
    if (not asc):
        not_occupied_intervals = sorted(not_occupied_intervals, key=(lambda x: x[key]), reverse=(not asc))
    return not_occupied_intervals