def lastTransitiveClosureTime(numNodes, transaction_list, verbose):
    """
    Given a list of transactions which are in the form of 
    a list of (sender, receiver, timestamp) tuples, return the list
    of times at which open two-paths are closed.
    
    Parameters:
        numNodes         - number of actors (nodes)
        transaction_list - list of (sender, receiver, timestamp) tuples.
                           Sender and receiver are intergers in 0..numNodes-1
                           and timestamps are numeric values.
                           The list must be ordered by timestamp ascending.
        verbose          - if True write debug output to stdout

    Return value:
        List of tuples (open_time, delta_time)
        where open_time is second timestamp in open two-path and
        and delta_time is it took open two-paths to be closed (the difference
        in timestamp between the closing transaction (arc) and the second
        (along arc) timestamp in the open two-path, BUT only if the second
        arc in two-path has higher timestamp than the first (i.e. we
        don't count a two-path that goes backward in time along the path)
    """
    G = Digraph(numNodes)
    lastTime = None
    delta_time_list = []
    for trans in transaction_list:
        assert (trans[TSENDER] >= 0 and trans[TSENDER] < numNodes)
        assert (trans[TRECEIVER] >= 0 and trans[TRECEIVER] < numNodes)
        assert (lastTime is None or trans[TTIME] >= lastTime)
        if verbose:
            print trans[TSENDER], '->', trans[TRECEIVER], ' at time ', trans[
                TTIME],
        closed_2paths_v = closedTwoPaths(G, trans[TSENDER], trans[TRECEIVER])
        if len(closed_2paths_v) > 0:
            if verbose:
                print 'closed', len(
                    closed_2paths_v), 'two-paths via', closed_2paths_v
            path_2nd_time_list = []
            for v in closed_2paths_v:
                path_1st_time = G.G[trans[TSENDER]][v]
                path_2nd_time = G.G[v][trans[TRECEIVER]]
                if path_2nd_time > path_1st_time:
                    if verbose:
                        print '  path via', v, 'is forward in time (', path_1st_time, ',', path_2nd_time, '), considering'
                    path_2nd_time_list.append(path_2nd_time)
                else:
                    if verbose:
                        print '  path via', v, 'is backwards in time (', path_1st_time, ',', path_2nd_time, '), skipping'
            if len(path_2nd_time_list) > 0:
                path_2nd_time_max = max(path_2nd_time_list)
                delta_time = trans[TTIME] - path_2nd_time_max
                if verbose:
                    print '  ', len(
                        path_2nd_time_list
                    ), 'paths considered as forward in time, max 2nd time is', path_2nd_time_max, ' appending delta_time =', delta_time
                delta_time_list.append((path_2nd_time_max, delta_time))
            else:
                if verbose:
                    print '  (no paths forward in time)'
        else:
            if verbose:
                print
        if not G.isArc(trans[TSENDER], trans[TRECEIVER]):
            # only insert arc if not already one there, to keep first
            # time on transactions, not subsequent times.
            G.insertArc(trans[TSENDER], trans[TRECEIVER], trans[TTIME])
        lastTime = trans[TTIME]
    return delta_time_list
def openTwoPathOpenTimes(numNodes, transaction_list, verbose):
    """Given a list of transactions which are in the form of 
    a list of (sender, receiver, timestamp) tuples, return the list
    of times at open two-paths are created.
    
    Parameters:
        numNodes         - number of actors (nodes)
        transaction_list - list of (sender, receiver, timestamp) tuples.
                           Sender and receiver are intergers in 0..numNodes-1
                           and timestamps are numeric values.
                           The list must be ordered by timestamp ascending.
        verbose          - if True write debug output to stdout

    Return value: 
        dict { (i, j, k) : t } where (i, j, k) is an open
        directed two-path (note this means it is not part of a
        transitive triad i.e. i -> k is not present, but it may be
        part of a cyclic triad i.e. k -> i may be present), and t is
        the time the two-path was created (open). BUT only if the
        second arc in two-path has higher timestamp than the first
        (i.e. we don't count a two-path that goes backward in time
        along the path)

    """
    G = Digraph(numNodes)
    lastTime = None
    pathdict = {}  # dict mapping (i,j,k) two-path tuple to open time
    for trans in transaction_list:
        assert (trans[TSENDER] >= 0 and trans[TSENDER] < numNodes)
        assert (trans[TRECEIVER] >= 0 and trans[TRECEIVER] < numNodes)
        assert (lastTime is None or trans[TTIME] >= lastTime)
        if verbose:
            print trans[TSENDER], '->', trans[TRECEIVER], ' at time ', trans[
                TTIME],
        (ulist, vlist) = openTwoPaths(G, trans[TSENDER], trans[TRECEIVER])
        i = trans[TSENDER]
        j = trans[TRECEIVER]
        if len(ulist) > 0 or len(vlist) > 0:
            if verbose:
                print 'opened', len(ulist) + len(vlist), 'two-paths'
            for u in ulist:  # u -> i -> j
                path_1st_time = G.G[u][i]
                path_2nd_time = trans[TTIME]  # will be G.G[i][j] when inserted
                if path_2nd_time > path_1st_time:
                    if verbose:
                        print '  path from', u, 'is forward in time (', path_1st_time, ',', path_2nd_time, '), including'
                    if not pathdict.has_key((u, i, j)):
                        pathdict[(u, i, j)] = path_2nd_time
                    else:
                        if verbose:
                            print '  two-path ', u, i, j, 'already present from time', pathdict[
                                (u, i, j)], ' not updating'
                else:
                    if verbose:
                        print '  path from', u, 'is backwards in time (', path_1st_time, ',', path_2nd_time, '), skipping'
            if len(vlist) > 0:
                if verbose:
                    print '  ', len(vlist), ' are backward in time, skipping'
            for v in vlist:  #  i -> j -> v
                path_1st_time = trans[TTIME]  # will be G.G[i][j] when inserted
                path_2nd_time = G.G[j][v]
                # as the transactions are ordered in time we cannot
                # have this a two-path ordered in time, as the 2nd is older
                assert (path_1st_time > path_2nd_time)
        else:
            if verbose:
                print

        # now check if the new arc i -> j would close any currently open
        # two-paths. If so, remove those from the dictionary of open
        # two paths.
        for v in closedTwoPaths(G, i, j):
            # For each v, i -> v -> j is now a two-path closed by i -> j
            if verbose:
                print '  removing ', i, v, j, ' as it is now a transitive triad'
            # note (i, v, j) might exist in pathdict as it was an open two-path
            # but NOT NECESSARILY as it might have been ignored as backward in time
            if pathdict.has_key((i, v, j)):
                pathdict.pop((i, v, j))
            else:
                if verbose:
                    print '  (did not exist in dict)'

        # add this new arc i -> j to the graph
        # note there is a potential inconsistency here in that this
        # arc might already exist in which case we update the time with
        # the new (later) time, however in the pathdict dictionary
        # we do not update times of open two-paths but keep the first
        # opening time. Need to decide which one really is correct.
        G.insertArc(trans[TSENDER], trans[TRECEIVER], trans[TTIME])

        lastTime = trans[TTIME]
    return pathdict