Esempio n. 1
0
        /// <summary>
        ///   Copies this instance of an arc and returns the copy.
        /// </summary>
        /// <returns>the copy of the arc.</returns>
        public virtual hyperarc copy()
        {
            var copyOfArc = new hyperarc();

            copy(copyOfArc);
            return(copyOfArc);
        }
 /// <summary>
 /// Replaces the type of the arc with inherited.
 /// </summary>
 /// <param name="origArc">The orig arc.</param>
 /// <param name="newType">The new type.</param>
 public void replaceHyperArcWithInheritedType(hyperarc origArc, Type newType)
 {
     addHyperArc(origArc.nodes, origArc.name, newType);
     origArc.copy(hyperarcs.Last());
     hyperarcs.Last().DisplayShape = origArc.DisplayShape;
     removeHyperArc(origArc);
 }
Esempio n. 3
0
 internal Boolean ruleIsRecognized(hyperarc dangleHyperArc, List<node> neighborNodes, node nodeRemoved)
 {
     IEnumerable<string> neighborlabels = null ;
     neighborlabels = neighborNodes.Aggregate(neighborlabels, (current, n) => current.Union(n.localLabels));
     return ((labelsMatch(dangleHyperArc.localLabels, neighborlabels.ToList()))
            && ((nodeRemoved == null) || (dangleHyperArc.nodes.Contains(nodeRemoved))));
 }
Esempio n. 4
0
        /// <summary>
        /// Replaces the type of the arc with inherited.
        /// </summary>
        /// <param name="origArc">The orig arc.</param>
        /// <param name="newType">The new type.</param>
        public void replaceHyperArcWithInheritedType(hyperarc origArc, Type newType)
        {
            var newHa = addHyperArc(origArc.nodes, origArc.name, newType);

            origArc.copy(newHa);
            newHa.DisplayShape = origArc.DisplayShape;
            removeHyperArc(origArc);
        }
Esempio n. 5
0
 /// <summary>
 /// Removes the hyper arc.
 /// </summary>
 /// <param name="arcToRemove">The arc to remove.</param>
 public void removeHyperArc(hyperarc arcToRemove)
 {
     for (var i = arcToRemove.nodes.Count - 1; i >= 0; i--)
     {
         arcToRemove.DisconnectFrom(arcToRemove.nodes[i]);
     }
     hyperarcs.Remove(arcToRemove);
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="ruleHyperarc"/> class.
 /// Converts a hyperarc to a ruleHyperArc and returns it with default Booleans.
 /// The original hyperarc is unaffected.
 /// </summary>
 /// <param name="ha">The hyperarc, ha.</param>
 /// <returns></returns>
 public ruleHyperarc(hyperarc ha)
     : this(ha.name)
 {
     DisplayShape = ha.DisplayShape;
     TargetType   = ha.GetType().ToString();
     localLabels.AddRange(ha.localLabels);
     localVariables.AddRange(ha.localVariables);
     nodes.AddRange(ha.nodes);
 }
Esempio n. 7
0
 /// <summary>
 /// Adds the arc to the graph and connects it between these nodes.
 /// </summary>
 /// <param name="newArc">The new arc.</param>
 /// <param name="attachedNodes">The nodes.</param>
 public void addHyperArc(hyperarc newArc, List <node> attachedNodes = null)
 {
     if (attachedNodes != null)
     {
         foreach (var n in attachedNodes)
         {
             newArc.ConnectTo(n);
         }
     }
     hyperarcs.Add(newArc);
 }
 /// <summary>
 ///   Copies this instance into the (already intialized) copyOfHyperArc.
 /// </summary>
 /// <param name = "copyOfHyperArc">The copy of node.</param>
 public override void copy(hyperarc copyOfHyperArc)
 {
     base.copy(copyOfHyperArc);
     if (copyOfHyperArc is ruleHyperarc)
     {
         var rcopy = (ruleHyperarc)copyOfHyperArc;
         rcopy.containsAllLocalLabels = containsAllLocalLabels;
         rcopy.strictNodeCountMatch   = strictNodeCountMatch;
         foreach (var label in negateLabels)
         {
             rcopy.negateLabels.Add(label);
         }
     }
 }
        /// <summary>
        /// Creates and Adds a new hyperarc to the graph, and connects it
        /// to the stated nodes.
        /// </summary>
        /// <param name="attachedNodes">The nodes.</param>
        /// <param name="newName">The new name.</param>
        /// <param name="hyperarcType">The t.</param>
        public void addHyperArc(List <node> attachedNodes, string newName = "", Type hyperarcType = null)
        {
            hyperarc newArc;

            if (string.IsNullOrWhiteSpace(newName))
            {
                newName = makeUniqueHyperArcName();
            }
            if (hyperarcType == null || hyperarcType == typeof(hyperarc))
            {
                newArc = new hyperarc(newName);
            }
            else
            {
                var types          = new[] { typeof(string), typeof(node), typeof(node) };
                var arcConstructor = hyperarcType.GetConstructor(types);

                var inputs = new object[] { newName, attachedNodes };
                newArc = (hyperarc)arcConstructor.Invoke(inputs);
            }
            addHyperArc(newArc, attachedNodes);
        }
Esempio n. 10
0
        /// <summary>
        /// Predicts whether the option p  invalidates option q.
        /// This invalidation is a tricky thing. For the most part, this function
        /// has been carefully coded to handle all cases. The only exceptions
        /// are from what the additional recognize and apply functions require or modify.
        /// This is handled by actually testing to see if this is true.
        /// </summary>
        /// <param name="p">The p.</param>
        /// <param name="q">The q.</param>
        /// <param name="cand">The cand.</param>
        /// <param name="confluenceAnalysis">The confluence analysis.</param>
        /// <returns></returns>
        private static int doesPInvalidateQ(option p, option q, candidate cand, ConfluenceAnalysis confluenceAnalysis)
        {
            #region Global Labels
            var pIntersectLabels = p.rule.L.globalLabels.Intersect(p.rule.R.globalLabels);
            var pRemovedLabels   = new List <string>(p.rule.L.globalLabels);
            pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
            var pAddedLabels = new List <string>(p.rule.R.globalLabels);
            pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));

            if ( /* first check that there are no labels deleted that the other depeonds on*/
                (q.rule.L.globalLabels.Intersect(pRemovedLabels).Any()) ||

                /* adding labels is problematic if the other rule was recognized under
                 * the condition of containsAllLocalLabels. */
                ((q.rule.containsAllGlobalLabels) && (pAddedLabels.Any())) ||

                /* adding labels is also problematic if you add a label that negates the
                 * other rule. */
                (pAddedLabels.Intersect(q.rule.negateLabels).Any()))
            {
                return(1);
            }
            #endregion

            #region Nodes

            /* first we check the nodes. If two options do not share any nodes, then
             * the whole block of code is skipped. q is to save time if comparing many
             * options on a large graph. However, since there is some need to understand what
             * nodes are saved in rule execution, the following two lists are defined outside
             * of q condition and are used in the Arcs section below. */
            /* why are the following three parameters declared here and not in scope with the
             * other node parameters below? This is because they are used in the induced and
             * shape restriction calculations below - why calculate twice? */
            int      Num_pKNodes  = 0;
            string[] pNodesKNames = null;
            node[]   pKNodes      = null;

            var commonNodes = q.nodes.Intersect(p.nodes);
            if (commonNodes.Any())
            /* if there are no common nodes, then no need to check the details. */
            {
                /* the following arrays of nodes are within the rule not the host. */

                #region  Check whether there are nodes that p will delete that q depends upon.
                var pNodesLNames = from n in p.rule.L.nodes
                                   where ((ruleNode)n).MustExist
                                   select n.name;
                var pNodesRNames = from n in p.rule.R.nodes
                                   select n.name;
                pNodesKNames = pNodesRNames.Intersect(pNodesLNames).ToArray();
                Num_pKNodes  = pNodesKNames.GetLength(0);
                pKNodes      = new node[Num_pKNodes];
                for (var i = 0; i < p.rule.L.nodes.Count; i++)
                {
                    var index = Array.IndexOf(pNodesKNames, p.rule.L.nodes[i].name);
                    if (index >= 0)
                    {
                        pKNodes[index] = p.nodes[i];
                    }
                    else if (commonNodes.Contains(p.nodes[i]))
                    {
                        return(1);
                    }
                }
                #endregion

                #region NodesModified

                /* in the above regions where deletions are checked, we also create lists for potentially
                 * modified nodes, nodes common to both L and R. We will now check these. There are several
                 * ways that a node can be modified:
                 * 1. labels are removed
                 * 2. labels are added (and potentially in the negabels of the other rule).
                 * 3. number of arcs connected, which affect strictDegreeMatch
                 * 4. variables are added/removed/changed
                 *
                 * There first 3 conditions are check all at once below. For the last one, it is impossible
                 * to tell without executing extra functions that the user may have created for rule
                 * recognition. Therefore, additional functions are not check in q confluence check. */
                foreach (var commonNode in commonNodes)
                {
                    var qNodeL = (ruleNode)q.rule.L.nodes[q.nodes.IndexOf(commonNode)];
                    var pNodeL = (ruleNode)p.rule.L.nodes[p.nodes.IndexOf(commonNode)];
                    var pNodeR = (ruleNode)p.rule.R[pNodeL.name];
                    pIntersectLabels = pNodeL.localLabels.Intersect(pNodeR.localLabels);
                    pRemovedLabels   = new List <string>(pNodeL.localLabels);
                    pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    pAddedLabels = new List <string>(pNodeR.localLabels);
                    pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    if ( /* first check that there are no labels deleted that the other depeonds on*/
                        (qNodeL.localLabels.Intersect(pRemovedLabels).Any()) ||

                        /* adding labels is problematic if the other rule was recognized under
                         * the condition of containsAllLocalLabels. */
                        ((qNodeL.containsAllLocalLabels) && (pAddedLabels.Any())) ||

                        /* adding labels is also problematic if you add a label that negates the
                         * other rule. */
                        (pAddedLabels.Intersect(qNodeL.negateLabels).Any()) ||

                        /* if one rule uses strictDegreeMatch, we need to make sure the other rule
                         * doesn't change the degree. */
                        (qNodeL.strictDegreeMatch && (pNodeL.degree != pNodeR.degree)) ||

                        /* actually, the degree can also change from free-arc embedding rules. These
                         * are checked below. */
                        (qNodeL.strictDegreeMatch &&
                         (p.rule.embeddingRules.FindAll(e => (e.RNodeName.Equals(pNodeR.name))).Count > 0)))
                    {
                        return(1);
                    }
                }
                #endregion
            }

            #endregion
            #region Arcs
            var commonArcs = q.arcs.Intersect(p.arcs);
            if (commonArcs.Any())
            /* if there are no common arcs, then no need to check the details. */
            {
                /* the following arrays of arcs are within the rule not the host. */
                #region  Check whether there are arcs that p will delete that q depends upon.
                var pArcsLNames = from n in p.rule.L.arcs
                                  where ((ruleArc)n).MustExist
                                  select n.name;
                var pArcsRNames = from n in p.rule.R.arcs
                                  select n.name;
                var pArcsKNames = new List <string>(pArcsRNames.Intersect(pArcsLNames));
                var pKArcs      = new arc[pArcsKNames.Count()];
                for (var i = 0; i < p.rule.L.arcs.Count; i++)
                {
                    if (pArcsKNames.Contains(p.rule.L.arcs[i].name))
                    {
                        pKArcs[pArcsKNames.IndexOf(p.rule.L.arcs[i].name)] = p.arcs[i];
                    }
                    else if (commonArcs.Contains(p.arcs[i]))
                    {
                        return(1);
                    }
                }
                #endregion


                #region ArcsModified
                foreach (var commonArc in commonArcs)
                {
                    var qArcL = (ruleArc)q.rule.L.arcs[q.arcs.IndexOf(commonArc)];
                    var pArcL = (ruleArc)p.rule.L.arcs[p.arcs.IndexOf(commonArc)];
                    var pArcR = (ruleArc)p.rule.R[pArcL.name];
                    pIntersectLabels = pArcL.localLabels.Intersect(pArcR.localLabels);
                    pRemovedLabels   = new List <string>(pArcL.localLabels);
                    pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    pAddedLabels = new List <string>(pArcR.localLabels);
                    pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    if ( /* first check that there are no labels deleted that the other depeonds on*/
                        (qArcL.localLabels.Intersect(pRemovedLabels).Any()) ||

                        /* adding labels is problematic if the other rule was recognized under
                         * the condition of containsAllLocalLabels. */
                        ((qArcL.containsAllLocalLabels) && (pAddedLabels.Any())) ||

                        /* adding labels is also problematic if you add a label that negates the
                         * other rule. */
                        (pAddedLabels.Intersect(qArcL.negateLabels).Any()) ||

                        /* if one rule uses strictDegreeMatch, we need to make sure the other rule
                         * doesn't change the degree. */

                        /* if one rule requires that an arc be dangling for correct recognition (nullMeansNull)
                         * then we check to make sure that the other rule doesn't add a node to it. */
                        ((qArcL.nullMeansNull) &&
                         (((qArcL.From == null) && (pArcR.From != null)) ||
                          ((qArcL.To == null) && (pArcR.To != null)))) ||

                        /* well, even if the dangling isn't required, we still need to ensure that p
                         * doesn't put a node on an empty end that q is expecting to belong
                         * to some other node. */
                        ((pArcL.From == null) && (pArcR.From != null) && (qArcL.From != null)) ||
                        /* check the To direction as well */
                        ((pArcL.To == null) && (pArcR.To != null) && (qArcL.To != null)) ||

                        /* in q, the rule is not matching with a dangling arc, but we need to ensure that
                         * the rule doesn't remove or re-connect the arc to something else in the K of the rule
                         * such that the recogniation of the second rule is invalidated. q may be a tad strong
                         * (or conservative) as there could still be confluence despite the change in connectivity.
                         * How? I have yet to imagine. But clearly the assumption here is that change in
                         * connectivity prevent confluence. */
                        ((pArcL.From != null) &&
                         (pNodesKNames != null && pNodesKNames.Contains(pArcL.From.name)) &&
                         ((pArcR.From == null) || (pArcL.From.name != pArcR.From.name))) ||
                        ((pArcL.To != null) &&
                         (pNodesKNames != null && pNodesKNames.Contains(pArcL.To.name)) &&
                         ((pArcR.To == null) || (pArcL.To.name != pArcR.To.name))) ||

                        /* Changes in Arc Direction
                         *
                         * /* finally we check that the changes in arc directionality (e.g. making
                         * directed, making doubly-directed, making undirected) do not affect
                         * the other rule. */
                        /* Here, the directionIsEqual restriction is easier to check than the
                         * default case where directed match with doubly-directed and undirected
                         * match with directed. */
                        ((qArcL.directionIsEqual) &&
                         ((!qArcL.directed.Equals(pArcR.directed)) ||
                          (!qArcL.doublyDirected.Equals(pArcR.doublyDirected)))) ||
                        ((qArcL.directed && !pArcR.directed) ||
                         (qArcL.doublyDirected && !pArcR.doublyDirected))
                        )
                    {
                        return(1);
                    }
                }
                #endregion
            }
            #endregion

            #region HyperArcs
            /* Onto hyperarcs! q is similiar to nodes - more so than arcs. */
            var commonHyperArcs = q.hyperarcs.Intersect(p.hyperarcs);
            if (commonHyperArcs.Any())
            {
                #region  Check whether there are hyperarcs that p will delete that q.option depends upon.

                var pHyperArcsLNames = from n in p.rule.L.hyperarcs
                                       where ((ruleHyperarc)n).MustExist
                                       select n.name;
                var pHyperArcsRNames = from n in p.rule.R.hyperarcs select n.name;
                var pHyperArcsKNames = new List <string>(pHyperArcsRNames.Intersect(pHyperArcsLNames));
                var pKHyperarcs      = new hyperarc[pHyperArcsKNames.Count()];

                for (var i = 0; i < p.rule.L.hyperarcs.Count; i++)
                {
                    if (pHyperArcsKNames.Contains(p.rule.L.hyperarcs[i].name))
                    {
                        pKHyperarcs[pHyperArcsKNames.IndexOf(p.rule.L.hyperarcs[i].name)] = p.hyperarcs[i];
                    }
                    else if (commonHyperArcs.Contains(p.hyperarcs[i]))
                    {
                        return(1);
                    }
                }
                #endregion

                #region HyperArcsModified
                foreach (var commonHyperArc in commonHyperArcs)
                {
                    var qHyperArcL = (ruleHyperarc)q.rule.L.hyperarcs[q.hyperarcs.IndexOf(commonHyperArc)];
                    var pHyperArcL = (ruleHyperarc)p.rule.L.hyperarcs[p.hyperarcs.IndexOf(commonHyperArc)];
                    var pHyperArcR = (ruleHyperarc)p.rule.R[pHyperArcL.name];
                    pIntersectLabels = pHyperArcL.localLabels.Intersect(pHyperArcR.localLabels);
                    pRemovedLabels   = new List <string>(pHyperArcL.localLabels);
                    pRemovedLabels.RemoveAll(s => pIntersectLabels.Contains(s));
                    pAddedLabels = new List <string>(pHyperArcR.localLabels);
                    pAddedLabels.RemoveAll(s => pIntersectLabels.Contains(s));

                    if ( /* first check that there are no labels deleted that the other depends on*/
                        (qHyperArcL.localLabels.Intersect(pRemovedLabels).Any()) ||

                        /* adding labels is problematic if the other rule was recognized under
                         * the condition of containsAllLocalLabels. */
                        ((qHyperArcL.containsAllLocalLabels) && (pAddedLabels.Any())) ||

                        /* adding labels is also problematic if you add a label that negates the
                         * other rule. */
                        (pAddedLabels.Intersect(qHyperArcL.negateLabels).Any()) ||

                        /* if one rule uses strictDegreeMatch, we need to make sure the other rule
                         * doesn't change the degree. */
                        (qHyperArcL.strictNodeCountMatch && (pHyperArcL.degree != pHyperArcR.degree)))
                    {
                        /* actually, the degree can also change from free-arc embedding rules. These
                         * are checked below. */
                        return(1);
                    }
                }
                #endregion
            }
            #endregion

            #region now we're left with some tricky checks...
            if (commonNodes.Any())
            {
                #region if q is induced

                /* if q is induced then p will invalidate it, if it adds arcs between the
                 * common nodes. */
                if (q.rule.induced)
                {
                    var pArcsLNames = from a in p.rule.L.arcs select a.name;
                    if ((from newArc in p.rule.R.arcs.Where(a => !pArcsLNames.Contains(a.name))
                         where newArc.To != null && newArc.From != null
                         let toName = newArc.To.name
                                      let fromName = newArc.To.name
                                                     where pNodesKNames.Contains(toName) && pNodesKNames.Contains(fromName)
                                                     where commonNodes.Contains(pKNodes[Array.IndexOf(pNodesKNames, toName)]) &&
                                                     commonNodes.Contains(pKNodes[Array.IndexOf(pNodesKNames, fromName)])
                                                     select toName).Any())
                    {
                        return(1);
                    }

                    /* is there another situation in which an embedding rule in p may work against
                     * q being an induced rule? It doesn't seem like it would seem embedding rules
                     * reattach free-arcs. oh, what about arc duplication in embedding rules? nah. */
                }

                #endregion

                #region shape restrictions
                for (int i = 0; i < Num_pKNodes; i++)
                {
                    var pNode = pKNodes[i];
                    if (commonNodes.Contains(pNode))
                    {
                        continue;
                    }
                    var pname = pNodesKNames[i];
                    var lNode = (node)p.rule.L[pname];
                    var rNode = (node)p.rule.R[pname];

                    if (q.rule.UseShapeRestrictions && p.rule.TransformNodePositions &&
                        !(MatrixMath.sameCloseZero(lNode.X, rNode.X) &&
                          MatrixMath.sameCloseZero(lNode.Y, rNode.Y) &&
                          MatrixMath.sameCloseZero(lNode.Z, rNode.Z)))
                    {
                        return(1);
                    }
                    if ((q.rule.RestrictToNodeShapeMatch && p.rule.TransformNodeShapes && lNode.DisplayShape != null &&
                         rNode.DisplayShape != null) &&
                        !(MatrixMath.sameCloseZero(lNode.DisplayShape.Height, rNode.DisplayShape.Height) &&
                          MatrixMath.sameCloseZero(lNode.DisplayShape.Width, rNode.DisplayShape.Width) &&
                          MatrixMath.sameCloseZero(p.positionTransform[0, 0], 1) &&
                          MatrixMath.sameCloseZero(p.positionTransform[1, 1], 1) &&
                          MatrixMath.sameCloseZero(p.positionTransform[1, 0]) &&
                          MatrixMath.sameCloseZero(p.positionTransform[0, 1])))
                    {
                        return(1);
                    }
                }
                #endregion
            }

            /* you've run the gauntlet of easy checks, now need to check
             * (1) if there is something caught by additional recognition functions,
             * or (2) NOTExist elements now exist. These can only be solving by an empirical
             * test, which will be expensive.
             * So, now we switch from conditions that return true (or a 1; p does invalidate q) to conditions that return false.
             */

            if (q.rule.ContainsNegativeElements || q.rule.recognizeFuncs.Any() || p.rule.applyFuncs.Any())
            {
                if (confluenceAnalysis == ConfluenceAnalysis.Full)
                {
                    return(fullInvalidationCheck(p, q, cand));
                }
                else
                {
                    return(0);   //return 0 is like "I don't know"
                }
            }
            return(-1);   //like false, there is no invalidating - in otherwords confluence (maybe)!

            #endregion
        }
Esempio n. 11
0
 /// <summary>
 /// Finds the L mapped hyperarc.
 /// </summary>
 /// <param name="x">The x.</param>
 /// <returns></returns>
 public hyperarc findLMappedHyperarc(hyperarc x)
 {
     return(hyperarcs[rule.L.hyperarcs.IndexOf(x)]);
 }
Esempio n. 12
0
        /* if allowArcDuplication is true then for each rule that matches with the arc the arc will be
         * duplicated. */

        #endregion

        #region Methods

        internal static Boolean hyperArcIsFree(hyperarc dangleHyperArc, designGraph host, out List <node> neighborNodes)
        {
            neighborNodes = dangleHyperArc.nodes.Where(n => !host.nodes.Contains(n)).ToList();
            return(neighborNodes.Count > 0);
        }
Esempio n. 13
0
 /// <summary>
 ///   Copies this.arc into the argument copyOfArc.
 /// </summary>
 /// <param name = "copyOfArc">The copy of arc.</param>
 public virtual void copy(hyperarc copyOfArc)
 {
     base.copy(copyOfArc);
 }
Esempio n. 14
0
        private void checkForNegativeHyperArc(option location, ruleHyperarc LHyperArc, hyperarc hostHyperArc)
        {
            if (!hyperArcMatches(LHyperArc, hostHyperArc))
            {
                return;
            }
            location.hyperarcs[L.hyperarcs.IndexOf(LHyperArc)] = hostHyperArc;
            var newLNode = (ruleNode)LHyperArc.nodes.FirstOrDefault(n => (location.findLMappedNode(n) == null));

            if (newLNode == null)
            {
                findNegativeStartElement(location);
            }
            else
            {
                foreach (var n in hostHyperArc.nodes.Where(n => !location.nodes.Contains(n)))
                {
                    checkForNegativeNode(location.copy(), newLNode, n);
                    if ((bool)AllNegativeElementsFound)
                    {
                        return;                                 /* another sub-branch found a match to the negative elements.
                                                                 * There's no point in finding more than one, so this statement
                                                                 * aborts the search down this branch. */
                    }
                }
            }
        }
Esempio n. 15
0
        private void checkHyperArcAvoidNegatives(option location, ruleHyperarc LHyperArc, hyperarc hostHyperArc)
        {
            if (!hyperArcMatches(LHyperArc, hostHyperArc) && !hyperArcMatchRelaxed(LHyperArc, hostHyperArc, location))
            {
                return;
            }
            location.hyperarcs[L.hyperarcs.IndexOf(LHyperArc)] = hostHyperArc;
            var newLNode = (ruleNode)LHyperArc.nodes.FirstOrDefault(n => ((ruleNode)n).MustExist &&
                                                                    (location.findLMappedNode(n) == null));

            if (newLNode == null)
            {
                FindPositiveStartElementAvoidNegatives(location);
            }
            else
            {
                foreach (var n in hostHyperArc.nodes.Where(n => !location.nodes.Contains(n)))
                {
                    checkNodeAvoidNegatives(location.copy(), newLNode, n);
                }
            }
        }