ShouldCalculateGraphMetrics() public method

public ShouldCalculateGraphMetrics ( GraphMetrics graphMetrics ) : System.Boolean
graphMetrics GraphMetrics
return System.Boolean
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            List <GraphMetricColumn> oGraphMetricColumns =
                new List <GraphMetricColumn>();

            Boolean bCancelled = false;

            if (calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    this.GraphMetricToCalculate))
            {
                bCancelled = !TryCalculateGraphMetricsInternal(graph,
                                                               calculateGraphMetricsContext, oGraphMetricColumns);
            }

            graphMetricColumns = oGraphMetricColumns.ToArray();

            return(!bCancelled);
        }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            // Attempt to retrieve the group information the WorkbookReader object
            // may have stored as metadata on the graph.

            GroupInfo [] aoGroups;

            if (
                !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    GraphMetrics.GroupMetrics)
                ||
                !GroupUtil.TryGetGroups(graph, out aoGroups)
                )
            {
                return(true);
            }

            // Count the edges using the IntergroupEdgeCalculator class in the
            // Algorithms namespace, which knows nothing about Excel.

            IList <IntergroupEdgeInfo> oIntergroupEdges;

            if (!(new Algorithms.IntergroupEdgeCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker, aoGroups, true,
                                         out oIntergroupEdges))
            {
                // The user cancelled.

                return(false);
            }

            // Add a row to the group-edge table for each pair of groups that has
            // edges.

            graphMetricColumns = CreateGraphMetricColumns(
                AddRows(aoGroups, oIntergroupEdges));

            return(true);
        }
Example #3
0
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (
                graph.Directedness != GraphDirectedness.Directed
                ||
                !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    GraphMetrics.ReciprocatedVertexPairRatio)
                )
            {
                return(true);
            }

            // Calculate the metric for each vertex using the
            // ReciprocatedVertexPairRatioCalculator class in the Algorithms
            // namespace, which knows nothing about Excel.

            Dictionary <Int32, Nullable <Double> > oReciprocatedVertexPairRatios;

            if (!(new Algorithms.ReciprocatedVertexPairRatioCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oReciprocatedVertexPairRatios))
            {
                // The user cancelled.

                return(false);
            }

            graphMetricColumns = new GraphMetricColumn [] {
                CreateGraphMetricColumn(graph, oReciprocatedVertexPairRatios)
            };

            return(true);
        }
Example #4
0
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (
                graph.Directedness != GraphDirectedness.Directed
                ||
                !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    GraphMetrics.EdgeReciprocation)
                )
            {
                return(true);
            }

            // Calculate the reciprocated flag for each edge using the
            // EdgeReciprocationCalculator class in the Algorithms namespace, which
            // knows nothing about Excel.

            Dictionary <Int32, Boolean> oReciprocatedFlags;

            if (!(new Algorithms.EdgeReciprocationCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oReciprocatedFlags))
            {
                // The user cancelled.

                return(false);
            }

            // Transfer the flags to an array of GraphMetricValue objects.

            List <GraphMetricValueWithID> oReciprocatedValues =
                new List <GraphMetricValueWithID>();

            BooleanConverter oBooleanConverter = new BooleanConverter();

            foreach (IEdge oEdge in graph.Edges)
            {
                // Try to get the row ID stored in the worksheet.

                Int32 iRowID;

                if (TryGetRowID(oEdge, out iRowID))
                {
                    oReciprocatedValues.Add(
                        new GraphMetricValueWithID(iRowID,
                                                   oBooleanConverter.GraphToWorkbook(
                                                       oReciprocatedFlags[oEdge.ID])
                                                   ));
                }
            }

            graphMetricColumns = new GraphMetricColumn [] {
                new GraphMetricColumnWithID(WorksheetNames.Edges,
                                            TableNames.Edges,
                                            EdgeTableColumnNames.IsReciprocated,
                                            ExcelTableUtil.AutoColumnWidth, null,
                                            CellStyleNames.GraphMetricGood,
                                            oReciprocatedValues.ToArray()
                                            ),
            };

            return(true);
        }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        if ( !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
            GraphMetrics.BrandesFastCentralities) )
        {
            return (true);
        }

        // Calculate the centralities for each vertex using the
        // BrandesFastCalculator class in the Algorithms namespace, which knows
        // nothing about Excel.

        Dictionary<Int32, Algorithms.BrandesVertexCentralities>
            oVertexCentralitiesDictionary;

        if ( !( new Algorithms.BrandesFastCentralityCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oVertexCentralitiesDictionary) )
        {
            // The user cancelled.

            return (false);
        }

        // Transfer the centralities to arrays of GraphMetricValue objects.

        List<GraphMetricValueWithID> oBetweennessCentralityValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oClosenessCentralityValues =
            new List<GraphMetricValueWithID>();

        foreach (IVertex oVertex in graph.Vertices)
        {
            // Try to get the row ID stored in the worksheet.

            Int32 iRowID;

            if ( TryGetRowID(oVertex, out iRowID) )
            {
                Algorithms.BrandesVertexCentralities oVertexCentralities =
                    oVertexCentralitiesDictionary[oVertex.ID];

                oBetweennessCentralityValues.Add(
                    new GraphMetricValueWithID(iRowID,
                        oVertexCentralities.BetweennessCentrality
                    ) );

                oClosenessCentralityValues.Add(
                    new GraphMetricValueWithID(iRowID,
                        oVertexCentralities.ClosenessCentrality
                    ) );
            }
        }

        graphMetricColumns = new GraphMetricColumn [] {

            new GraphMetricColumnWithID( WorksheetNames.Vertices,
                TableNames.Vertices,
                VertexTableColumnNames.BetweennessCentrality,
                ExcelTableUtil.AutoColumnWidth,
                NumericFormat, CellStyleNames.GraphMetricGood,
                oBetweennessCentralityValues.ToArray()
                ),

            new GraphMetricColumnWithID( WorksheetNames.Vertices,
                TableNames.Vertices,
                VertexTableColumnNames.ClosenessCentrality,
                ExcelTableUtil.AutoColumnWidth,
                NumericFormat, CellStyleNames.GraphMetricGood,
                oClosenessCentralityValues.ToArray()
                )
                };

        return (true);
    }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    GraphMetrics.BrandesFastCentralities))
            {
                return(true);
            }

            // Calculate the centralities for each vertex using the
            // BrandesFastCalculator class in the Algorithms namespace, which knows
            // nothing about Excel.

            Dictionary <Int32, Algorithms.BrandesVertexCentralities>
            oVertexCentralitiesDictionary;

            if (!(new Algorithms.BrandesFastCentralityCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oVertexCentralitiesDictionary))
            {
                // The user cancelled.

                return(false);
            }

            // Transfer the centralities to arrays of GraphMetricValue objects.

            List <GraphMetricValueWithID> oBetweennessCentralityValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oClosenessCentralityValues =
                new List <GraphMetricValueWithID>();

            foreach (IVertex oVertex in graph.Vertices)
            {
                // Try to get the row ID stored in the worksheet.

                Int32 iRowID;

                if (TryGetRowID(oVertex, out iRowID))
                {
                    Algorithms.BrandesVertexCentralities oVertexCentralities =
                        oVertexCentralitiesDictionary[oVertex.ID];

                    oBetweennessCentralityValues.Add(
                        new GraphMetricValueWithID(iRowID,
                                                   oVertexCentralities.BetweennessCentrality
                                                   ));

                    oClosenessCentralityValues.Add(
                        new GraphMetricValueWithID(iRowID,
                                                   oVertexCentralities.ClosenessCentrality
                                                   ));
                }
            }

            graphMetricColumns = new GraphMetricColumn [] {
                new GraphMetricColumnWithID(WorksheetNames.Vertices,
                                            TableNames.Vertices,
                                            VertexTableColumnNames.BetweennessCentrality,
                                            ExcelTableUtil.AutoColumnWidth,
                                            NumericFormat, CellStyleNames.GraphMetricGood,
                                            oBetweennessCentralityValues.ToArray()
                                            ),

                new GraphMetricColumnWithID(WorksheetNames.Vertices,
                                            TableNames.Vertices,
                                            VertexTableColumnNames.ClosenessCentrality,
                                            ExcelTableUtil.AutoColumnWidth,
                                            NumericFormat, CellStyleNames.GraphMetricGood,
                                            oClosenessCentralityValues.ToArray()
                                            )
            };

            return(true);
        }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            // Attempt to retrieve the group information the WorkbookReader object
            // may have stored as metadata on the graph.

            GroupInfo [] aoGroups;

            if (
                !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    GraphMetrics.GroupMetrics)
                ||
                !GroupUtil.TryGetGroups(graph, out aoGroups)
                )
            {
                return(true);
            }

            // The following lists correspond to group worksheet columns.

            List <GraphMetricValueWithID> oVerticesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oUniqueEdgesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oEdgesWithDuplicatesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oTotalEdgesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oSelfLoopsGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID>
            oReciprocatedVertexPairRatioGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oReciprocatedEdgeRatioGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oConnectedComponentsGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID>
            oSingleVertexConnectedComponentsGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID>
            oMaximumConnectedComponentVerticesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID>
            oMaximumConnectedComponentEdgesGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oMaximumGeodesicDistanceGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oAverageGeodesicDistanceGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oGraphDensityGraphMetricValues =
                new List <GraphMetricValueWithID>();

            foreach (ExcelTemplateGroupInfo oExcelTemplateGroupInfo in aoGroups)
            {
                ICollection <IVertex> oVertices = oExcelTemplateGroupInfo.Vertices;

                if (oVertices == null || oVertices.Count == 0 ||
                    !oExcelTemplateGroupInfo.RowID.HasValue)
                {
                    continue;
                }

                OverallMetrics oOverallMetrics;

                if (!TryCalculateGraphMetricsForOneGroup(oExcelTemplateGroupInfo,
                                                         calculateGraphMetricsContext, out oOverallMetrics))
                {
                    // The user cancelled.

                    return(false);
                }

                Int32 iRowID = oExcelTemplateGroupInfo.RowID.Value;

                oVerticesGraphMetricValues.Add(new GraphMetricValueWithID(
                                                   iRowID, oOverallMetrics.Vertices));

                oUniqueEdgesGraphMetricValues.Add(new GraphMetricValueWithID(
                                                      iRowID, oOverallMetrics.UniqueEdges));

                oEdgesWithDuplicatesGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.EdgesWithDuplicates));

                oTotalEdgesGraphMetricValues.Add(new GraphMetricValueWithID(iRowID,
                                                                            oOverallMetrics.TotalEdges));

                oSelfLoopsGraphMetricValues.Add(new GraphMetricValueWithID(iRowID,
                                                                           oOverallMetrics.SelfLoops));

                oReciprocatedVertexPairRatioGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Double>(
                                                   oOverallMetrics.ReciprocatedVertexPairRatio)));

                oReciprocatedEdgeRatioGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Double>(
                                                   oOverallMetrics.ReciprocatedEdgeRatio)));

                oConnectedComponentsGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.ConnectedComponents));

                oSingleVertexConnectedComponentsGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.SingleVertexConnectedComponents));

                oMaximumConnectedComponentVerticesGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.MaximumConnectedComponentVertices));

                oMaximumConnectedComponentEdgesGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               oOverallMetrics.MaximumConnectedComponentEdges));

                oMaximumGeodesicDistanceGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Int32>(
                                                   oOverallMetrics.MaximumGeodesicDistance)));

                oAverageGeodesicDistanceGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Double>(
                                                   oOverallMetrics.AverageGeodesicDistance)));

                oGraphDensityGraphMetricValues.Add(
                    new GraphMetricValueWithID(iRowID,
                                               OverallMetricCalculator2.NullableToGraphMetricValue <Double>(
                                                   oOverallMetrics.GraphDensity)));
            }

            graphMetricColumns = new GraphMetricColumn [] {
                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.Vertices,
                    Int32NumericFormat,
                    oVerticesGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.UniqueEdges,
                    Int32NumericFormat,
                    oUniqueEdgesGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.EdgesWithDuplicates,
                    Int32NumericFormat,
                    oEdgesWithDuplicatesGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.TotalEdges,
                    Int32NumericFormat,
                    oTotalEdgesGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.SelfLoops,
                    Int32NumericFormat,
                    oSelfLoopsGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.ReciprocatedVertexPairRatio,
                    DoubleNumericFormat,
                    oReciprocatedVertexPairRatioGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.ReciprocatedEdgeRatio,
                    DoubleNumericFormat,
                    oReciprocatedEdgeRatioGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.ConnectedComponents,
                    Int32NumericFormat, oConnectedComponentsGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.SingleVertexConnectedComponents,
                    Int32NumericFormat,
                    oSingleVertexConnectedComponentsGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.MaximumConnectedComponentVertices,
                    Int32NumericFormat,
                    oMaximumConnectedComponentVerticesGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.MaximumConnectedComponentEdges,
                    Int32NumericFormat,
                    oMaximumConnectedComponentEdgesGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.MaximumGeodesicDistance,
                    Int32NumericFormat,
                    oMaximumGeodesicDistanceGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.AverageGeodesicDistance,
                    DoubleNumericFormat,
                    oAverageGeodesicDistanceGraphMetricValues),

                CreateGraphMetricColumnWithID(
                    GroupTableColumnNames.GraphDensity,
                    DoubleNumericFormat,
                    oGraphDensityGraphMetricValues),
            };

            return(true);
        }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        // Note regarding cell styles:
        //
        // Versions of NodeXL earlier than 1.0.1.130 didn't merge duplicate
        // edges before calculating graph metrics, and as a result some metrics
        // were invalid.  That was indicated by applying the CellStyleNames.Bad
        // Excel style to the invalid cells.  Starting in version 1.0.1.130
        // there are no longer invalid metrics, but the
        // CellStyleNames.GraphMetricGood style is always applied to those old
        // potentially bad metric cells (instead of null, which uses the
        // current cell style) in case graph metrics are calculated on an old
        // workbook that had bad metric cells.  If null were used, the old Bad
        // style would always remain on the previously bad cells, even if they
        // are now filled with good metric values.

        graphMetricColumns = new GraphMetricColumn[0];

        if ( !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
            GraphMetrics.OverallMetrics) )
        {
            return (true);
        }

        // Calculate the overall metrics using the OverallMetricCalculator
        // class in the Algorithms namespace, which knows nothing about Excel.

        OverallMetrics oOverallMetrics;

        if ( !( new Algorithms.OverallMetricCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oOverallMetrics) )
        {
            // The user cancelled.

            return (false);
        }

        OverallMetricRows oOverallMetricRows = new OverallMetricRows();


        //*********************************
        // Graph type
        //*********************************

        AddRow(OverallMetricNames.Directedness,
            oOverallMetrics.Directedness.ToString(), oOverallMetricRows);


        //*********************************
        // Vertex count
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.Vertices, oOverallMetrics.Vertices,
            oOverallMetricRows);


        //*********************************
        // Edge counts
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.UniqueEdges, oOverallMetrics.UniqueEdges,
            oOverallMetricRows);

        AddRow(OverallMetricNames.EdgesWithDuplicates,
            oOverallMetrics.EdgesWithDuplicates, oOverallMetricRows);

        AddRow(OverallMetricNames.TotalEdges, oOverallMetrics.TotalEdges,
            oOverallMetricRows);


        //*********************************
        // Self-loops
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.SelfLoops, oOverallMetrics.SelfLoops,
            oOverallMetricRows);


        //*********************************
        // Reciprocation ratios
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.ReciprocatedVertexPairRatio,

            NullableToGraphMetricValue<Double>(
                oOverallMetrics.ReciprocatedVertexPairRatio),

            oOverallMetricRows);

        AddRow(OverallMetricNames.ReciprocatedEdgeRatio,

            NullableToGraphMetricValue<Double>(
                oOverallMetrics.ReciprocatedEdgeRatio),

            oOverallMetricRows);


        //*********************************
        // Connected component counts
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.ConnectedComponents,
            oOverallMetrics.ConnectedComponents, oOverallMetricRows);

        AddRow(OverallMetricNames.SingleVertexConnectedComponents,
            oOverallMetrics.SingleVertexConnectedComponents,
            oOverallMetricRows);

        AddRow(OverallMetricNames.MaximumConnectedComponentVertices,
            oOverallMetrics.MaximumConnectedComponentVertices,
            oOverallMetricRows);

        AddRow(OverallMetricNames.MaximumConnectedComponentEdges,
            oOverallMetrics.MaximumConnectedComponentEdges,
            oOverallMetricRows);


        //*********************************
        // Geodesic distances
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.MaximumGeodesicDistance,

            NullableToGraphMetricValue<Int32>(
                oOverallMetrics.MaximumGeodesicDistance),

            oOverallMetricRows);

        AddRow(OverallMetricNames.AverageGeodesicDistance,

            NullableToGraphMetricValue<Double>(
                oOverallMetrics.AverageGeodesicDistance),

            oOverallMetricRows);


        //*********************************
        // Graph density
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.GraphDensity,
            NullableToGraphMetricValue<Double>(oOverallMetrics.GraphDensity),
            oOverallMetricRows);

        AddRow(OverallMetricNames.Modularity,
            NullableToGraphMetricValue<Double>(oOverallMetrics.Modularity),
            oOverallMetricRows);


        //*********************************
        // NodeXL version
        //*********************************

        AddRow(oOverallMetricRows);

        AddRow(OverallMetricNames.NodeXLVersion,
            AssemblyUtil2.GetFileVersion(), oOverallMetricRows);


        graphMetricColumns = new GraphMetricColumn[] {

            CreateGraphMetricColumnOrdered(
                OverallMetricsTableColumnNames.Name,
                oOverallMetricRows.MetricNames),

            CreateGraphMetricColumnOrdered(
                OverallMetricsTableColumnNames.Value,
                oOverallMetricRows.MetricValues),
            };

        return (true);
    }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            // Note regarding cell styles:
            //
            // Versions of NodeXL earlier than 1.0.1.130 didn't merge duplicate
            // edges before calculating graph metrics, and as a result some metrics
            // were invalid.  That was indicated by applying the CellStyleNames.Bad
            // Excel style to the invalid cells.  Starting in version 1.0.1.130
            // there are no longer invalid metrics, but the
            // CellStyleNames.GraphMetricGood style is always applied to those old
            // potentially bad metric cells (instead of null, which uses the
            // current cell style) in case graph metrics are calculated on an old
            // workbook that had bad metric cells.  If null were used, the old Bad
            // style would always remain on the previously bad cells, even if they
            // are now filled with good metric values.

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    GraphMetrics.OverallMetrics))
            {
                return(true);
            }

            // Calculate the overall metrics using the OverallMetricCalculator
            // class in the Algorithms namespace, which knows nothing about Excel.

            OverallMetrics oOverallMetrics;

            if (!(new Algorithms.OverallMetricCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oOverallMetrics))
            {
                // The user cancelled.

                return(false);
            }

            OverallMetricRows oOverallMetricRows = new OverallMetricRows();


            //*********************************
            // Graph type
            //*********************************

            AddRow(OverallMetricNames.Directedness,
                   oOverallMetrics.Directedness.ToString(), oOverallMetricRows);


            //*********************************
            // Vertex count
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.Vertices, oOverallMetrics.Vertices,
                   oOverallMetricRows);


            //*********************************
            // Edge counts
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.UniqueEdges, oOverallMetrics.UniqueEdges,
                   oOverallMetricRows);

            AddRow(OverallMetricNames.EdgesWithDuplicates,
                   oOverallMetrics.EdgesWithDuplicates, oOverallMetricRows);

            AddRow(OverallMetricNames.TotalEdges, oOverallMetrics.TotalEdges,
                   oOverallMetricRows);


            //*********************************
            // Self-loops
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.SelfLoops, oOverallMetrics.SelfLoops,
                   oOverallMetricRows);


            //*********************************
            // Reciprocation ratios
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.ReciprocatedVertexPairRatio,

                   NullableToGraphMetricValue <Double>(
                       oOverallMetrics.ReciprocatedVertexPairRatio),

                   oOverallMetricRows);

            AddRow(OverallMetricNames.ReciprocatedEdgeRatio,

                   NullableToGraphMetricValue <Double>(
                       oOverallMetrics.ReciprocatedEdgeRatio),

                   oOverallMetricRows);


            //*********************************
            // Connected component counts
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.ConnectedComponents,
                   oOverallMetrics.ConnectedComponents, oOverallMetricRows);

            AddRow(OverallMetricNames.SingleVertexConnectedComponents,
                   oOverallMetrics.SingleVertexConnectedComponents,
                   oOverallMetricRows);

            AddRow(OverallMetricNames.MaximumConnectedComponentVertices,
                   oOverallMetrics.MaximumConnectedComponentVertices,
                   oOverallMetricRows);

            AddRow(OverallMetricNames.MaximumConnectedComponentEdges,
                   oOverallMetrics.MaximumConnectedComponentEdges,
                   oOverallMetricRows);


            //*********************************
            // Geodesic distances
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.MaximumGeodesicDistance,

                   NullableToGraphMetricValue <Int32>(
                       oOverallMetrics.MaximumGeodesicDistance),

                   oOverallMetricRows);

            AddRow(OverallMetricNames.AverageGeodesicDistance,

                   NullableToGraphMetricValue <Double>(
                       oOverallMetrics.AverageGeodesicDistance),

                   oOverallMetricRows);


            //*********************************
            // Graph density
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.GraphDensity,
                   NullableToGraphMetricValue <Double>(oOverallMetrics.GraphDensity),
                   oOverallMetricRows);

            AddRow(OverallMetricNames.Modularity,
                   NullableToGraphMetricValue <Double>(oOverallMetrics.Modularity),
                   oOverallMetricRows);


            //*********************************
            // NodeXL version
            //*********************************

            AddRow(oOverallMetricRows);

            AddRow(OverallMetricNames.NodeXLVersion,
                   AssemblyUtil2.GetFileVersion(), oOverallMetricRows);


            graphMetricColumns = new GraphMetricColumn[] {
                CreateGraphMetricColumnOrdered(
                    OverallMetricsTableColumnNames.Name,
                    oOverallMetricRows.MetricNames),

                CreateGraphMetricColumnOrdered(
                    OverallMetricsTableColumnNames.Value,
                    oOverallMetricRows.MetricValues),
            };

            return(true);
        }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,

        Algorithms.OneDoubleGraphMetricCalculatorBase
            oneDoubleGraphMetricCalculator,

        GraphMetrics graphMetric,
        String columnName,
        Double columnWidthChars,
        String style,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        Debug.Assert(oneDoubleGraphMetricCalculator != null);
        Debug.Assert( !String.IsNullOrEmpty(columnName) );

        Debug.Assert(columnWidthChars == ExcelTableUtil.AutoColumnWidth ||
            columnWidthChars > 0);

        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        if ( !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
            graphMetric) )
        {
            return (true);
        }

        // Calculate the graph metrics for each vertex using the
        // OneDoubleGraphMetricCalculatorBase object, which knows nothing about
        // Excel.

        Dictionary<Int32, Double> oGraphMetrics;

        if ( !oneDoubleGraphMetricCalculator.TryCalculateGraphMetrics(graph,
            calculateGraphMetricsContext.BackgroundWorker, out oGraphMetrics) )
        {
            // The user cancelled.

            return (false);
        }

        // Transfer the graph metrics to an array of GraphMetricValue objects.

        List<GraphMetricValueWithID> oGraphMetricValues =
            new List<GraphMetricValueWithID>();

        foreach (IVertex oVertex in graph.Vertices)
        {
            // Try to get the row ID stored in the worksheet.

            Int32 iRowID;

            if ( TryGetRowID(oVertex, out iRowID) )
            {
                oGraphMetricValues.Add( new GraphMetricValueWithID(
                    iRowID, oGraphMetrics[oVertex.ID] ) );
            }
        }

        graphMetricColumns = new GraphMetricColumn [] {
            new GraphMetricColumnWithID( WorksheetNames.Vertices,
                TableNames.Vertices, columnName, columnWidthChars,
                NumericFormat, style, oGraphMetricValues.ToArray()
                ) };

        return (true);
    }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        List<GraphMetricColumn> oGraphMetricColumns =
            new List<GraphMetricColumn>();

        Boolean bCancelled = false;

        if ( calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
            this.GraphMetricToCalculate) )
        {
            bCancelled = !TryCalculateGraphMetricsInternal(graph,
                calculateGraphMetricsContext, oGraphMetricColumns);
        }

        graphMetricColumns = oGraphMetricColumns.ToArray();

        return (!bCancelled);
    }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    GraphMetrics.Degree |
                    GraphMetrics.InDegree |
                    GraphMetrics.OutDegree
                    ))
            {
                return(true);
            }

            IVertexCollection oVertices = graph.Vertices;
            Int32             iVertices = oVertices.Count;

            // The following lists correspond to vertex worksheet columns.

            List <GraphMetricValueWithID> oInDegreeGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oOutDegreeGraphMetricValues =
                new List <GraphMetricValueWithID>();

            List <GraphMetricValueWithID> oDegreeGraphMetricValues =
                new List <GraphMetricValueWithID>();

            // Create a dictionary to keep track of vertex degrees.  The key is the
            // IVertex.ID and the value is a zero-based index into the above lists.

            Dictionary <Int32, Int32> oVertexIDDictionary =
                new Dictionary <Int32, Int32>();

            // Calculate the degrees for each vertex using the
            // VertexDegreeCalculator class in the Algorithms namespace, which
            // knows nothing about Excel.
            //
            // For simplicity, all degree metrics (in-degree, out-degree, and
            // degree) are calculated regardless of whether the graph is directed
            // or undirected.  After all metrics are calculated,
            // FilterGraphMetricColumns() filters out the metrics that don't apply
            // to the graph, based on its directedness.

            Dictionary <Int32, VertexDegrees> oVertexDegreeDictionary;

            if (!(new Algorithms.VertexDegreeCalculator()).
                TryCalculateGraphMetrics(graph,
                                         calculateGraphMetricsContext.BackgroundWorker,
                                         out oVertexDegreeDictionary))
            {
                // The user cancelled.

                return(false);
            }

            Int32 iRowID;

            foreach (IVertex oVertex in oVertices)
            {
                if (!TryGetRowID(oVertex, out iRowID))
                {
                    continue;
                }

                VertexDegrees oVertexDegrees;

                if (!oVertexDegreeDictionary.TryGetValue(oVertex.ID,
                                                         out oVertexDegrees))
                {
                    Debug.Assert(false);
                }

                oInDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                                                   iRowID, oVertexDegrees.InDegree));

                oOutDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                                                    iRowID, oVertexDegrees.OutDegree));

                oDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                                                 iRowID, oVertexDegrees.Degree));

                Debug.Assert(oInDegreeGraphMetricValues.Count ==
                             oOutDegreeGraphMetricValues.Count);

                oVertexIDDictionary.Add(oVertex.ID,
                                        oInDegreeGraphMetricValues.Count - 1);
            }

            // Figure out which columns to add.

            graphMetricColumns = FilterGraphMetricColumns(graph,
                                                          calculateGraphMetricsContext, oInDegreeGraphMetricValues,
                                                          oOutDegreeGraphMetricValues, oDegreeGraphMetricValues);

            return(true);
        }
        FilterGraphMetricColumns
        (
            IGraph oGraph,
            CalculateGraphMetricsContext oCalculateGraphMetricsContext,
            List <GraphMetricValueWithID> oInDegreeGraphMetricValues,
            List <GraphMetricValueWithID> oOutDegreeGraphMetricValues,
            List <GraphMetricValueWithID> oDegreeGraphMetricValues
        )
        {
            AssertValid();

            Debug.Assert(oGraph != null);
            Debug.Assert(oCalculateGraphMetricsContext != null);
            Debug.Assert(oInDegreeGraphMetricValues != null);
            Debug.Assert(oOutDegreeGraphMetricValues != null);
            Debug.Assert(oDegreeGraphMetricValues != null);

            Boolean bGraphIsDirected =
                (oGraph.Directedness == GraphDirectedness.Directed);

            Boolean bCalculateInDegree = bGraphIsDirected &&
                                         oCalculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.InDegree);

            Boolean bCalculateOutDegree = bGraphIsDirected &&
                                          oCalculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.OutDegree);

            Boolean bCalculateDegree = !bGraphIsDirected &&
                                       oCalculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.Degree);

            // Figure out which columns to add.

            List <GraphMetricColumn> oGraphMetricColumns =
                new List <GraphMetricColumn>();

            if (bCalculateInDegree)
            {
                oGraphMetricColumns.Add(new GraphMetricColumnWithID(
                                            WorksheetNames.Vertices, TableNames.Vertices,
                                            VertexTableColumnNames.InDegree,
                                            ExcelTableUtil.AutoColumnWidth, NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oInDegreeGraphMetricValues.ToArray()));
            }

            if (bCalculateOutDegree)
            {
                oGraphMetricColumns.Add(new GraphMetricColumnWithID(
                                            WorksheetNames.Vertices, TableNames.Vertices,
                                            VertexTableColumnNames.OutDegree,
                                            ExcelTableUtil.AutoColumnWidth, NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oOutDegreeGraphMetricValues.ToArray()));
            }

            if (bCalculateDegree)
            {
                oGraphMetricColumns.Add(new GraphMetricColumnWithID(
                                            WorksheetNames.Vertices, TableNames.Vertices,
                                            VertexTableColumnNames.Degree,
                                            ExcelTableUtil.AutoColumnWidth, NumericFormat,
                                            CellStyleNames.GraphMetricGood,
                                            oDegreeGraphMetricValues.ToArray()));
            }

            return(oGraphMetricColumns.ToArray());
        }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        // Attempt to retrieve the group information the WorkbookReader object
        // may have stored as metadata on the graph.

        GroupInfo [] aoGroups;

        if (
            !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.GroupMetrics)
            ||
            !GroupUtil.TryGetGroups(graph, out aoGroups)
            )
        {
            return (true);
        }

        // The following lists correspond to group worksheet columns.

        List<GraphMetricValueWithID> oVerticesGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oUniqueEdgesGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oEdgesWithDuplicatesGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oTotalEdgesGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oSelfLoopsGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID>
            oReciprocatedVertexPairRatioGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oReciprocatedEdgeRatioGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oConnectedComponentsGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID>
            oSingleVertexConnectedComponentsGraphMetricValues =
                new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID>
            oMaximumConnectedComponentVerticesGraphMetricValues =
                new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID>
            oMaximumConnectedComponentEdgesGraphMetricValues =
                new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oMaximumGeodesicDistanceGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oAverageGeodesicDistanceGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oGraphDensityGraphMetricValues =
            new List<GraphMetricValueWithID>();

        foreach (ExcelTemplateGroupInfo oExcelTemplateGroupInfo in aoGroups)
        {
            ICollection<IVertex> oVertices = oExcelTemplateGroupInfo.Vertices;

            if (oVertices == null || oVertices.Count == 0 ||
                !oExcelTemplateGroupInfo.RowID.HasValue)
            {
                continue;
            }

            OverallMetrics oOverallMetrics;

            if ( !TryCalculateGraphMetricsForOneGroup(oExcelTemplateGroupInfo,
                calculateGraphMetricsContext, out oOverallMetrics) )
            {
                // The user cancelled.

                return (false);
            }

            Int32 iRowID = oExcelTemplateGroupInfo.RowID.Value;

            oVerticesGraphMetricValues.Add( new GraphMetricValueWithID(
                iRowID, oOverallMetrics.Vertices) );

            oUniqueEdgesGraphMetricValues.Add( new GraphMetricValueWithID(
                iRowID, oOverallMetrics.UniqueEdges) );

            oEdgesWithDuplicatesGraphMetricValues.Add(
                new GraphMetricValueWithID(iRowID,
                    oOverallMetrics.EdgesWithDuplicates) );

            oTotalEdgesGraphMetricValues.Add(new GraphMetricValueWithID(iRowID,
                oOverallMetrics.TotalEdges) );

            oSelfLoopsGraphMetricValues.Add(new GraphMetricValueWithID(iRowID,
                oOverallMetrics.SelfLoops) );

            oReciprocatedVertexPairRatioGraphMetricValues.Add(
                new GraphMetricValueWithID( iRowID,
                    OverallMetricCalculator2.NullableToGraphMetricValue<Double>(
                        oOverallMetrics.ReciprocatedVertexPairRatio) ) );

            oReciprocatedEdgeRatioGraphMetricValues.Add(
                new GraphMetricValueWithID( iRowID,
                    OverallMetricCalculator2.NullableToGraphMetricValue<Double>(
                        oOverallMetrics.ReciprocatedEdgeRatio) ) );

            oConnectedComponentsGraphMetricValues.Add(
                new GraphMetricValueWithID(iRowID,
                    oOverallMetrics.ConnectedComponents) );

            oSingleVertexConnectedComponentsGraphMetricValues.Add(
                new GraphMetricValueWithID(iRowID,
                    oOverallMetrics.SingleVertexConnectedComponents) );

            oMaximumConnectedComponentVerticesGraphMetricValues.Add(
                new GraphMetricValueWithID(iRowID,
                    oOverallMetrics.MaximumConnectedComponentVertices) );

            oMaximumConnectedComponentEdgesGraphMetricValues.Add(
                new GraphMetricValueWithID(iRowID,
                    oOverallMetrics.MaximumConnectedComponentEdges) );

            oMaximumGeodesicDistanceGraphMetricValues.Add(
                new GraphMetricValueWithID(iRowID,
                    OverallMetricCalculator2.NullableToGraphMetricValue<Int32>(
                        oOverallMetrics.MaximumGeodesicDistance) ) );

            oAverageGeodesicDistanceGraphMetricValues.Add(
                new GraphMetricValueWithID( iRowID,
                    OverallMetricCalculator2.NullableToGraphMetricValue<Double>(
                        oOverallMetrics.AverageGeodesicDistance) ) );

            oGraphDensityGraphMetricValues.Add(
                new GraphMetricValueWithID( iRowID,
                    OverallMetricCalculator2.NullableToGraphMetricValue<Double>(
                        oOverallMetrics.GraphDensity) ) );
        }

        graphMetricColumns = new GraphMetricColumn [] {

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.Vertices,
                Int32NumericFormat,
                oVerticesGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.UniqueEdges,
                Int32NumericFormat,
                oUniqueEdgesGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.EdgesWithDuplicates,
                Int32NumericFormat,
                oEdgesWithDuplicatesGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.TotalEdges,
                Int32NumericFormat,
                oTotalEdgesGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.SelfLoops,
                Int32NumericFormat,
                oSelfLoopsGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.ReciprocatedVertexPairRatio,
                DoubleNumericFormat,
                oReciprocatedVertexPairRatioGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.ReciprocatedEdgeRatio,
                DoubleNumericFormat,
                oReciprocatedEdgeRatioGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.ConnectedComponents,
                Int32NumericFormat, oConnectedComponentsGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.SingleVertexConnectedComponents,
                Int32NumericFormat,
                oSingleVertexConnectedComponentsGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.MaximumConnectedComponentVertices,
                Int32NumericFormat,
                oMaximumConnectedComponentVerticesGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.MaximumConnectedComponentEdges,
                Int32NumericFormat,
                oMaximumConnectedComponentEdgesGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.MaximumGeodesicDistance,
                Int32NumericFormat,
                oMaximumGeodesicDistanceGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.AverageGeodesicDistance,
                DoubleNumericFormat,
                oAverageGeodesicDistanceGraphMetricValues),

            CreateGraphMetricColumnWithID(
                GroupTableColumnNames.GraphDensity,
                DoubleNumericFormat,
                oGraphDensityGraphMetricValues),
            };

        return (true);
    }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        if (
            graph.Directedness != GraphDirectedness.Directed
            ||
            !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.ReciprocatedVertexPairRatio)
            )
        {
            return (true);
        }

        // Calculate the metric for each vertex using the
        // ReciprocatedVertexPairRatioCalculator class in the Algorithms
        // namespace, which knows nothing about Excel.

        Dictionary< Int32, Nullable<Double> > oReciprocatedVertexPairRatios;

        if ( !( new Algorithms.ReciprocatedVertexPairRatioCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oReciprocatedVertexPairRatios) )
        {
            // The user cancelled.

            return (false);
        }

        graphMetricColumns = new GraphMetricColumn [] {
            CreateGraphMetricColumn(graph, oReciprocatedVertexPairRatios)
            };

        return (true);
    }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        // Attempt to retrieve the group information the WorkbookReader object
        // may have stored as metadata on the graph.

        GroupInfo [] aoGroups;

        if (
            !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.GroupMetrics)
            ||
            !GroupUtil.TryGetGroups(graph, out aoGroups)
            )
        {
            return (true);
        }

        // Count the edges using the IntergroupEdgeCalculator class in the
        // Algorithms namespace, which knows nothing about Excel.

        IList<IntergroupEdgeInfo> oIntergroupEdges;

        if ( !(new Algorithms.IntergroupEdgeCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker, aoGroups, true,
                out oIntergroupEdges) )
        {
            // The user cancelled.

            return (false);
        }

        // Add a row to the group-edge table for each pair of groups that has
        // edges.

        graphMetricColumns = CreateGraphMetricColumns(
            AddRows(aoGroups, oIntergroupEdges) );

        return (true);
    }
    FilterGraphMetricColumns
    (
        IGraph oGraph,
        CalculateGraphMetricsContext oCalculateGraphMetricsContext,
        List<GraphMetricValueWithID> oInDegreeGraphMetricValues,
        List<GraphMetricValueWithID> oOutDegreeGraphMetricValues,
        List<GraphMetricValueWithID> oDegreeGraphMetricValues
    )
    {
        AssertValid();

        Debug.Assert(oGraph != null);
        Debug.Assert(oCalculateGraphMetricsContext != null);
        Debug.Assert(oInDegreeGraphMetricValues != null);
        Debug.Assert(oOutDegreeGraphMetricValues != null);
        Debug.Assert(oDegreeGraphMetricValues != null);

        Boolean bGraphIsDirected =
            (oGraph.Directedness == GraphDirectedness.Directed);

        Boolean bCalculateInDegree = bGraphIsDirected &&
            oCalculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.InDegree);

        Boolean bCalculateOutDegree = bGraphIsDirected &&
            oCalculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.OutDegree);

        Boolean bCalculateDegree = !bGraphIsDirected &&
            oCalculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.Degree);

        // Figure out which columns to add.

        List<GraphMetricColumn> oGraphMetricColumns =
            new List<GraphMetricColumn>();

        if (bCalculateInDegree)
        {
            oGraphMetricColumns.Add( new GraphMetricColumnWithID(
                WorksheetNames.Vertices, TableNames.Vertices,
                VertexTableColumnNames.InDegree,
                ExcelTableUtil.AutoColumnWidth, NumericFormat,
                CellStyleNames.GraphMetricGood,
                oInDegreeGraphMetricValues.ToArray() ) );
        }

        if (bCalculateOutDegree)
        {
            oGraphMetricColumns.Add( new GraphMetricColumnWithID(
                WorksheetNames.Vertices, TableNames.Vertices,
                VertexTableColumnNames.OutDegree,
                ExcelTableUtil.AutoColumnWidth, NumericFormat,
                CellStyleNames.GraphMetricGood,
                oOutDegreeGraphMetricValues.ToArray() ) );
        }

        if (bCalculateDegree)
        {
            oGraphMetricColumns.Add( new GraphMetricColumnWithID(
                WorksheetNames.Vertices, TableNames.Vertices,
                VertexTableColumnNames.Degree,
                ExcelTableUtil.AutoColumnWidth, NumericFormat,
                CellStyleNames.GraphMetricGood,
                oDegreeGraphMetricValues.ToArray() ) );
        }

        return ( oGraphMetricColumns.ToArray() );
    }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        WordMetricUserSettings oWordMetricUserSettings =
            calculateGraphMetricsContext.GraphMetricUserSettings
            .WordMetricUserSettings;

        if (
            !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.Words)
            ||
            String.IsNullOrEmpty(oWordMetricUserSettings.TextColumnName)
            )
        {
            return (true);
        }

        String [] asWordsToSkip = StringUtil.SplitOnCommonDelimiters(
            oWordMetricUserSettings.WordsToSkip);

        WordCounter oWordCounter = new WordCounter(asWordsToSkip);
        WordPairCounter oWordPairCounter = new WordPairCounter(asWordsToSkip);

        // The edges or vertices may have unique imported IDs.  If so, this
        // becomes a collection of the IDs.

        HashSet<String> oUniqueImportedIDs = 
            EdgesOrVerticesHaveImportedIDs(graph,
                oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet) ?
            new HashSet<String>() : null;

        if (oWordMetricUserSettings.CountByGroup)
        {
            if (oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet)
            {
                return ( TryCountEdgeTermsByGroup(graph,
                    oWordMetricUserSettings, oWordCounter, oWordPairCounter,
                    oUniqueImportedIDs, out graphMetricColumns) );
            }
            else
            {
                return ( TryCountVertexTermsByGroup(graph,
                    oWordMetricUserSettings, oWordCounter, oWordPairCounter,
                    oUniqueImportedIDs, out graphMetricColumns) );
            }
        }
        else
        {
            return ( TryCountTermsNoGroups(graph,
                oWordMetricUserSettings, oWordCounter, oWordPairCounter,
                oUniqueImportedIDs, out graphMetricColumns) );
        }
    }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        if ( !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.Degree |
                GraphMetrics.InDegree |
                GraphMetrics.OutDegree
            ) )
        {
            return (true);
        }

        IVertexCollection oVertices = graph.Vertices;
        Int32 iVertices = oVertices.Count;

        // The following lists correspond to vertex worksheet columns.

        List<GraphMetricValueWithID> oInDegreeGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oOutDegreeGraphMetricValues =
            new List<GraphMetricValueWithID>();

        List<GraphMetricValueWithID> oDegreeGraphMetricValues =
            new List<GraphMetricValueWithID>();

        // Create a dictionary to keep track of vertex degrees.  The key is the
        // IVertex.ID and the value is a zero-based index into the above lists.

        Dictionary<Int32, Int32> oVertexIDDictionary =
            new Dictionary<Int32, Int32>();

        // Calculate the degrees for each vertex using the
        // VertexDegreeCalculator class in the Algorithms namespace, which
        // knows nothing about Excel.
        //
        // For simplicity, all degree metrics (in-degree, out-degree, and
        // degree) are calculated regardless of whether the graph is directed
        // or undirected.  After all metrics are calculated,
        // FilterGraphMetricColumns() filters out the metrics that don't apply
        // to the graph, based on its directedness.

        Dictionary<Int32, VertexDegrees> oVertexDegreeDictionary;

        if ( !( new Algorithms.VertexDegreeCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oVertexDegreeDictionary) )
        {
            // The user cancelled.

            return (false);
        }

        Int32 iRowID;

        foreach (IVertex oVertex in oVertices)
        {
            if ( !TryGetRowID(oVertex, out iRowID) )
            {
                continue;
            }

            VertexDegrees oVertexDegrees;

            if ( !oVertexDegreeDictionary.TryGetValue(oVertex.ID,
                out oVertexDegrees) )
            {
                Debug.Assert(false);
            }

            oInDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                iRowID, oVertexDegrees.InDegree) );

            oOutDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                iRowID, oVertexDegrees.OutDegree) );

            oDegreeGraphMetricValues.Add(new GraphMetricValueWithID(
                iRowID, oVertexDegrees.Degree) );

            Debug.Assert(oInDegreeGraphMetricValues.Count ==
                oOutDegreeGraphMetricValues.Count);

            oVertexIDDictionary.Add(oVertex.ID,
                oInDegreeGraphMetricValues.Count - 1);
        }

        // Figure out which columns to add.

        graphMetricColumns = FilterGraphMetricColumns(graph,
            calculateGraphMetricsContext, oInDegreeGraphMetricValues,
            oOutDegreeGraphMetricValues, oDegreeGraphMetricValues);

        return (true);
    }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        if (
            graph.Directedness != GraphDirectedness.Directed
            ||
            !calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                GraphMetrics.EdgeReciprocation)
            )
        {
            return (true);
        }

        // Calculate the reciprocated flag for each edge using the
        // EdgeReciprocationCalculator class in the Algorithms namespace, which
        // knows nothing about Excel.

        Dictionary<Int32, Boolean> oReciprocatedFlags;

        if ( !( new Algorithms.EdgeReciprocationCalculator() ).
            TryCalculateGraphMetrics(graph,
                calculateGraphMetricsContext.BackgroundWorker,
                out oReciprocatedFlags) )
        {
            // The user cancelled.

            return (false);
        }

        // Transfer the flags to an array of GraphMetricValue objects.

        List<GraphMetricValueWithID> oReciprocatedValues =
            new List<GraphMetricValueWithID>();

        BooleanConverter oBooleanConverter = new BooleanConverter();

        foreach (IEdge oEdge in graph.Edges)
        {
            // Try to get the row ID stored in the worksheet.

            Int32 iRowID;

            if ( TryGetRowID(oEdge, out iRowID) )
            {
                oReciprocatedValues.Add(
                    new GraphMetricValueWithID(iRowID,
                        oBooleanConverter.GraphToWorkbook(
                        oReciprocatedFlags[oEdge.ID] )
                    ) );
            }
        }

        graphMetricColumns = new GraphMetricColumn [] {

            new GraphMetricColumnWithID( WorksheetNames.Edges,
                TableNames.Edges,
                EdgeTableColumnNames.IsReciprocated,
                ExcelTableUtil.AutoColumnWidth, null,
                CellStyleNames.GraphMetricGood,
                oReciprocatedValues.ToArray()
                ),
                };

        return (true);
    }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,

            Algorithms.OneDoubleGraphMetricCalculatorBase
            oneDoubleGraphMetricCalculator,

            GraphMetrics graphMetric,
            String columnName,
            Double columnWidthChars,
            String style,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            Debug.Assert(oneDoubleGraphMetricCalculator != null);
            Debug.Assert(!String.IsNullOrEmpty(columnName));

            Debug.Assert(columnWidthChars == ExcelTableUtil.AutoColumnWidth ||
                         columnWidthChars > 0);

            AssertValid();

            graphMetricColumns = new GraphMetricColumn[0];

            if (!calculateGraphMetricsContext.ShouldCalculateGraphMetrics(
                    graphMetric))
            {
                return(true);
            }

            // Calculate the graph metrics for each vertex using the
            // OneDoubleGraphMetricCalculatorBase object, which knows nothing about
            // Excel.

            Dictionary <Int32, Double> oGraphMetrics;

            if (!oneDoubleGraphMetricCalculator.TryCalculateGraphMetrics(graph,
                                                                         calculateGraphMetricsContext.BackgroundWorker, out oGraphMetrics))
            {
                // The user cancelled.

                return(false);
            }

            // Transfer the graph metrics to an array of GraphMetricValue objects.

            List <GraphMetricValueWithID> oGraphMetricValues =
                new List <GraphMetricValueWithID>();

            foreach (IVertex oVertex in graph.Vertices)
            {
                // Try to get the row ID stored in the worksheet.

                Int32 iRowID;

                if (TryGetRowID(oVertex, out iRowID))
                {
                    oGraphMetricValues.Add(new GraphMetricValueWithID(
                                               iRowID, oGraphMetrics[oVertex.ID]));
                }
            }

            graphMetricColumns = new GraphMetricColumn [] {
                new GraphMetricColumnWithID(WorksheetNames.Vertices,
                                            TableNames.Vertices, columnName, columnWidthChars,
                                            NumericFormat, style, oGraphMetricValues.ToArray()
                                            )
            };

            return(true);
        }