def _supercounts(graph, template_event_set, subcount_table):
    ''' Given
        graph              - a reconciliation graph
        template_event_set - a reconciliation in event-set form
        subcount_table     - a mapping from nodes in the graph to subcounts
    Computes the stratified symmetric supercounts of every node in the graph
    with respect to the template. Relies on pre-computed subcounts
    '''
    table = {}
    for n in graph.preorder():
        if n.isRoot():
            table[n] = DistanceFunction.kronicker(0)
        elif n.isMap():
            def process_parent(event_parent):
                shift_amount = 1 if event_parent not in template_event_set else -1
                parent_super = table[event_parent]
                otherChild = event_parent.otherChild(n)
                convolved = parent_super.convolve(subcount_table[otherChild]) \
                                if otherChild \
                                else parent_super
                return convolved.shift(shift_amount)
            sup_count = reduce(lambda x, y: x.sum(y), map(process_parent, n.parents))
            table[n] = sup_count
        else:
            shift_amount = 1 if n not in template_event_set else -1
            # Event nodes inherit their single parent's super-count.
            # Shallow copy is same because we don't mutatate Fn's
            table[n] = table[n.parents[0]]
    return table
def _supercounts(graph, template_event_set, subcount_table):
    ''' Given
        graph              - a reconciliation graph
        template_event_set - a reconciliation in event-set form
        subcount_table     - a mapping from nodes in the graph to subcounts
    Computes the stratified symmetric supercounts of every node in the graph
    with respect to the template. Relies on pre-computed subcounts
    '''
    table = {}
    for n in graph.preorder():
        if n.isRoot():
            table[n] = DistanceFunction.kronicker(0)
        elif n.isMap():

            def process_parent(event_parent):
                shift_amount = 1 if event_parent not in template_event_set else -1
                parent_super = table[event_parent]
                otherChild = event_parent.otherChild(n)
                convolved = parent_super.convolve(subcount_table[otherChild]) \
                                if otherChild \
                                else parent_super
                return convolved.shift(shift_amount)

            sup_count = reduce(lambda x, y: x.sum(y),
                               map(process_parent, n.parents))
            table[n] = sup_count
        else:
            shift_amount = 1 if n not in template_event_set else -1
            # Event nodes inherit their single parent's super-count.
            # Shallow copy is same because we don't mutatate Fn's
            table[n] = table[n.parents[0]]
    return table
def _subcounts(graph, template_event_set):
    ''' Given
        graph              - a reconciliation graph
        template_event_set - a reconciliation in event-set form
    Computes the stratified symmetric subcounts of every node in the graph
    with respect to the template
    '''
    table = {}
    for n in graph.postorder():
        if n.isLeaf():
            table[n] = DistanceFunction.kronicker(-1)
        elif n.isMap():
            table[n] = reduce(lambda x, y: x.sum(y), \
                                       [table[c] for c in n.children])
        else:
            shift_amount = 1 if n not in template_event_set else -1
            table[n] = reduce(lambda x, y: x.convolve(y), \
                                       [table[c] for c in n.children])
            table[n] = table[n].shift(shift_amount)
    return table
def _subcounts(graph, template_event_set):
    ''' Given
        graph              - a reconciliation graph
        template_event_set - a reconciliation in event-set form
    Computes the stratified symmetric subcounts of every node in the graph
    with respect to the template
    '''
    table = {}
    for n in graph.postorder():
        if n.isLeaf():
            table[n] = DistanceFunction.kronicker(-1)
        elif n.isMap():
            table[n] = reduce(lambda x, y: x.sum(y), \
                                       [table[c] for c in n.children])
        else:
            shift_amount = 1 if n not in template_event_set else -1
            table[n] = reduce(lambda x, y: x.convolve(y), \
                                       [table[c] for c in n.children])
            table[n] = table[n].shift(shift_amount)
    return table