def createIntegerForm(self, node, value):
        newForm = CanonicalForm()
        newForm.subforms[1] = value

        self.graph.nodes[node]["form"] = newForm
        newKey = newForm.generateKey()
        self.graph.nodes[node]["canonicalKey"] = newKey
        self.key2uniqueOperatorNodes[newKey] = node
    def generateCanonicalFormForNode(self, node):
        nodeOperator = self.subgraph.nodes[node]["operator"]
        predecessors = list(self.subgraph.predecessors(node))

        if nodeOperator == "+":
            firstPredecessor = predecessors.pop(0)

            newForm = deepcopy(self.subgraph.nodes[firstPredecessor]["form"])
            for pred in predecessors:
                fold = self.subgraph[pred][node]["fold"]

                for i in range(fold):
                    newForm.add(self.subgraph.nodes[pred]["form"])

            self.subgraph.nodes[node]["form"] = newForm

        elif nodeOperator == "*":
            firstPredecessor = predecessors.pop(0)

            newForm = deepcopy(self.subgraph.nodes[firstPredecessor]["form"])
            for pred in predecessors:
                fold = self.subgraph[pred][node]["fold"]

                for i in range(fold):
                    newForm.multiply(self.subgraph.nodes[pred]["form"])

            self.subgraph.nodes[node]["form"] = newForm

        elif nodeOperator == "-":
            order2pred = {}

            if not self.subgraph.nodes[node]["symmetric"]:
                for pred in predecessors:
                    order2pred[self.subgraph[pred][node]["order"]] = pred

            else:
                if len(predecessors) > 1:
                    raise Exception(
                        "Symmetric - operator with more than one argument " +
                        str(node))
                order2pred[0] = predecessors[0]

            sortedOrders = sorted(list(order2pred.keys()))

            if not self.subgraph.nodes[node]["symmetric"]:
                newForm = deepcopy(
                    self.subgraph.nodes[order2pred[sortedOrders[0]]]["form"])
                newForm.subtract(
                    self.subgraph.nodes[order2pred[sortedOrders[1]]]["form"])
            else:
                newForm = CanonicalForm()
                newForm.subtract(
                    self.subgraph.nodes[order2pred[sortedOrders[0]]]["form"])

            self.subgraph.nodes[node]["form"] = newForm

        else:
            raise Exception("Operator not supported! " + nodeOperator)
    def createPrimeForm(self, node):
        newSubformKey = self.subformFactory.createSubform(node)
        newForm = CanonicalForm()
        newForm.subforms[newSubformKey] = 1

        self.graph.nodes[node]["form"] = newForm
        newKey = newForm.generateKey()
        self.graph.nodes[node]["canonicalKey"] = newKey
        self.key2uniqueOperatorNodes[newKey] = node
def reduceForm(form):
    gcd = reduce(math.gcd, list(form.subforms.keys()))

    if gcd == 1:
        return form

    reducedForm = CanonicalForm()

    for subKey in form.subforms:
        reducedForm.subforms[subKey] = form.subforms[subKey] // gcd

    return reducedForm
    def clusterSubforms(self):
        #zmienic na generacje kluczy zredukowanych form
        reducedKey2nodes = {}
        subKeyList = list( self.monomials )
        reducedKey2form = {}
        
        for node in self.node2subforms:
            coeffs = []
            for subKey in subKeyList:
                coeffs.append(self.node2subforms[node][subKey])
                
            gcd = reduce(math.gcd, coeffs)
            if gcd != 1:
                form = CanonicalForm()
                subforms = self.node2subforms[node]
                for subKey in subforms:
                    form.subforms[subKey] = subforms[subKey] // gcd
                    
                reducedKey = form.generateKey()
            else:
                form = CanonicalForm()
                form.subforms = self.node2subforms[node]
                reducedKey = form.generateKey()
                
            if reducedKey in reducedKey2nodes:
                reducedKey2nodes[reducedKey].add(node)
            else:
                reducedKey2nodes[reducedKey] = set([ node ])
                reducedKey2form[reducedKey] = form
#                
        return reducedKey2nodes, reducedKey2form
    def insertNode(self, node):
        originalSucc = list(self.graph.successors(node))
        originalPred = list(self.graph.predecessors(node))

        newSucc = list(self.subgraph.successors(node))
        newPred = list(self.subgraph.predecessors(node))

        if len(originalPred) > len(newPred):
            newInputs = set(originalPred) - set(newPred)

            for newInp in newInputs:

                if not newInp in self.inputNodes:
                    self.inputNodes.append(newInp)
                    name = "i" + str(self.inpInd)
                    self.inpInd += 1

                    newAtom = CanonicalAtom(name, 1, newInp)

                    newSubform = CanonicalSubform()
                    newSubform.atoms[newAtom.name] = newAtom

                    newForm = CanonicalForm()
                    newForm.subforms[newSubform.getKey()] = newSubform

                    self.subgraph.add_node(newInp,
                                           form=newForm,
                                           kind="input",
                                           variable=name)

                newEdgeFold = self.graph[newInp][node]["fold"]

                if self.graph.nodes[node]["symmetric"]:
                    self.subgraph.add_edge(newInp, node, fold=newEdgeFold)
                else:
                    newEdgeOrder = self.graph[newInp][node]["order"]
                    self.subgraph.add_edge(newInp,
                                           node,
                                           fold=newEdgeFold,
                                           order=newEdgeOrder)

        self.generateCanonicalFormForNode(node)

        if len(originalSucc) > len(newSucc):
            self.outputNodes.append(node)
            self.subgraph.nodes[node]["kind"] = "output"
            self.subgraph.nodes[node]["variable"] = "return"
    def optimize(self):
        self.findPotentialSolutions()

        unitPolynomial = frozenset([1])

        optimizationResult = OptimizationResult()

        if not self.potentialSolutions:
            optimizationResult.dividingWasPossible = False
            #            print("nie znaleziono zadnych rozwiazan!")
            #            print(self.form.subforms)
            if not optimizationResult.relativelyPrimes:
                for subKey in self.form.subforms:
                    newForm = CanonicalForm()
                    newForm.subforms[subKey] = self.form.subforms[subKey]
                    optimizationResult.relativelyPrimes.append(newForm)

            return optimizationResult

        if self.gcdKeys == unitPolynomial:
            optimizationResult.dividingWasPossible = False

            for subKey in self.form.subforms:
                newForm = CanonicalForm()
                newForm.subforms[subKey] = self.form.subforms[subKey]
                optimizationResult.relativelyPrimes.append(newForm)

            return optimizationResult

        bestPolynomial = max(self.potentialSolutions,
                             key=lambda item: item.profit)
        gcdCoeff = reduce(math.gcd, list(self.form.subforms.values()))

        quotientForm, dividerForm, divisibleForm, restForm = findPolynomialCoeff(
            self.form, bestPolynomial, gcdCoeff)

        optimizationResult.dividingWasPossible = True
        optimizationResult.quotientForm = quotientForm
        optimizationResult.deviderForm = dividerForm
        optimizationResult.divisibleForm = divisibleForm
        optimizationResult.remainderForm = restForm
        #        if len( bestPolynomial.resultsMonomials ) < len(bestPolynomial.gcdMonomials)*len(bestPolynomial.intermediateMonomials):
        #        print(15*"#")
        #        print("Found best solution from ", len(processedPolynomials))
        #        print("Highest profit: ", bestPolynomial.profit)
        #        print("Full form len: ", len(self.form.subforms))
        #        print("Best poly describes: ", len(bestPolynomial.resultsMonomials))
        #        print("Using polynomials of size: ", len(bestPolynomial.gcdMonomials), " and ",len(bestPolynomial.intermediateMonomials))
        return optimizationResult
def findPolynomialCoeff(form, poly, gcdCoeff):
    #        remainderForm = CanonicalForm()
    #inicjalizacja wartosci
    quotientForm = CanonicalForm()
    dividerForm = CanonicalForm()

    #    if len(poly.intermediateMonomials) > len(poly.gcdMonomials):
    #        reverseGcdMat = {}
    #
    #        for gcdKey in poly.gcdMatrix:
    #            for interKey in poly.gcdMatrix[gcdKey]:
    #                resKey = poly.gcdMatrix[gcdKey][interKey]
    #
    #                if not interKey in reverseGcdMat:
    #                    reverseGcdMat[interKey] = {}
    #
    #                reverseGcdMat[interKey][gcdKey] = resKey
    #
    #        poly = DivisiblePolynomial(reverseGcdMat, form)

    for subKey in poly.gcdMonomials:
        dividerForm.subforms[subKey] = None

    for subKey in poly.intermediateMonomials:
        quotientForm.subforms[subKey] = None

    #znalezienie wynikow zaleznych tylko i wylacznie od pojedynczych intermediatow
    intermediate2results = {}

    for dividerKey in poly.gcdMatrix:
        for interKey in poly.gcdMatrix[dividerKey]:
            if not interKey in intermediate2results:
                intermediate2results[interKey] = set([])

            intermediate2results[interKey].add(
                poly.gcdMatrix[dividerKey][interKey])

    intermediate2uniqueResults = {}

    for interKey in intermediate2results:
        uniqueInters = intermediate2results[interKey]
        otherInters = set(intermediate2results.keys())
        otherInters.remove(interKey)

        for otherInter in otherInters:
            uniqueInters -= intermediate2results[otherInter]

        intermediate2uniqueResults[interKey] = uniqueInters

    #okreslenie wartosci intermediatow

    for intermediate in intermediate2uniqueResults:
        uniqueResCoeffs = [
            form.subforms[subKey]
            for subKey in intermediate2uniqueResults[intermediate]
        ]
        #        print(uniqueResCoeffs)
        newCoeff = 1
        if uniqueResCoeffs:
            newCoeff = reduce(math.gcd, uniqueResCoeffs)
        quotientForm.subforms[intermediate] = newCoeff

    #okreslenie wartosci dzielnikow

    for gcdKey in poly.gcdMatrix:
        for interKey in poly.gcdMatrix[gcdKey]:
            resKey = poly.gcdMatrix[gcdKey][interKey]
            if resKey in intermediate2uniqueResults[interKey]:
                b = form.subforms[resKey] // quotientForm.subforms[interKey]
                #                if b > 1:
                #                    print("lol")
                #                    dividerForm.subforms[gcdKey] = randint( 1, b )
                #                else:
                dividerForm.subforms[gcdKey] = b
#                dividerForm.subforms[gcdKey] = form.subforms[resKey]//quotientForm.subforms[interKey]

    divisibleForm = multiplyForms(quotientForm, dividerForm)
    restForm = subtractForms(form, divisibleForm)

    #    testForm = addForms(restForm, divisibleForm)
    #    if form.generateKey() != testForm.generateKey():
    #        raise Exception("Simplyfied form is not identical to source form!")
    #wiecej info
    #        print(15*"#")
    #        print("Full form len: ", len(self.form.subforms))
    #        print("Divisible describes: ", len(divisibleForm.subforms))
    #        print("By multipling: ", len(quotientForm.subforms), " and ", len(dividerForm.subforms))
    #        print("Rest size: ", len(restForm.subforms))

    return quotientForm, dividerForm, divisibleForm, restForm
    def createIntegerCanonical(self, value):
        newForm = CanonicalForm()
        newForm.subforms[1] = value

        return newForm