BackgroundWorker_DoWork
        (
            object sender,
            DoWorkEventArgs e
        )
        {
            Debug.Assert(sender is BackgroundWorker);
            AssertValid();

            BackgroundWorker oBackgroundWorker = (BackgroundWorker)sender;

            Debug.Assert(e.Argument is CalculateGraphMetricsAsyncArgs);

            CalculateGraphMetricsAsyncArgs oCalculateGraphMetricsAsyncArgs =
                (CalculateGraphMetricsAsyncArgs)e.Argument;

            CalculateGraphMetricsAsyncInternal(oCalculateGraphMetricsAsyncArgs,
                                               m_oBackgroundWorker, e);
        }
        /* Create analyzers and graph according to a "setting" object provided by outter View component; then pass to a BackgroundWorker.
         * In this project, this is invoked by a Dialog onload event handler.
         * */
        public void calculateMetricsAsync(IGraph graph, MetricsCheckedList checkedlist) {  

            if (m_oBackgroundWorker != null && m_oBackgroundWorker.IsBusy)
            {
                /*
                throw new InvalidOperationException(String.Format(
                    "{0}:{1}: An asynchronous operation is already in progress."
                    ,
                    this.ClassName,
                    MethodName
                    ));
                 * */
            }

            // logic about which calculator should be created 

            CalculateGraphMetricsAsyncArgs args = new CalculateGraphMetricsAsyncArgs(); // add analyzer to this object and pass to BackgroundWorker
            args.Analyzers = new LinkedList<AnalyzerBase>();
            args.Graph = graph;


            if (checkedlist.overall_graph_metrics == true) {args.Analyzers.AddLast(new OverallMetricCalculator()); }
            if (checkedlist.vertex_degree == true) {args.Analyzers.AddLast(new VertexDegreeCalculator()); }
            if (checkedlist.vertex_reciprocated_pair_ratio == true) {args.Analyzers.AddLast(new ReciprocatedVertexPairRatioCalculator()); }
            if (checkedlist.vertex_clustering_coefficient == true) {args.Analyzers.AddLast(new ClusteringCoefficientCalculator()); }
            if (checkedlist.vertex_pagerank == true) { args.Analyzers.AddLast(new PageRankCalculator());}
            if (checkedlist.vertex_eigenvector_centrality == true) {}
            if (checkedlist.group_metrics == true) { args.Analyzers.AddLast(new GroupMetricCalculator());} 
            
            // create a new BackgroundWorker
            m_oBackgroundWorker = new BackgroundWorker();

            m_oBackgroundWorker.WorkerReportsProgress = true;
            m_oBackgroundWorker.WorkerSupportsCancellation = true;

            m_oBackgroundWorker.DoWork += new DoWorkEventHandler(BackgroundWorker_DoWork);
            m_oBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);
            m_oBackgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BackgroundWorker_RunWorkerCompleted);

            m_oBackgroundWorker.RunWorkerAsync(args);
        }
        //*************************************************************************
        //  Method: CalculateGraphMetricsAsyncInternal()
        //
        /// <summary>
        /// Calculates one or more sets of graph metrics and stores the results in
        /// one or more worksheet columns.
        /// </summary>
        ///
        /// <param name="oCalculateGraphMetricsAsyncArgs">
        /// Contains the arguments needed to asynchronously calculate graph
        /// metrics.
        /// </param>
        ///
        /// <param name="oBackgroundWorker">
        /// A BackgroundWorker object.
        /// </param>
        ///
        /// <param name="oDoWorkEventArgs">
        /// A DoWorkEventArgs object.
        /// </param>
        //*************************************************************************
        protected void 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);
        }
        //*************************************************************************
        //  Method: CalculateGraphMetricsAsync()
        //
        /// <summary>
        /// Asynchronously calculates one or more sets of specified graph metrics
        /// and returns the results as an array of <see
        /// cref="GraphMetricColumn" /> objects.
        /// </summary>
        ///
        /// <param name="workbook">
        /// Workbook containing the graph contents.
        /// </param>
        ///
        /// <param name="graphMetricCalculators">
        /// An array of <see cref="IGraphMetricCalculator2" /> implementations, one
        /// for each set of graph metrics that should be calculated.  This method
        /// sorts the array in place, so its contents will likely be in a different
        /// order when the method returns.
        /// </param>
        ///
        /// <param name="graphMetricUserSettings">
        /// User settings for calculating graph metrics.
        /// </param>
        ///
        /// <remarks>
        /// For each <see cref="IGraphMetricCalculator2" /> implementation in the
        /// <paramref name="graphMetricCalculators" /> array, this method calls the
        /// implementation's <see
        /// cref="IGraphMetricCalculator2.TryCalculateGraphMetrics" /> method.  The
        /// <see cref="GraphMetricColumn" /> objects returned by each
        /// implementation are aggregated.  When graph metric calculations
        /// complete, the <see cref="GraphMetricCalculationCompleted" /> event
        /// fires and the aggregated results can be obtained via the <see
        /// cref="RunWorkerCompletedEventArgs.Result" /> property.
        ///
        /// <para>
        /// To cancel the calculations, call <see cref="CancelAsync" />.
        /// </para>
        ///
        /// <para>
        /// If <paramref name="workbook" /> contains invalid graph data, a <see
        /// cref="WorkbookFormatException" /> is thrown on the caller's thread
        /// before asynchronous calculations begin.
        /// </para>
        ///
        /// </remarks>
        //*************************************************************************
        public void CalculateGraphMetricsAsync(
            Microsoft.Office.Interop.Excel.Workbook workbook,
            IGraphMetricCalculator2 [] graphMetricCalculators,
            GraphMetricUserSettings graphMetricUserSettings
            )
        {
            Debug.Assert(workbook != null);
            Debug.Assert(graphMetricCalculators != null);
            Debug.Assert(graphMetricUserSettings != null);
            AssertValid();

            const String MethodName = "CalculateGraphMetricsAsync";

            if (this.IsBusy)
            {
            throw new InvalidOperationException( String.Format(

                "{0}:{1}: An asynchronous operation is already in progress."
                ,
                this.ClassName,
                MethodName
                ) );
            }

            // Read the workbook into a graph.  Do this from the calling thread to
            // avoid reading the Excel UI from a background thread.

            IGraph oGraph = ReadWorkbook(workbook, graphMetricUserSettings);

            // Sort the calculators so that those that can handle duplicate edges
            // are grouped at the beginning of the array.  When the workbook is
            // read into a graph, duplicate edges are included and that graph is
            // passed to the first group of calculators.  When the second group is
            // reached -- those that cannot handle duplicate edges -- the
            // duplicates are removed from the graph before passing the graph to
            // the second group.

            Array.Sort(graphMetricCalculators,
            (x, y) =>
                y.HandlesDuplicateEdges.CompareTo(x.HandlesDuplicateEdges)
            );

            // Wrap the arguments in an object that can be passed to
            // BackgroundWorker.RunWorkerAsync().

            CalculateGraphMetricsAsyncArgs oCalculateGraphMetricsAsyncArgs =
            new CalculateGraphMetricsAsyncArgs();

            oCalculateGraphMetricsAsyncArgs.Graph = oGraph;

            oCalculateGraphMetricsAsyncArgs.SortedGraphMetricCalculators =
            graphMetricCalculators;

            oCalculateGraphMetricsAsyncArgs.GraphMetricUserSettings =
            graphMetricUserSettings;

            // Create a BackgroundWorker and handle its events.

            m_oBackgroundWorker = new BackgroundWorker();

            m_oBackgroundWorker.WorkerReportsProgress = true;
            m_oBackgroundWorker.WorkerSupportsCancellation = true;

            m_oBackgroundWorker.DoWork += new DoWorkEventHandler(
            BackgroundWorker_DoWork);

            m_oBackgroundWorker.ProgressChanged +=
            new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);

            m_oBackgroundWorker.RunWorkerCompleted +=
            new RunWorkerCompletedEventHandler(
                BackgroundWorker_RunWorkerCompleted);

            m_oBackgroundWorker.RunWorkerAsync(oCalculateGraphMetricsAsyncArgs);
        }
        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);
        }
        CalculateGraphMetricsAsync
        (
            Microsoft.Office.Interop.Excel.Workbook workbook,
            IGraphMetricCalculator2 [] graphMetricCalculators,
            GraphMetricUserSettings graphMetricUserSettings
        )
        {
            Debug.Assert(workbook != null);
            Debug.Assert(graphMetricCalculators != null);
            Debug.Assert(graphMetricUserSettings != null);
            AssertValid();

            const String MethodName = "CalculateGraphMetricsAsync";

            if (this.IsBusy)
            {
                throw new InvalidOperationException(String.Format(

                                                        "{0}:{1}: An asynchronous operation is already in progress."
                                                        ,
                                                        this.ClassName,
                                                        MethodName
                                                        ));
            }

            // Read the workbook into a graph.  Do this from the calling thread to
            // avoid reading the Excel UI from a background thread.

            IGraph oGraph = ReadWorkbook(workbook, graphMetricUserSettings);

            // Sort the calculators so that those that can handle duplicate edges
            // are grouped at the beginning of the array.  When the workbook is
            // read into a graph, duplicate edges are included and that graph is
            // passed to the first group of calculators.  When the second group is
            // reached -- those that cannot handle duplicate edges -- the
            // duplicates are removed from the graph before passing the graph to
            // the second group.

            Array.Sort(graphMetricCalculators,
                       (x, y) =>
                       y.HandlesDuplicateEdges.CompareTo(x.HandlesDuplicateEdges)
                       );

            // Wrap the arguments in an object that can be passed to
            // BackgroundWorker.RunWorkerAsync().

            CalculateGraphMetricsAsyncArgs oCalculateGraphMetricsAsyncArgs =
                new CalculateGraphMetricsAsyncArgs();

            oCalculateGraphMetricsAsyncArgs.Graph = oGraph;

            oCalculateGraphMetricsAsyncArgs.SortedGraphMetricCalculators =
                graphMetricCalculators;

            oCalculateGraphMetricsAsyncArgs.GraphMetricUserSettings =
                graphMetricUserSettings;

            // Create a BackgroundWorker and handle its events.

            m_oBackgroundWorker = new BackgroundWorker();

            m_oBackgroundWorker.WorkerReportsProgress      = true;
            m_oBackgroundWorker.WorkerSupportsCancellation = true;

            m_oBackgroundWorker.DoWork += new DoWorkEventHandler(
                BackgroundWorker_DoWork);

            m_oBackgroundWorker.ProgressChanged +=
                new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);

            m_oBackgroundWorker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
                    BackgroundWorker_RunWorkerCompleted);

            m_oBackgroundWorker.RunWorkerAsync(oCalculateGraphMetricsAsyncArgs);
        }