def A(parasiteTree, hostTree, phi, ep, eh):
    ''' The A table for the dynamic program. '''
    global Amemo

    if (ep, eh) in Amemo: return Amemo[(ep, eh)]

    if tipEdge(eh, hostTree):
        if tipEdge(ep, parasiteTree) and \
           phi[endVertex(ep, parasiteTree)] == endVertex(eh, hostTree):
            return [CostVector(0, 0, 0, 0, 1)]
        else:
            return [CostVector(INF, INF, INF, INF, 0)]
    else:
        ehLeftChild = leftChildEdge(eh, hostTree)
        ehRightChild = rightChildEdge(eh, hostTree)

        # Cospeciation
        if tipEdge(ep, parasiteTree):
            cospeciation = [CostVector(INF, INF, INF, INF, 0)]
        else:
            epLeftChild = leftChildEdge(ep, parasiteTree)
            epRightChild = rightChildEdge(ep, parasiteTree)

            cospeciation1 = merge(C(parasiteTree, hostTree, phi, \
                                    epLeftChild, ehLeftChild), \
                                  C(parasiteTree, hostTree, phi, \
                                    epRightChild, ehRightChild), \
                                  ep, eh, epLeftChild, ehLeftChild, \
                                  epRightChild, ehRightChild, "cospeciation")

            cospeciation2 = merge(C(parasiteTree, hostTree, phi, \
                                    epLeftChild, ehRightChild), \
                                  C(parasiteTree, hostTree, phi, \
                                    epRightChild, ehLeftChild), \
                                  ep, eh, epLeftChild, ehRightChild, \
                                  epRightChild, ehLeftChild, "cospeciation")

            cospeciation = cospeciation1 + cospeciation2

        # Loss
        loss1 = lossmerge(ep, eh, ehLeftChild, \
                     C(parasiteTree, hostTree, phi, ep, ehLeftChild))

        loss2 = lossmerge(ep, eh, ehRightChild, \
                     C(parasiteTree, hostTree, phi, ep, ehRightChild))

        loss = loss1 + loss2

        output = paretoFilter(cospeciation + loss)
        Amemo[(ep, eh)] = output
        return output
def merge(CVlist1, CVlist2, ep, eh, epChild1, ehChild1, epChild2, \
          ehChild2, eventType):
    ''' Given two lists of CostVectors, returns a new list of CostVectors, each
        of which is the sum of a pair of vectors from the two given lists.'''
    global CVevents
    global CVallEvents
    global CandidateCVlist

    intersection = CONFIG.intersection
    if intersection:
        global CVseen
        global CVcommonEvents

    output = []
    for v in CVlist1:
        for w in CVlist2:
            if eventType == "cospeciation":
                newCV = CostVector(1, 0, 0, 0, 1) + v + w
            elif eventType == "duplication":
                newCV = CostVector(0, 1, 0, 0, 1) + v + w
            else:  # eventType == "switch":
                newCV = CostVector(0, 0, 1, 0, 1) + v + w
            keepnewCV = True
            for cv in CandidateCVlist:
                if cv < newCV:
                    keepnewCV = False
                    break

            if keepnewCV:
                output.append(newCV)
                vsoln = (epChild1, ehChild1) + v.toTupleCDSL()
                wsoln = (epChild2, ehChild2) + w.toTupleCDSL()
                if eventType == "switch":
                    eventType = "switch to " + str(ehChild2)
                nswe = (ep, eh, eventType) + newCV.toTupleCDSL()
                ns = (ep, eh) + newCV.toTupleCDSL()
                CVevents[nswe].add(nswe)
                CVevents[nswe] = CVevents[nswe].\
                                 union(CVallEvents[vsoln]).\
                                 union(CVallEvents[wsoln])
                CVallEvents[ns] = CVallEvents[ns].union(CVevents[nswe])

                if intersection:
                    key = newCV.toTupleCDSL()
                    if CVseen[key]:
                        CVcommonEvents[key] = CVcommonEvents[key].\
                                              intersection(CVevents[nswe])
                    else:
                        CVcommonEvents[key] = CVevents[nswe]
                        CVseen[key] = True
    return output
def C(parasiteTree, hostTree, phi, ep, eh):
    ''' The C table for the dynamic program. '''

    global Cmemo

    if (ep, eh) in Cmemo: return Cmemo[(ep, eh)]

    # Option 1:  Pass through
    passThrough = A(parasiteTree, hostTree, phi, ep, eh)

    if tipEdge(ep, parasiteTree):  # The options below don't apply to tips
        return passThrough

    else:
        epLeftChild = leftChildEdge(ep, parasiteTree)
        epRightChild = rightChildEdge(ep, parasiteTree)

        # Option 2:  Duplicate here
        duplicate = CostVector(0, 1, 0, 0, 1) * \
                    merge(C(parasiteTree, hostTree, phi, epLeftChild, eh), \
                          C(parasiteTree, hostTree, phi, epRightChild, eh))

        # Option 3:  Switch here

        switch1 = CostVector(0, 0, 1, 0, 1) * \
                  merge(C(parasiteTree, hostTree, phi, epLeftChild, eh), \
                        switches(parasiteTree, hostTree, phi, epRightChild, eh))

        switch2 = CostVector(0, 0, 1, 0, 1) * \
                  merge(C(parasiteTree, hostTree, phi, epRightChild, eh), \
                        switches(parasiteTree, hostTree, phi, epLeftChild, eh))

        switch = switch1 + switch2

    output = paretoFilter(passThrough + duplicate + switch)
    Cmemo[(ep, eh)] = output
    return output
def lossmerge(ep, eh, ehChild, CVlist):
    global CVevents
    global CVallEvents
    global CandidateCVlist

    intersection = CONFIG.intersection
    if intersection:
        global CVseen
        global CVcommonEvents

    output = []
    for v in CVlist:
        newCV = CostVector(0, 0, 0, 1, 1) + v
        keepnewCV = True
        for cv in CandidateCVlist:
            if cv < newCV:
                keepnewCV = False
                break

        if keepnewCV:
            output.append(newCV)
            vsoln = (ep, ehChild) + v.toTupleCDSL()
            nswe = (ep, eh, "loss " + str(ehChild)) + newCV.toTupleCDSL()
            ns = (ep, eh) + newCV.toTupleCDSL()
            CVevents[nswe].add(nswe)
            CVevents[nswe] = CVevents[nswe].union(CVallEvents[vsoln])
            CVallEvents[ns] = CVallEvents[ns].union(CVevents[nswe])

            if intersection:
                key = newCV.toTupleCDSL()
                if CVseen[key]:
                    CVcommonEvents[key] = CVcommonEvents[key].\
                                              intersection(CVevents[nswe])
                else:
                    CVcommonEvents[key] = CVevents[nswe]
                    CVseen[key] = True
    return output
def lossmerge(ep, eh, ehChild, CVlist):
    global CVevents
    global CVallEvents
    global CandidateCVlist

    intersection = CONFIG.intersection
    if intersection:
        global CVseen
        global CVcommonEvents
    
    output = []
    for v in CVlist:
        newCV = CostVector(0, 0, 0, 1, 1) + v
        keepnewCV = True
        for cv in CandidateCVlist:
            if cv < newCV: 
                keepnewCV = False
                break
    
        if keepnewCV:
            output.append(newCV)
            vsoln = (ep, ehChild) + v.toTupleCDSL()
            nswe = (ep, eh, "loss "+str(ehChild)) + newCV.toTupleCDSL()
            ns = (ep, eh) + newCV.toTupleCDSL()
            CVevents[nswe].add(nswe)
            CVevents[nswe] = CVevents[nswe].union(CVallEvents[vsoln])
            CVallEvents[ns] = CVallEvents[ns].union(CVevents[nswe])

            if intersection:
                key = newCV.toTupleCDSL()
                if CVseen[key]:
                    CVcommonEvents[key] = CVcommonEvents[key].\
                                              intersection(CVevents[nswe])
                else:
                    CVcommonEvents[key] = CVevents[nswe]
                    CVseen[key] = True
    return output
def merge(CVlist1, CVlist2, ep, eh, epChild1, ehChild1, epChild2, \
          ehChild2, eventType):
    ''' Given two lists of CostVectors, returns a new list of CostVectors, each
        of which is the sum of a pair of vectors from the two given lists.'''
    global CVevents
    global CVallEvents
    global CandidateCVlist

    intersection = CONFIG.intersection
    if intersection:
        global CVseen
        global CVcommonEvents
        
    output = []
    for v in CVlist1:
        for w in CVlist2:
            if eventType == "cospeciation":
                newCV = CostVector(1, 0, 0, 0, 1) + v + w
            elif eventType == "duplication":
                newCV = CostVector(0, 1, 0, 0, 1) + v + w
            else:   # eventType == "switch":
                newCV = CostVector(0, 0, 1, 0, 1) + v + w
            keepnewCV = True
            for cv in CandidateCVlist:
                if cv < newCV: 
                    keepnewCV = False
                    break
    
            if keepnewCV:
                output.append(newCV)
                vsoln = (epChild1, ehChild1) + v.toTupleCDSL()
                wsoln = (epChild2, ehChild2) + w.toTupleCDSL()
                if eventType == "switch": eventType = "switch to "+str(ehChild2)
                nswe = (ep, eh, eventType) + newCV.toTupleCDSL()
                ns = (ep, eh) + newCV.toTupleCDSL()
                CVevents[nswe].add(nswe)
                CVevents[nswe] = CVevents[nswe].\
                                 union(CVallEvents[vsoln]).\
                                 union(CVallEvents[wsoln])
                CVallEvents[ns] = CVallEvents[ns].union(CVevents[nswe])

                if intersection:
                    key = newCV.toTupleCDSL()
                    if CVseen[key]:
                        CVcommonEvents[key] = CVcommonEvents[key].\
                                              intersection(CVevents[nswe])
                    else:
                        CVcommonEvents[key] = CVevents[nswe]
                        CVseen[key] = True
    return output
def tupleToCV(entry):
    return CostVector(entry[0], entry[1], entry[2], entry[3], entry[4])