Counts duplicate and unique edges in a graph.
The IIdentityProvider.Name property on each of an edge's vertices is used to test for duplicate edges.

In a directed graph, (A,B) and (A,B) are considered duplicates. (A,B) and (B,A) are not duplicates.

In an undirected graph, (A,B) and (A,B) are considered duplicates. (A,B) and (B,A) are also duplicates.

        CalculateReciprocatedEdgeRatio
        (
            IGraph oGraph,
            Int32 iVertexPairsWithBothDirectedEdges
        )
        {
            Debug.Assert(oGraph != null);
            Debug.Assert(oGraph.Directedness == GraphDirectedness.Directed);
            Debug.Assert(iVertexPairsWithBothDirectedEdges >= 0);
            AssertValid();

            // Definition, from Derek Hansen in July 2012:
            //
            // # of edges that have a reciprocated "pair" / total number of edges
            //
            // Equivalent definition, from http://
            // faculty.ucr.edu/~hanneman/nettext/C8_Embedding.html#reciprocity
            //
            // "...the number of ties that are involved in reciprocal relations
            // relative to the total number of actual ties (not possible ties)."

            Int32 iTotalEdgesAfterMergingDupcliatesNoSelfLoops =
                new DuplicateEdgeDetector(oGraph)
                .TotalEdgesAfterMergingDuplicatesNoSelfLoops;

            if (iTotalEdgesAfterMergingDupcliatesNoSelfLoops == 0)
            {
                return(null);
            }

            return((Double)iVertexPairsWithBothDirectedEdges * 2.0 /
                   (Double)iTotalEdgesAfterMergingDupcliatesNoSelfLoops);
        }
        TryCalculateGraphMetrics
        (
            IGraph graph,
            BackgroundWorker backgroundWorker,
            out OverallMetrics graphMetrics
        )
        {
            Debug.Assert(graph != null);
            AssertValid();

            graphMetrics = null;

            if (!ReportProgressAndCheckCancellationPending(
                    1, 3, backgroundWorker))
            {
                return(false);
            }

            DuplicateEdgeDetector oDuplicateEdgeDetector =
                new DuplicateEdgeDetector(graph);

            Int32 iVertices  = graph.Vertices.Count;
            Int32 iEdges     = graph.Edges.Count;
            Int32 iSelfLoops = CountSelfLoops(graph);

            Int32 iConnectedComponents, iSingleVertexConnectedComponents,
                  iMaximumConnectedComponentVertices,
                  iMaximumConnectedComponentEdges;

            CalculateConnectedComponentMetrics(graph, out iConnectedComponents,
                                               out iSingleVertexConnectedComponents,
                                               out iMaximumConnectedComponentVertices,
                                               out iMaximumConnectedComponentEdges);

            Nullable <Int32>  iMaximumGeodesicDistance;
            Nullable <Double> dAverageGeodesicDistance;
            Nullable <Double> dModularity;

            if (!ReportProgressAndCheckCancellationPending(
                    2, 3, backgroundWorker))
            {
                return(false);
            }

            CalculateSnapOverallMetrics(graph, out iMaximumGeodesicDistance,
                                        out dAverageGeodesicDistance, out dModularity);

            Nullable <Double> dReciprocatedVertexPairRatio, dReciprocatedEdgeRatio;

            if (!(new OverallReciprocationCalculator())
                .TryCalculateGraphMetrics(graph, backgroundWorker,
                                          out dReciprocatedVertexPairRatio, out dReciprocatedEdgeRatio))
            {
                return(false);
            }

            OverallMetrics oOverallMetrics = new OverallMetrics(
                graph.Directedness,
                oDuplicateEdgeDetector.UniqueEdges,
                oDuplicateEdgeDetector.EdgesWithDuplicates,
                iSelfLoops,
                iVertices,

                CalculateGraphDensity(graph, iVertices,
                                      oDuplicateEdgeDetector.
                                      TotalEdgesAfterMergingDuplicatesNoSelfLoops),

                dModularity,
                iConnectedComponents,
                iSingleVertexConnectedComponents,
                iMaximumConnectedComponentVertices,
                iMaximumConnectedComponentEdges,
                iMaximumGeodesicDistance,
                dAverageGeodesicDistance,
                dReciprocatedVertexPairRatio,
                dReciprocatedEdgeRatio
                );

            graphMetrics = oOverallMetrics;

            return(true);
        }
    CalculateReciprocatedEdgeRatio
    (
        IGraph oGraph,
        Int32 iVertexPairsWithBothDirectedEdges
    )
    {
        Debug.Assert(oGraph != null);
        Debug.Assert(oGraph.Directedness == GraphDirectedness.Directed);
        Debug.Assert(iVertexPairsWithBothDirectedEdges >= 0);
        AssertValid();

        // Definition, from Derek Hansen in July 2012:
        //
        // # of edges that have a reciprocated "pair" / total number of edges
        //
        // Equivalent definition, from http://
        // faculty.ucr.edu/~hanneman/nettext/C8_Embedding.html#reciprocity
        //
        // "...the number of ties that are involved in reciprocal relations
        // relative to the total number of actual ties (not possible ties)."

        Int32 iTotalEdgesAfterMergingDupcliatesNoSelfLoops =
            new DuplicateEdgeDetector(oGraph)
            .TotalEdgesAfterMergingDuplicatesNoSelfLoops;

        if (iTotalEdgesAfterMergingDupcliatesNoSelfLoops == 0)
        {
            return (null);
        }

        return ( (Double)iVertexPairsWithBothDirectedEdges * 2.0 /
            (Double)iTotalEdgesAfterMergingDupcliatesNoSelfLoops );
    }
    TryCalculateGraphMetrics
    (
        IGraph graph,
        BackgroundWorker backgroundWorker,
        out OverallMetrics graphMetrics
    )
    {
        Debug.Assert(graph != null);
        AssertValid();

        graphMetrics = null;

        if ( !ReportProgressAndCheckCancellationPending(
            1, 3, backgroundWorker) )
        {
            return (false);
        }

        DuplicateEdgeDetector oDuplicateEdgeDetector =
            new DuplicateEdgeDetector(graph);

        Int32 iVertices = graph.Vertices.Count;
        Int32 iEdges = graph.Edges.Count;
        Int32 iSelfLoops = CountSelfLoops(graph);

        Int32 iConnectedComponents, iSingleVertexConnectedComponents,
            iMaximumConnectedComponentVertices,
            iMaximumConnectedComponentEdges;

        CalculateConnectedComponentMetrics(graph, out iConnectedComponents,
            out iSingleVertexConnectedComponents,
            out iMaximumConnectedComponentVertices,
            out iMaximumConnectedComponentEdges);

        Nullable<Int32> iMaximumGeodesicDistance;
        Nullable<Double> dAverageGeodesicDistance;
        Nullable<Double> dModularity;

        if ( !ReportProgressAndCheckCancellationPending(
            2, 3, backgroundWorker) )
        {
            return (false);
        }

        CalculateSnapOverallMetrics(graph, out iMaximumGeodesicDistance,
            out dAverageGeodesicDistance, out dModularity);

        Nullable<Double> dReciprocatedVertexPairRatio, dReciprocatedEdgeRatio;

        if ( !( new OverallReciprocationCalculator() )
            .TryCalculateGraphMetrics(graph, backgroundWorker, 
                out dReciprocatedVertexPairRatio, out dReciprocatedEdgeRatio) )
        {
            return (false);
        }

        OverallMetrics oOverallMetrics = new OverallMetrics(
            graph.Directedness,
            oDuplicateEdgeDetector.UniqueEdges,
            oDuplicateEdgeDetector.EdgesWithDuplicates,
            iSelfLoops,
            iVertices,

            CalculateGraphDensity(graph, iVertices,
                oDuplicateEdgeDetector.
                    TotalEdgesAfterMergingDuplicatesNoSelfLoops),

            dModularity,
            iConnectedComponents,
            iSingleVertexConnectedComponents,
            iMaximumConnectedComponentVertices,
            iMaximumConnectedComponentEdges,
            iMaximumGeodesicDistance,
            dAverageGeodesicDistance,
            dReciprocatedVertexPairRatio,
            dReciprocatedEdgeRatio
            );

        graphMetrics = oOverallMetrics;

        return (true);
    }
    TestGraphContainsDuplicateEdges13()
    {
        // Undirected graph, many duplicates.

        m_oDuplicateEdgeDetector =
            new DuplicateEdgeDetector(m_oUndirectedGraph);

        IEdgeCollection oEdges = m_oUndirectedGraph.Edges;

        oEdges.Add(m_oUndirectedVertexB, m_oUndirectedVertexA, false);
        oEdges.Add(m_oUndirectedVertexC, m_oUndirectedVertexD, false);
        oEdges.Add(m_oUndirectedVertexA, m_oUndirectedVertexA, false);
        oEdges.Add(m_oUndirectedVertexB, m_oUndirectedVertexB, false);
        oEdges.Add(m_oUndirectedVertexC, m_oUndirectedVertexD, false);
        oEdges.Add(m_oUndirectedVertexA, m_oUndirectedVertexB, false);
        oEdges.Add(m_oUndirectedVertexA, m_oUndirectedVertexB, false);
        oEdges.Add(m_oUndirectedVertexB, m_oUndirectedVertexA, false);
        oEdges.Add(m_oUndirectedVertexC, m_oUndirectedVertexD, false);

        Assert.IsTrue(m_oDuplicateEdgeDetector.GraphContainsDuplicateEdges);
        Assert.AreEqual(2, m_oDuplicateEdgeDetector.UniqueEdges);
        Assert.AreEqual(7, m_oDuplicateEdgeDetector.EdgesWithDuplicates);

        Assert.AreEqual(2, m_oDuplicateEdgeDetector.
            TotalEdgesAfterMergingDuplicatesNoSelfLoops);
    }
    TestGraphContainsDuplicateEdges8()
    {
        // Undirected graph, no duplicate edges.

        m_oDuplicateEdgeDetector =
            new DuplicateEdgeDetector(m_oUndirectedGraph);

        IEdgeCollection oEdges = m_oUndirectedGraph.Edges;

        oEdges.Add(m_oUndirectedVertexA, m_oUndirectedVertexB, false);
        oEdges.Add(m_oUndirectedVertexA, m_oUndirectedVertexA, false);
        oEdges.Add(m_oUndirectedVertexB, m_oUndirectedVertexB, false);
        oEdges.Add(m_oUndirectedVertexC, m_oUndirectedVertexD, false);
        oEdges.Add(m_oUndirectedVertexC, m_oUndirectedVertexWithNullName,
            false);

        Assert.IsFalse(m_oDuplicateEdgeDetector.GraphContainsDuplicateEdges);
        Assert.AreEqual(5, m_oDuplicateEdgeDetector.UniqueEdges);
        Assert.AreEqual(0, m_oDuplicateEdgeDetector.EdgesWithDuplicates);

        Assert.AreEqual(3, m_oDuplicateEdgeDetector.
            TotalEdgesAfterMergingDuplicatesNoSelfLoops);
    }
    TestGraphContainsDuplicateEdges6()
    {
        // Directed graph, non-duplicate null name, C.

        m_oDuplicateEdgeDetector = new DuplicateEdgeDetector(m_oDirectedGraph);

        IEdgeCollection oEdges = m_oDirectedGraph.Edges;

        oEdges.Add(m_oDirectedVertexA, m_oDirectedVertexB, true);
        oEdges.Add(m_oDirectedVertexB, m_oDirectedVertexA, true);
        oEdges.Add(m_oDirectedVertexA, m_oDirectedVertexA, true);
        oEdges.Add(m_oDirectedVertexB, m_oDirectedVertexB, true);
        oEdges.Add(m_oDirectedVertexC, m_oDirectedVertexD, true);
        oEdges.Add(m_oDirectedVertexC, m_oDirectedVertexWithNullName, true);

        oEdges.Add(m_oDirectedVertexWithNullName, m_oDirectedVertexC, true);

        Assert.IsFalse(m_oDuplicateEdgeDetector.GraphContainsDuplicateEdges);
        Assert.AreEqual(7, m_oDuplicateEdgeDetector.UniqueEdges);
        Assert.AreEqual(0, m_oDuplicateEdgeDetector.EdgesWithDuplicates);

        Assert.AreEqual(5, m_oDuplicateEdgeDetector.
            TotalEdgesAfterMergingDuplicatesNoSelfLoops);
    }