/// <summary>
        /// Returns all the directed,connected pairs of shapes in the  page
        /// </summary>
        /// <param name="page"></param>
        /// <param name="flag"></param>
        /// <returns></returns>
        public static IList <ConnectorEdge> GetDirectedEdges(
            IVisio.Page page,
            ConnectorEdgeHandling flag)
        {
            if (page == null)
            {
                throw new System.ArgumentNullException(nameof(page));
            }

            var edges = PathAnalysis.GetDirectedEdgesRaw(page);

            if (flag == ConnectorEdgeHandling.Raw)
            {
                return(edges);
            }

            // At this point we know we need to analyze the connetor arrows to produce the correct results

            var connnector_ids = edges.Select(e => e.Connector.ID).ToList();

            // Get the arrows for each connector
            var src_beginarrow = ShapeSheet.SRCConstants.BeginArrow;
            var src_endarrow   = ShapeSheet.SRCConstants.EndArrow;

            var query          = new ShapeSheet.Query.CellQuery();
            var col_beginarrow = query.AddCell(src_beginarrow, "BeginArrow");
            var col_endarrow   = query.AddCell(src_endarrow, "EndArrow");

            var arrow_table = query.GetResults <int>(page, connnector_ids);

            IList <ConnectorEdge> directed_edges = new List <ConnectorEdge>();

            int connector_index = 0;

            foreach (var e in edges)
            {
                int beginarrow = arrow_table[connector_index][col_beginarrow];
                int endarrow   = arrow_table[connector_index][col_endarrow];

                if ((beginarrow < 1) && (endarrow < 1))
                {
                    // the line has no arrows
                    if (flag == ConnectorEdgeHandling.Arrow_TreatConnectorsWithoutArrowsAsBidirectional)
                    {
                        // in this case treat the connector as pointing in both directions
                        var de1 = new ConnectorEdge(e.Connector, e.To, e.From);
                        var de2 = new ConnectorEdge(e.Connector, e.From, e.To);
                        directed_edges.Add(de1);
                        directed_edges.Add(de2);
                    }
                    else if (flag == ConnectorEdgeHandling.Arrow_ExcludeConnectorsWithoutArrows)
                    {
                        // in this case ignore the connector completely
                    }
                    else
                    {
                        throw new AutomationException("Internal error");
                    }
                }
                else
                {
                    // The connector has either a from-arrow, a to-arrow, or both

                    // handle if it has a from arrow
                    if (beginarrow > 0)
                    {
                        var de = new ConnectorEdge(e.Connector, e.To, e.From);
                        directed_edges.Add(de);
                    }

                    // handle if it has a to arrow
                    if (endarrow > 0)
                    {
                        var de = new ConnectorEdge(e.Connector, e.From, e.To);
                        directed_edges.Add(de);
                    }
                }

                connector_index++;
            }

            return(directed_edges);
        }
        internal static IEnumerable <DirectedEdge <A, object> > GetClosureFromEdges <A, B>(
            IEnumerable <DirectedEdge <A, B> > edges)
        {
            if (edges == null)
            {
                throw new System.ArgumentNullException(nameof(edges));
            }

            var object_to_id = new Dictionary <A, int>();
            var id_to_object = new Dictionary <int, A>();

            foreach (var edge in edges)
            {
                if (!object_to_id.ContainsKey(edge.From))
                {
                    object_to_id[edge.From] = object_to_id.Count;
                }

                if (!object_to_id.ContainsKey(edge.To))
                {
                    object_to_id[edge.To] = object_to_id.Count;
                }
            }

            foreach (var i in object_to_id)
            {
                id_to_object[i.Value] = i.Key;
            }

            var internal_edges = new List <DirectedEdge <int, object> >();

            foreach (var edge in edges)
            {
                int fromid        = object_to_id[edge.From];
                int toid          = object_to_id[edge.To];
                var directed_edge = new DirectedEdge <int, object>(fromid, toid, null);
                internal_edges.Add(directed_edge);
            }

            if (internal_edges.Count == 0)
            {
                yield break;
            }

            int num_vertices = object_to_id.Count;
            var adj_matrix   = new VisioAutomation.Shapes.Connections.BitArray2D(num_vertices, num_vertices);

            foreach (var iedge in internal_edges)
            {
                adj_matrix[iedge.From, iedge.To] = true;
            }

            var warshall_result = adj_matrix.Clone();

            PathAnalysis.PerformWarshall(warshall_result);

            for (int row = 0; row < adj_matrix.Width; row++)
            {
                for (int col = 0; col < adj_matrix.Height; col++)
                {
                    if (warshall_result.Get(row, col) && (row != col))
                    {
                        var de = new DirectedEdge <A, object>(id_to_object[row], id_to_object[col], null);
                        yield return(de);
                    }
                }
            }
        }