/// <summary>
        /// Given a set of Heaviside enriched nodes, find which of them must not be enriched, in order to avoid the global
        /// stiffness matrix being singular.
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="heavisideNodes">They will not be altered.</param>
        /// <returns></returns>
        public ISet <XNode> FindHeavisideNodesToRemove(ISingleCrack crack, IMesh2D <XNode, XContinuumElement2D> mesh,
                                                       ISet <XNode> heavisideNodes)
        {
            var processedElements = new Dictionary <XContinuumElement2D, Tuple <double, double> >();
            var nodesToRemove     = new HashSet <XNode>();

            foreach (var node in heavisideNodes)
            {
                double nodePositiveArea = 0.0;
                double nodeNegativeArea = 0.0;

                foreach (var element in mesh.FindElementsWithNode(node))
                {
                    bool alreadyProcessed = processedElements.TryGetValue(element, out Tuple <double, double> elementPosNegAreas);
                    if (!alreadyProcessed)
                    {
                        (double elementPosArea, double elementNegArea) = FindSignedAreasOfElement(crack, element);
                        elementPosNegAreas         = new Tuple <double, double>(elementPosArea, elementNegArea);
                        processedElements[element] = elementPosNegAreas;
                    }
                    nodePositiveArea += elementPosNegAreas.Item1;
                    nodeNegativeArea += elementPosNegAreas.Item2;
                }

                if (crack.SignedDistanceOf(node) >= 0.0)
                {
                    double negativeAreaRatio = nodeNegativeArea / (nodePositiveArea + nodeNegativeArea);
                    if (negativeAreaRatio < relativeAreaTolerance)
                    {
                        nodesToRemove.Add(node);
                    }
                }
                else
                {
                    double positiveAreaRatio = nodePositiveArea / (nodePositiveArea + nodeNegativeArea);
                    if (positiveAreaRatio < relativeAreaTolerance)
                    {
                        nodesToRemove.Add(node);
                    }
                }
            }

            return(nodesToRemove);
        }
        private HashSet <TElement> FindElementsWithCommonEdges(TElement element)
        {
            //TODO: At least 2 common nodes works for 1st order elements, but what about 2nd order ones?
            //TODO: Perhaps I should implement a method to get the Edge of an element and work with that one.

            // Find all elements with at least one common node. Also store the common nodes.
            var allNeighbors = new Dictionary <TElement, HashSet <INode> >();

            foreach (TNode node in element.Nodes)
            {
                foreach (TElement neighbor in mesh.FindElementsWithNode(node))
                {
                    bool neighborIsKnown = allNeighbors.TryGetValue(neighbor, out HashSet <INode> commonNodes);
                    if (!neighborIsKnown)
                    {
                        commonNodes = new HashSet <INode>();
                        allNeighbors.Add(neighbor, commonNodes);
                    }
                    commonNodes.Add(node);
                }
            }
            allNeighbors.Remove(element); // The same element may be added by the above procedure.

            // Only keep the elements that have 2 or more common nodes.
            var elementsWithCommonEdge = new HashSet <TElement>();

            foreach (var neighborNodesPair in allNeighbors)
            {
                if (neighborNodesPair.Value.Count > 1)
                {
                    elementsWithCommonEdge.Add(neighborNodesPair.Key);
                }
            }

            return(elementsWithCommonEdge);
        }