Examples
Input: ([('follow', 1, 2), ('snap',), ('is_following', 1, 2, 0), ('unfollow', 1, 2), ('is_following', 1, 2, 0), ('snap',), ('is_following', 1, 2, 1)],)
Expected Output: [None, 0, True, None, True, 1, False]
Explanation: After snap 0, (1->2) is True. Unfollow happens after snap 0 (in snap 1 interval), so snap 0 remains True, but snap 1 becomes False.
Input: ([('follow', 1, 2), ('unfollow', 1, 2), ('follow', 1, 2), ('snap',), ('is_following', 1, 2, 0)],)
Expected Output: [None, None, None, 0, True]
Explanation: All changes occur before the first snap; the final state before snap 0 is following.
Input: ([('unfollow', 5, 6), ('snap',), ('is_following', 5, 6, 0), ('is_following', 7, 8, 0)],)
Expected Output: [None, 0, False, False]
Explanation: Unfollowing a non-existent follow is a no-op. Unseen edges are not followed in any snapshot.
Input: ([('snap',), ('is_following', 1, 1, 0), ('follow', 1, 1), ('snap',), ('is_following', 1, 1, 0), ('is_following', 1, 1, 1)],)
Expected Output: [0, False, None, 1, False, True]
Explanation: The self-follow is created after snap 0 and is included starting at snap 1.
Input: ([],)
Expected Output: []
Explanation: No operations produce no outputs.
Input: ([('follow', 1, 2), ('follow', 1, 3), ('snap',), ('unfollow', 1, 2), ('snap',), ('is_following', 1, 2, 0), ('is_following', 1, 2, 1), ('is_following', 1, 3, 1), ('follow', 1, 2), ('is_following', 1, 2, 1), ('snap',), ('is_following', 1, 2, 2)],)
Expected Output: [None, None, 0, None, 1, True, False, True, None, False, 2, True]
Explanation: Edge (1->2) is True at snap 0, becomes False at snap 1, then is re-followed and becomes True again at snap 2. Edge (1->3) stays True.