/// <summary>
        /// Get the <see cref="Edge"/> representing the original edge on the graph.
        /// </summary>
        /// <remarks>
        /// As the core layout algorithm creates temporary edges for example for same-layer edges and edges spanning
        /// multiple layers, we need to lookup the original edge of the graph for example as key in data providers.
        /// </remarks>
        private Edge GetOriginalEdge(Edge edge, ILayoutDataProvider ldp)
        {
            var originalEdge = sameLayerData.GetOriginalEdge(edge.Source) ??
                               (sameLayerData.GetOriginalEdge(edge.Target) ?? edge);
            var edgeData = ldp.GetEdgeData(originalEdge);

            return(edgeData.AssociatedEdge ?? originalEdge);
        }
        /// <summary>
        /// Returns the best suited edge in <paramref name="edges"/> for use as in-flow edge or <see langword="null"/>
        /// if no such edge could be found.
        /// </summary>
        private Edge GetBestFlowEdge(IEnumerable <Edge> edges, ILayoutDataProvider ldp, LayoutGraph graph)
        {
            List <Edge> weakCandidates = new List <Edge>();
            List <Edge> candidates     = new List <Edge>();

            foreach (var edge in edges)
            {
                var originalEdge = GetOriginalEdge(edge, ldp);
                if ((LaneCrossing)edge2LaneCrossing.Get(edge) != LaneCrossing.None ||
                    BpmnLayout.IsBoundaryInterrupting(originalEdge, graph) ||
                    IsSameLayerEdge(originalEdge, ldp) ||
                    edge.SelfLoop)
                {
                    // an edge should not be aligned if:
                    // - it crosses stripe borders
                    // - it is boundary interrupting
                    // - it is a same-layer edge
                    // - it is a self-loop
                    continue;
                }
                if (ldp.GetEdgeData(edge).Reversed ||
                    !BpmnLayout.IsSequenceFlow(originalEdge, graph))
                {
                    // it is only a weak candidate if:
                    // - it is reversed
                    // - it is no sequence flow
                    weakCandidates.Add(edge);
                }
                else
                {
                    candidates.Add(edge);
                }
            }
            if (candidates.Count > 0)
            {
                // if there are several candidates, choose the one that would keep the LaneAlignment
                // of its source and target node consistent
                candidates.Sort((edge1, edge2) => {
                    var ac1 = GetAlignmentConsistency(edge1);
                    var ac2 = GetAlignmentConsistency(edge2);
                    return(ac2 - ac1);
                });
                return(candidates[0]);
            }
            if (weakCandidates.Count > 0)
            {
                return(weakCandidates[(int)Math.Floor(weakCandidates.Count / 2.0)]);
            }
            return(null);
        }