Provides access to objects needed for calculating graph metrics.
An instance of this class gets passed to .
Inheritance: Object
        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);
        }
        TryCalculateGraphMetricsForOneGroup
        (
            ExcelTemplateGroupInfo oExcelTemplateGroupInfo,
            CalculateGraphMetricsContext oCalculateGraphMetricsContext,
            out OverallMetrics oOverallMetrics
        )
        {
            Debug.Assert(oExcelTemplateGroupInfo != null);
            Debug.Assert(oExcelTemplateGroupInfo.Vertices != null);
            Debug.Assert(oExcelTemplateGroupInfo.Vertices.Count > 0);
            Debug.Assert(oCalculateGraphMetricsContext != null);
            AssertValid();

            oOverallMetrics = null;

            ICollection <IVertex> oVertices = oExcelTemplateGroupInfo.Vertices;

            // Create a new graph from the vertices in the group and the edges that
            // connect them.

            IGraph oNewGraph = SubgraphCalculator.GetSubgraphAsNewGraph(oVertices);

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

            return((new Algorithms.OverallMetricCalculator()).
                   TryCalculateGraphMetrics(oNewGraph,
                                            oCalculateGraphMetricsContext.BackgroundWorker,
                                            out oOverallMetrics));
        }
Example #3
0
        CreateCountersForWordsAndWordPairs
        (
            String searchTerm,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out WordCounter wordCounter,
            out WordPairCounter wordPairCounter
        )
        {
            Debug.Assert(calculateGraphMetricsContext != null);

            // When counting word pairs, skip words in the user-supplied list.

            String [] asWordsForWordCounterToSkip = StringUtil.SplitOnSpaces(
                calculateGraphMetricsContext.GraphMetricUserSettings
                .WordMetricUserSettings.WordsToSkip);

            wordPairCounter = new WordPairCounter(asWordsForWordCounterToSkip);

            // When counting words, skip words in the user-supplied list, AND the
            // search term, AND "rt" ("reply to").

            List <String> oWordsForWordPairCounterToSkip = new List <String>(
                asWordsForWordCounterToSkip);

            if (!String.IsNullOrEmpty(searchTerm))
            {
                oWordsForWordPairCounterToSkip.AddRange(
                    StringUtil.SplitOnSpaces(searchTerm));
            }

            oWordsForWordPairCounterToSkip.Add("rt");

            wordCounter = new WordCounter(
                oWordsForWordPairCounterToSkip.ToArray());
        }
        TryCalculateWordMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            String statusColumnName,
            out GraphMetricColumn [] wordMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            Debug.Assert(!String.IsNullOrEmpty(statusColumnName));

            // Use the WordMetricCalculator2() class to calculate word metrics for
            // all groups.
            //
            // This is somewhat wasteful, because we don't actually need all the
            // word metrics for all groups.  A future version might refactor common
            // code out of WordMetricCalculator2() that can be called by that class
            // and this one.

            GraphMetricUserSettings oGraphMetricUserSettings =
                calculateGraphMetricsContext.GraphMetricUserSettings;

            WordMetricUserSettings oWordMetricUserSettings =
                oGraphMetricUserSettings.WordMetricUserSettings;

            GraphMetrics eOriginalGraphMetricsToCalculate =
                oGraphMetricUserSettings.GraphMetricsToCalculate;

            Boolean bOriginalTextColumnIsOnEdgeWorksheet =
                oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet;

            String sOriginalTextColumnName =
                oWordMetricUserSettings.TextColumnName;

            Boolean bOriginalCountByGroup = oWordMetricUserSettings.CountByGroup;

            oGraphMetricUserSettings.GraphMetricsToCalculate    = GraphMetrics.Words;
            oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet = true;
            oWordMetricUserSettings.TextColumnName = statusColumnName;
            oWordMetricUserSettings.CountByGroup   = true;

            try
            {
                return((new WordMetricCalculator2()).TryCalculateGraphMetrics(
                           graph, calculateGraphMetricsContext, out wordMetricColumns));
            }
            finally
            {
                oGraphMetricUserSettings.GraphMetricsToCalculate =
                    eOriginalGraphMetricsToCalculate;

                oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet =
                    bOriginalTextColumnIsOnEdgeWorksheet;

                oWordMetricUserSettings.TextColumnName = sOriginalTextColumnName;
                oWordMetricUserSettings.CountByGroup   = bOriginalCountByGroup;
            }
        }
    TryCalculateWordMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        String statusColumnName,
        out GraphMetricColumn [] wordMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        Debug.Assert( !String.IsNullOrEmpty(statusColumnName) );

        // Use the WordMetricCalculator2() class to calculate word metrics for
        // all groups.
        //
        // This is somewhat wasteful, because we don't actually need all the
        // word metrics for all groups.  A future version might refactor common
        // code out of WordMetricCalculator2() that can be called by that class
        // and this one.

        GraphMetricUserSettings oGraphMetricUserSettings =
            calculateGraphMetricsContext.GraphMetricUserSettings;

        WordMetricUserSettings oWordMetricUserSettings =
            oGraphMetricUserSettings.WordMetricUserSettings;

        GraphMetrics eOriginalGraphMetricsToCalculate =
            oGraphMetricUserSettings.GraphMetricsToCalculate;

        Boolean bOriginalTextColumnIsOnEdgeWorksheet =
            oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet;

        String sOriginalTextColumnName =
            oWordMetricUserSettings.TextColumnName;

        Boolean bOriginalCountByGroup = oWordMetricUserSettings.CountByGroup;

        oGraphMetricUserSettings.GraphMetricsToCalculate = GraphMetrics.Words;
        oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet = true;
        oWordMetricUserSettings.TextColumnName = statusColumnName;
        oWordMetricUserSettings.CountByGroup = true;

        try
        {
            return ( ( new WordMetricCalculator2() ).TryCalculateGraphMetrics(
                graph, calculateGraphMetricsContext, out wordMetricColumns) );
        }
        finally
        {
            oGraphMetricUserSettings.GraphMetricsToCalculate =
                eOriginalGraphMetricsToCalculate;

            oWordMetricUserSettings.TextColumnIsOnEdgeWorksheet =
                bOriginalTextColumnIsOnEdgeWorksheet;

            oWordMetricUserSettings.TextColumnName = sOriginalTextColumnName;
            oWordMetricUserSettings.CountByGroup = bOriginalCountByGroup;
        }
    }
        TryCalculateGraphMetricsInternal
        (
            IGraph oGraph,
            CalculateGraphMetricsContext oCalculateGraphMetricsContext,
            List <GraphMetricColumn> oGraphMetricColumns
        )
        {
            Debug.Assert(oGraph != null);
            Debug.Assert(oCalculateGraphMetricsContext != null);
            AssertValid();

            Int32 iTopNByMetricsIndex = 0;

            foreach (TopNByMetricUserSettings oTopNByMetricUserSettings in
                     oCalculateGraphMetricsContext.GraphMetricUserSettings.
                     TopNByMetricsToCalculate)
            {
                if (oCalculateGraphMetricsContext.BackgroundWorker
                    .CancellationPending)
                {
                    // The user cancelled.

                    return(false);
                }

                if (
                    oTopNByMetricUserSettings.WorksheetName !=
                    WorksheetNames.Vertices
                    ||
                    oTopNByMetricUserSettings.TableName != TableNames.Vertices
                    ||
                    oTopNByMetricUserSettings.ItemNameColumnName !=
                    VertexTableColumnNames.VertexName
                    )
                {
                    // This currently works only for the vertex worksheet.
                    // GraphMetricCalculationManager.ReadWorkbook() will have to be
                    // modified to read the cell values in other worksheets when
                    // top-N-by metrics are extended to support other worksheets.

                    throw new NotSupportedException();
                }

                AddColumnsForRankedVertices(oGraph.Vertices,
                                            oTopNByMetricUserSettings.RankedColumnName,
                                            oTopNByMetricUserSettings.N,
                                            WorksheetNames.TopNByMetrics,
                                            TableNames.TopNByMetricsRoot + (iTopNByMetricsIndex + 1),
                                            oTopNByMetricUserSettings.ToString(),
                                            oTopNByMetricUserSettings.RankedColumnName,
                                            oGraphMetricColumns);

                iTopNByMetricsIndex++;
            }

            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);
        }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            return(TryCalculateGraphMetrics(graph, calculateGraphMetricsContext,
                                            new Algorithms.PageRankCalculator(), GraphMetrics.PageRank,
                                            VertexTableColumnNames.PageRank, ExcelTableUtil.AutoColumnWidth,
                                            CellStyleNames.GraphMetricGood, out graphMetricColumns));
        }
Example #9
0
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = null;

            // Partition the graph into motifs using the MotifCalculator class in
            // the Algorithms namespace, which knows nothing about Excel.

            Algorithms.MotifCalculator oMotifCalculator =
                new Algorithms.MotifCalculator();

            ICollection <Motif> oMotifs;

            if (!oMotifCalculator.TryCalculateMotifs(graph,
                                                     m_eMotifsToCalculate, m_iDConnectorMinimumAnchorVertices,
                                                     m_iDConnectorMaximumAnchorVertices,
                                                     m_iCliqueMinimumMemberVertices, m_iCliqueMaximumMemberVertices,
                                                     calculateGraphMetricsContext.BackgroundWorker, out oMotifs))
            {
                // The user cancelled.

                return(false);
            }

            // Convert the collection of motifs to an array of GraphMetricColumn
            // objects.

            graphMetricColumns =
                GroupsToGraphMetricColumnsConverter.Convert <Motif>(
                    oMotifs,
                    (oMotif) => oMotif.VerticesInMotif,
                    (oMotif) => MotifToGroupName(oMotif),
                    true,
                    (oMotif) => oMotif.CollapsedAttributes
                    );

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

            graphMetricColumns = null;

            // Partition the graph into clusters using the ClusterCalculator class
            // in the Algorithms namespace, which knows nothing about Excel.

            ICollection <Community> oCommunities;

            Algorithms.ClusterCalculator oClusterCalculator =
                new Algorithms.ClusterCalculator();

            oClusterCalculator.Algorithm = m_eAlgorithm;

            oClusterCalculator.PutNeighborlessVerticesInOneCluster =
                m_bPutNeighborlessVerticesInOneCluster;

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

                return(false);
            }

            // Convert the collection of communities to an array of
            // GraphMetricColumn objects.

            graphMetricColumns =
                GroupsToGraphMetricColumnsConverter.Convert <Community>(
                    oCommunities,
                    (oCommunity) => oCommunity.Vertices
                    );

            return(true);
        }
Example #11
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 #12
0
        TryCalculateGraphMetrics
        (
            IGraph graph,
            CalculateGraphMetricsContext calculateGraphMetricsContext,
            out GraphMetricColumn [] graphMetricColumns
        )
        {
            Debug.Assert(graph != null);
            Debug.Assert(calculateGraphMetricsContext != null);
            AssertValid();

            graphMetricColumns = null;

            // Partition the graph into strongly connected components using the
            // ConnectedComponentCalculator class in the Algorithms namespace,
            // which knows nothing about Excel.
            //
            // Note that ConnectedComponentCalculator does its work synchronously.

            Algorithms.ConnectedComponentCalculator oConnectedComponentCalculator =
                new Algorithms.ConnectedComponentCalculator();

            IList <LinkedList <IVertex> > oComponents =
                oConnectedComponentCalculator.CalculateStronglyConnectedComponents(
                    graph, false);

            // Convert the collection of components to an array of
            // GraphMetricColumn objects.

            graphMetricColumns =
                GroupsToGraphMetricColumnsConverter.Convert <LinkedList <IVertex> >(
                    oComponents,
                    (oComponent) => oComponent
                    );

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

        graphMetricColumns = null;

        // Partition the graph into strongly connected components using the
        // ConnectedComponentCalculator class in the Algorithms namespace,
        // which knows nothing about Excel.
        //
        // Note that ConnectedComponentCalculator does its work synchronously.

        Algorithms.ConnectedComponentCalculator oConnectedComponentCalculator =
            new Algorithms.ConnectedComponentCalculator();

        IList< LinkedList<IVertex> > oComponents =
            oConnectedComponentCalculator.CalculateStronglyConnectedComponents(
                graph, false);

        // Convert the collection of components to an array of
        // GraphMetricColumn objects.

        graphMetricColumns =
            GroupsToGraphMetricColumnsConverter.Convert< LinkedList<IVertex> >(
                oComponents,
                (oComponent) => oComponent
                );

        return (true);
    }
 TryCalculateGraphMetricsInternal
 (
     IGraph oGraph,
     CalculateGraphMetricsContext oCalculateGraphMetricsContext,
     List <GraphMetricColumn> oGraphMetricColumns
 );
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = null;

        // Partition the graph into clusters using the ClusterCalculator class
        // in the Algorithms namespace, which knows nothing about Excel.

        ICollection<Community> oCommunities;

        Algorithms.ClusterCalculator oClusterCalculator =
            new Algorithms.ClusterCalculator();

        oClusterCalculator.Algorithm = m_eAlgorithm;

        oClusterCalculator.PutNeighborlessVerticesInOneCluster =
            m_bPutNeighborlessVerticesInOneCluster;

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

            return (false);
        }

        // Convert the collection of communities to an array of
        // GraphMetricColumn objects.

        graphMetricColumns =
            GroupsToGraphMetricColumnsConverter.Convert<Community>(
                oCommunities,
                (oCommunity) => oCommunity.Vertices
                );

        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);
        }

        // 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);
    }
    TryCalculateGraphMetricsForOneGroup
    (
        ExcelTemplateGroupInfo oExcelTemplateGroupInfo,
        CalculateGraphMetricsContext oCalculateGraphMetricsContext,
        out OverallMetrics oOverallMetrics
    )
    {
        Debug.Assert(oExcelTemplateGroupInfo != null);
        Debug.Assert(oExcelTemplateGroupInfo.Vertices != null);
        Debug.Assert(oExcelTemplateGroupInfo.Vertices.Count > 0);
        Debug.Assert(oCalculateGraphMetricsContext != null);
        AssertValid();

        oOverallMetrics = null;

        ICollection<IVertex> oVertices = oExcelTemplateGroupInfo.Vertices;

        // Create a new graph from the vertices in the group and the edges that
        // connect them.

        IGraph oNewGraph = SubgraphCalculator.GetSubgraphAsNewGraph(oVertices);

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

        return ( (new Algorithms.OverallMetricCalculator() ).
            TryCalculateGraphMetrics(oNewGraph,
                oCalculateGraphMetricsContext.BackgroundWorker,
                out oOverallMetrics) );
    }
    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();

        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 (
            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,

        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();

        // 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);
    }
Example #25
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);
        }
        CalculateGraphMetricsAsyncInternal
        (
            CalculateGraphMetricsAsyncArgs oCalculateGraphMetricsAsyncArgs,
            BackgroundWorker oBackgroundWorker,
            DoWorkEventArgs oDoWorkEventArgs
        )
        {
            Debug.Assert(oCalculateGraphMetricsAsyncArgs != null);
            Debug.Assert(oBackgroundWorker != null);
            Debug.Assert(oDoWorkEventArgs != null);
            AssertValid();

            IGraph oGraph = oCalculateGraphMetricsAsyncArgs.Graph;

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

            CalculateGraphMetricsContext oCalculateGraphMetricsContext =
                new CalculateGraphMetricsContext(
                    oCalculateGraphMetricsAsyncArgs.GraphMetricUserSettings,
                    oBackgroundWorker);

            Boolean bDuplicateEdgesRemoved = false;

            foreach (IGraphMetricCalculator2 oGraphMetricCalculator in
                     oCalculateGraphMetricsAsyncArgs.SortedGraphMetricCalculators)
            {
                if (!oGraphMetricCalculator.HandlesDuplicateEdges &&
                    !bDuplicateEdgesRemoved)
                {
                    // This and the remainder of the graph metric calculators
                    // cannot handle duplicate edges.  Remove them from the graph.

                    oGraph.Edges.RemoveDuplicates();
                    bDuplicateEdgesRemoved = true;
                }

                // Calculate the implementation's graph metrics.

                GraphMetricColumn [] aoGraphMetricColumns;

                if (!oGraphMetricCalculator.TryCalculateGraphMetrics(oGraph,
                                                                     oCalculateGraphMetricsContext, out aoGraphMetricColumns))
                {
                    // The user cancelled.

                    oDoWorkEventArgs.Cancel = true;

                    oBackgroundWorker.ReportProgress(0,
                                                     new GraphMetricProgress("Cancelled.", false)
                                                     );

                    return;
                }

                // Aggregate the results.

                Debug.Assert(aoGraphMetricColumns != null);

                oAggregatedGraphMetricColumns.AddRange(aoGraphMetricColumns);
            }

            oDoWorkEventArgs.Result = oAggregatedGraphMetricColumns.ToArray();

            oBackgroundWorker.ReportProgress(100,
                                             new GraphMetricProgress(
                                                 "Inserting metrics into the workbook.",
                                                 true)
                                             );

            // Let the dialog the display the final progress report and update its
            // controls.

            System.Threading.Thread.Sleep(1);
        }
    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 (!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);
        }
    CalculateGraphMetricsAsyncInternal
    (
        CalculateGraphMetricsAsyncArgs oCalculateGraphMetricsAsyncArgs,
        BackgroundWorker oBackgroundWorker,
        DoWorkEventArgs oDoWorkEventArgs
    )
    {
        Debug.Assert(oCalculateGraphMetricsAsyncArgs != null);
        Debug.Assert(oBackgroundWorker != null);
        Debug.Assert(oDoWorkEventArgs != null);
        AssertValid();

        IGraph oGraph = oCalculateGraphMetricsAsyncArgs.Graph;

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

        CalculateGraphMetricsContext oCalculateGraphMetricsContext =
            new CalculateGraphMetricsContext(
                oCalculateGraphMetricsAsyncArgs.GraphMetricUserSettings,
                oBackgroundWorker);

        Boolean bDuplicateEdgesRemoved = false;

        foreach (IGraphMetricCalculator2 oGraphMetricCalculator in
            oCalculateGraphMetricsAsyncArgs.SortedGraphMetricCalculators)
        {
            if (!oGraphMetricCalculator.HandlesDuplicateEdges &&
                !bDuplicateEdgesRemoved)
            {
                // This and the remainder of the graph metric calculators
                // cannot handle duplicate edges.  Remove them from the graph.

                oGraph.Edges.RemoveDuplicates();
                bDuplicateEdgesRemoved = true;
            }

            // Calculate the implementation's graph metrics.

            GraphMetricColumn [] aoGraphMetricColumns;

            if ( !oGraphMetricCalculator.TryCalculateGraphMetrics(oGraph,
                oCalculateGraphMetricsContext, out aoGraphMetricColumns) )
            {
                // The user cancelled.

                oDoWorkEventArgs.Cancel = true;

                oBackgroundWorker.ReportProgress(0,
                    new GraphMetricProgress("Cancelled.", false)
                    );

                return;
            }

            // Aggregate the results.

            Debug.Assert(aoGraphMetricColumns != null);

            oAggregatedGraphMetricColumns.AddRange(aoGraphMetricColumns);
        }

        oDoWorkEventArgs.Result = oAggregatedGraphMetricColumns.ToArray();

        oBackgroundWorker.ReportProgress(100,
            new GraphMetricProgress(
                "Inserting metrics into the workbook.",
                true)
            );

        // Let the dialog the display the final progress report and update its
        // controls.

        System.Threading.Thread.Sleep(1);
    }
    TryCalculateGraphMetricsInternal
    (
        IGraph oGraph,
        CalculateGraphMetricsContext oCalculateGraphMetricsContext,
        List<GraphMetricColumn> oGraphMetricColumns
    )
    {
        Debug.Assert(oGraph != null);
        Debug.Assert(oCalculateGraphMetricsContext != null);
        Debug.Assert(oGraphMetricColumns != null);
        AssertValid();

        return (
            TryCalculateTopItemAndGroupMetrics(
                oGraph, oCalculateGraphMetricsContext, oGraphMetricColumns)
            &&
            TryCalculateVertexMetrics(
                oGraph, oCalculateGraphMetricsContext, oGraphMetricColumns)
            );
    }
    CreateCountersForWordsAndWordPairs
    (
        String searchTerm,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out WordCounter wordCounter,
        out WordPairCounter wordPairCounter
    )
    {
        Debug.Assert(calculateGraphMetricsContext != null);

        // When counting word pairs, skip words in the user-supplied list.

        String [] asWordsForWordCounterToSkip = StringUtil.SplitOnSpaces(
            calculateGraphMetricsContext.GraphMetricUserSettings
            .WordMetricUserSettings.WordsToSkip);

        wordPairCounter = new WordPairCounter(asWordsForWordCounterToSkip);

        // When counting words, skip words in the user-supplied list, AND the
        // search term, AND "rt" ("reply to").

        List<String> oWordsForWordPairCounterToSkip = new List<String>(
            asWordsForWordCounterToSkip);

        if ( !String.IsNullOrEmpty(searchTerm) )
        {
            oWordsForWordPairCounterToSkip.AddRange(
                StringUtil.SplitOnSpaces(searchTerm) );
        }

        oWordsForWordPairCounterToSkip.Add("rt");

        wordCounter = new WordCounter(
            oWordsForWordPairCounterToSkip.ToArray() );
    }
    TryCalculateTopItemAndGroupMetrics
    (
        IGraph oGraph,
        CalculateGraphMetricsContext oCalculateGraphMetricsContext,
        List<GraphMetricColumn> oGraphMetricColumns
    )
    {
        Debug.Assert(oGraph != null);
        Debug.Assert(oCalculateGraphMetricsContext != null);
        Debug.Assert(oGraphMetricColumns != null);
        AssertValid();

        if ( !ReportProgressAndCheckCancellationPending(
            oCalculateGraphMetricsContext) )
        {
            return (false);
        }

        // Get information about each of the graph's groups, including a
        // "dummy" group for the entire graph.

        List<GroupEdgeInfo> oAllGroupEdgeInfos =
            GroupEdgeSorter.SortGroupEdges(oGraph, Int32.MaxValue, true,
                false);

        Int32 iTableIndex = 1;

        AddColumnsForStatusContent(ref iTableIndex, oAllGroupEdgeInfos,
            UrlsInTweetColumnName, true,
            GroupTableColumnNames.TopUrlsInTweet, oGraphMetricColumns);

        AddColumnsForStatusContent(ref iTableIndex, oAllGroupEdgeInfos,
            DomainsInTweetColumnName, false,
            GroupTableColumnNames.TopDomainsInTweet, oGraphMetricColumns);

        AddColumnsForStatusContent(ref iTableIndex, oAllGroupEdgeInfos,
            HashtagsInTweetColumnName, false,
            GroupTableColumnNames.TopHashtagsInTweet, oGraphMetricColumns);

        if ( !TryAddColumnsForWordsAndWordPairs(ref iTableIndex, oGraph,
            oAllGroupEdgeInfos, oCalculateGraphMetricsContext,
            oGraphMetricColumns) )
        {
            return (false);
        }

        AddColumnsForRepliesToAndMentions(ref iTableIndex, oAllGroupEdgeInfos,
            oGraphMetricColumns);

        AddColumnsForTweeters(ref iTableIndex, oGraph, oGraphMetricColumns);

        AdjustColumnWidths(oGraphMetricColumns, oAllGroupEdgeInfos);

        return (true);
    }
    TryCalculateGraphMetricsInternal
    (
        IGraph oGraph,
        CalculateGraphMetricsContext oCalculateGraphMetricsContext,
        List<GraphMetricColumn> oGraphMetricColumns
    )
    {
        Debug.Assert(oGraph != null);
        Debug.Assert(oCalculateGraphMetricsContext != null);
        AssertValid();

        Int32 iTopNByMetricsIndex = 0;

        foreach (TopNByMetricUserSettings oTopNByMetricUserSettings in
            oCalculateGraphMetricsContext.GraphMetricUserSettings.
                TopNByMetricsToCalculate)
        {
            if (oCalculateGraphMetricsContext.BackgroundWorker
                .CancellationPending)
            {
                // The user cancelled.

                return (false);
            }

            if (
                oTopNByMetricUserSettings.WorksheetName !=
                    WorksheetNames.Vertices
                ||
                oTopNByMetricUserSettings.TableName != TableNames.Vertices
                ||
                oTopNByMetricUserSettings.ItemNameColumnName !=
                    VertexTableColumnNames.VertexName
                )
            {
                // This currently works only for the vertex worksheet.
                // GraphMetricCalculationManager.ReadWorkbook() will have to be
                // modified to read the cell values in other worksheets when
                // top-N-by metrics are extended to support other worksheets.

                throw new NotSupportedException();
            }

            AddColumnsForRankedVertices(oGraph.Vertices,
                oTopNByMetricUserSettings.RankedColumnName,
                oTopNByMetricUserSettings.N,
                WorksheetNames.TopNByMetrics,
                TableNames.TopNByMetricsRoot + (iTopNByMetricsIndex + 1),
                oTopNByMetricUserSettings.ToString(),
                oTopNByMetricUserSettings.RankedColumnName,
                oGraphMetricColumns);

            iTopNByMetricsIndex++;
        }

        return (true);
    }
    TryAddColumnsForWordsAndWordPairs
    (
        ref Int32 iTableIndex,
        IGraph oGraph,
        List<GroupEdgeInfo> oAllGroupEdgeInfos,
        CalculateGraphMetricsContext oCalculateGraphMetricsContext,
        List<GraphMetricColumn> oGraphMetricColumns
    )
    {
        Debug.Assert(iTableIndex >= 1);
        Debug.Assert(oGraph != null);
        Debug.Assert(oAllGroupEdgeInfos != null);
        Debug.Assert(oCalculateGraphMetricsContext != null);
        Debug.Assert(oGraphMetricColumns != null);
        AssertValid();

        // Calculate all word metrics for all the graph's groups.

        GraphMetricColumn [] oWordMetricColumns;

        if ( !TwitterSearchNetworkWordMetricUtil.TryCalculateWordMetrics(
            oGraph, oCalculateGraphMetricsContext, StatusColumnName,
            out oWordMetricColumns) )
        {
            return (false);
        }

        // Not all of the word metrics are needed, and they are in the wrong
        // format.  Filter and reformat them.

        FilterAndReformatWordMetrics(ref iTableIndex, oWordMetricColumns,
            oAllGroupEdgeInfos, oGraphMetricColumns);

        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);
        }
 TryCalculateGraphMetricsInternal
 (
     IGraph oGraph,
     CalculateGraphMetricsContext oCalculateGraphMetricsContext,
     List<GraphMetricColumn> oGraphMetricColumns
 );
    TryCalculateGraphMetrics
    (
        IGraph graph,
        CalculateGraphMetricsContext calculateGraphMetricsContext,
        out GraphMetricColumn [] graphMetricColumns
    )
    {
        Debug.Assert(graph != null);
        Debug.Assert(calculateGraphMetricsContext != null);
        AssertValid();

        graphMetricColumns = new GraphMetricColumn[0];

        BackgroundWorker oBackgroundWorker =
            calculateGraphMetricsContext.BackgroundWorker;

        ReadabilityMetricUserSettings oReadabilityMetricUserSettings =
            new ReadabilityMetricUserSettings();

        IGraph oGraphClone = CloneAndFilterGraph(graph);

        // The key is an IEdge and the value is the number of edges that cross
        // the edge.

        Dictionary<IEdge, Int32> oEdgeInformation;

        // The key is an IVertex and the value is a VertexInformation object.

        Dictionary<IVertex, VertexInformation> oVertexInformations;

        // Future improvement: Calculate edge and vertex information only when
        // necessary, based on the metrics selected by the user.

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

        if (
            !TryCalculateEdgeInformation(oGraphClone, oBackgroundWorker,
                out oEdgeInformation)
            ||
            !TryCalculateVertexInformations(oGraphClone, oBackgroundWorker,
                out oVertexInformations)
            ||
            !TryCalculatePerEdgeAndPerVertexMetrics(oGraphClone, graph,
                oReadabilityMetricUserSettings, oBackgroundWorker,
                oEdgeInformation, oVertexInformations, oGraphMetricColumns)
            ||
            !TryCalculateOverallMetrics(oGraphClone,
                oReadabilityMetricUserSettings, oBackgroundWorker,
                oEdgeInformation, oVertexInformations, oGraphMetricColumns)
            )
        {
            // The user cancelled.

            return (false);
        }

        graphMetricColumns = oGraphMetricColumns.ToArray();

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

        graphMetricColumns = null;

        // Partition the graph into motifs using the MotifCalculator class in
        // the Algorithms namespace, which knows nothing about Excel.

        Algorithms.MotifCalculator oMotifCalculator =
            new Algorithms.MotifCalculator();

        ICollection<Motif> oMotifs;

        if ( !oMotifCalculator.TryCalculateMotifs(graph,
            m_eMotifsToCalculate, m_iDConnectorMinimumAnchorVertices,
            m_iDConnectorMaximumAnchorVertices,
            m_iCliqueMinimumMemberVertices, m_iCliqueMaximumMemberVertices,
            calculateGraphMetricsContext.BackgroundWorker, out oMotifs) )
        {
            // The user cancelled.

            return (false);
        }

        // Convert the collection of motifs to an array of GraphMetricColumn
        // objects.

        graphMetricColumns =
            GroupsToGraphMetricColumnsConverter.Convert<Motif>(
                oMotifs,
                (oMotif) => oMotif.VerticesInMotif,
                (oMotif) => MotifToGroupName(oMotif),
                true,
                (oMotif) => oMotif.CollapsedAttributes
                );

        return (true);
    }
    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
 );
    ReportProgressAndCheckCancellationPending
    (
        CalculateGraphMetricsContext oCalculateGraphMetricsContext
    )
    {
        Debug.Assert(oCalculateGraphMetricsContext != null);
        AssertValid();

        BackgroundWorker oBackgroundWorker =
            oCalculateGraphMetricsContext.BackgroundWorker;

        if (oBackgroundWorker != null)
        {
            if (oBackgroundWorker.CancellationPending)
            {
                return (false);
            }

            oBackgroundWorker.ReportProgress(50,
                "Calculating Twitter search network top items."
                );
        }

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

        return ( TryCalculateGraphMetrics(graph, calculateGraphMetricsContext,
            new Algorithms.PageRankCalculator(), GraphMetrics.PageRank,
            VertexTableColumnNames.PageRank, ExcelTableUtil.AutoColumnWidth,
            CellStyleNames.GraphMetricGood, out graphMetricColumns) );
    }
    TryCalculateVertexMetrics
    (
        IGraph oGraph,
        CalculateGraphMetricsContext oCalculateGraphMetricsContext,
        List<GraphMetricColumn> oGraphMetricColumns
    )
    {
        Debug.Assert(oGraph != null);
        Debug.Assert(oCalculateGraphMetricsContext != null);
        Debug.Assert(oGraphMetricColumns != null);
        AssertValid();

        if ( !ReportProgressAndCheckCancellationPending(
            oCalculateGraphMetricsContext) )
        {
            return (false);
        }

        // The key is a screen name and the value is a list of zero or more
        // unique edges belonging to that user.

        Dictionary< String, List<IEdge> > oUniqueEdgesByUser =
            TwitterSearchNetworkVertexMetricUtil.GetUniqueEdgesByUser(oGraph);

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

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

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

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

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

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

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

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

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

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

        // For efficiency, these counters are created once only.

        WordCounter oWordCounter;
        WordPairCounter oWordPairCounter;

        TwitterSearchNetworkVertexMetricUtil
            .CreateCountersForWordsAndWordPairs(
                GetSearchTerm(), oCalculateGraphMetricsContext,
                out oWordCounter, out oWordPairCounter);

        foreach (IVertex oVertex in oGraph.Vertices)
        {
            Int32 iRowID;
            List<IEdge> oUniqueEdgesForUser;

            if (
                TryGetRowID(oVertex, out iRowID)
                &&
                !String.IsNullOrEmpty(oVertex.Name)
                &&
                oUniqueEdgesByUser.TryGetValue(
                    oVertex.Name, out oUniqueEdgesForUser)
                )
            {
                TwitterSearchNetworkVertexMetricUtil
                    .AddGraphMetricValueForTopStrings(
                        oUniqueEdgesForUser, UrlsInTweetColumnName,
                        MaximumTopItems, iRowID,
                        oTopUrlsInTweetByCountGraphMetricValues,
                        oTopUrlsInTweetBySalienceGraphMetricValues
                        );

                TwitterSearchNetworkVertexMetricUtil
                    .AddGraphMetricValueForTopStrings(
                        oUniqueEdgesForUser, DomainsInTweetColumnName,
                        MaximumTopItems, iRowID,
                        oTopDomainsInTweetByCountGraphMetricValues,
                        oTopDomainsInTweetBySalienceGraphMetricValues
                        );

                TwitterSearchNetworkVertexMetricUtil
                    .AddGraphMetricValueForTopStrings(
                        oUniqueEdgesForUser, HashtagsInTweetColumnName,
                        MaximumTopItems, iRowID,
                        oTopHashtagsInTweetByCountGraphMetricValues,
                        oTopHashtagsInTweetBySalienceGraphMetricValues
                        );

                TwitterSearchNetworkVertexMetricUtil
                    .AddGraphMetricValuesForTopWordsAndWordPairs(
                        oUniqueEdgesForUser, StatusColumnName,
                        MaximumTopItems, oWordCounter, oWordPairCounter,
                        iRowID,
                        oTopWordsInTweetByCountGraphMetricValues,
                        oTopWordsInTweetBySalienceGraphMetricValues,
                        oTopWordPairsInTweetByCountGraphMetricValues,
                        oTopWordPairsInTweetBySalienceGraphMetricValues
                        );
            }
        }

        TwitterSearchNetworkVertexMetricUtil.AddGraphMetricColumns(

            oGraphMetricColumns,

            oTopUrlsInTweetByCountGraphMetricValues,
            VertexTableColumnNames.TopUrlsInTweetByCount,

            oTopUrlsInTweetBySalienceGraphMetricValues,
            VertexTableColumnNames.TopUrlsInTweetBySalience,

            oTopDomainsInTweetByCountGraphMetricValues,
            VertexTableColumnNames.TopDomainsInTweetByCount,

            oTopDomainsInTweetBySalienceGraphMetricValues,
            VertexTableColumnNames.TopDomainsInTweetBySalience,

            oTopHashtagsInTweetByCountGraphMetricValues,
            VertexTableColumnNames.TopHashtagsInTweetByCount,

            oTopHashtagsInTweetBySalienceGraphMetricValues,
            VertexTableColumnNames.TopHashtagsInTweetBySalience,

            oTopWordsInTweetByCountGraphMetricValues,
            VertexTableColumnNames.TopWordsInTweetByCount,

            oTopWordsInTweetBySalienceGraphMetricValues,
            VertexTableColumnNames.TopWordsInTweetBySalience,

            oTopWordPairsInTweetByCountGraphMetricValues,
            VertexTableColumnNames.TopWordPairsInTweetByCount,

            oTopWordPairsInTweetBySalienceGraphMetricValues,
            VertexTableColumnNames.TopWordPairsInTweetBySalience
            );

        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();

            // 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();

        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 (
            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);
        }