def calculate_EQ(G, communities):
    """Calculate the Extended Modularity (EQ) of Overlapping Communities"""
    m = G.number_of_edges()
    EQ = 0.0

    # Pre-calculate the Number of Communities a Node Belongs To
    node_community_count = {}
    for node in G.nodes():
        node_community_count[node] = sum(1 for community in communities if node in community)
        if node_community_count[node] == 0:
            node_community_count[node] = 1

    # Optimize the Calculation Process Using Matrix Operations
    for community in communities:
        community = list(community)  # Convert to a List to Support Indexing
        for i in range(len(community)):
            for j in range(i + 1, len(community)):  # Calculate Only the Upper Triangular Matrix
                x, y = community[i], community[j]

                A_xy = 1.0 if G.has_edge(x, y) else 0.0
                k_x = G.degree(x)
                k_y = G.degree(y)
                O_x = node_community_count[x]
                O_y = node_community_count[y]

                EQ += 2 * (A_xy - (k_x * k_y) / (2 * m)) / (O_x * O_y) 

    EQ = EQ / (2 * m)
    return EQ

def evaluate_communities(G, communities):
    """
    Evaluate Community Detection Results
    Parameters:
        G: Network graph
        communities: List of detected communities
    Returns:
        metrics: A dictionary containing various evaluation metrics
    """
    metrics = {
        'EQ': calculate_EQ(G, communities),
        'community_count': len(communities),
        'avg_community_size': sum(len(c) for c in communities) / len(communities)
    }
    return metrics

