// File: sebastienmeyer2[P]mst-clustering/src/mst_algorithm.cpp
// Function: MSTAlgorithm::compute_clustering(double)
std::unordered_map<Node*, Node*> MSTAlgorithm::compute_clustering(double cutoff)
{
    /*!
     * @brief Creates a mapping between nodes of the MST (and initial) graph 
     * after it is built, and their representatives where we have eliminated 
     * the edges whose weights have a standard deviation larger than cutoff 
     * times the average weights of the other edges connected to its nodes
     * 
     * @param cutoff The scale to eliminate edges
     * 
     * @return A mapping between nodes of the graph(s) and their representatives 
     * after removing inconsistent edges
     * 
     */

    if (!this->treated) {
        this->compute_mst();
    }

    auto cmp = [](const Edge* e1, const Edge* e2) 
        { 
            double w1 = e1->weight;
            double w2 = e2->weight;

            if (w1 < w2) {
                return false;
            } else if (w1 > w2) {
                return true;
            } else { // Allows multiple keys for same weight values
                intptr_t p1 = reinterpret_cast<intptr_t>(e1);
                intptr_t p2 = reinterpret_cast<intptr_t>(e2);

                return p1 < p2;
            }
        };

    // builds an union-find ds and initializes the edges
    std::unordered_set<Edge*> mst_edges = this->mst_graph.get_edges();

    std::vector<Node*> nodes = this->mst_graph.get_nodes();
    UnionFind uf = UnionFind(nodes);

    // passes through the MST edges and keeps the consistent edges
    
    for (Edge* e : mst_edges) {
        bool add_edge = true;

        Node* p1 = e->p1;
        Node* p2 = e->p2;

        double avg_weight = 0.0;
        double std_dev = 0.0;
        double cnt = 0.0;

        double edge_std_dev = 0.0;

        // computes mean weight and std deviation for edges on both sides
        for (Edge* e1 : this->mst_graph.connected_edges(p1)) {
            if (e1 != e) {
                double edge_weight = e1->weight;
                avg_weight += edge_weight;
                std_dev += edge_weight*edge_weight;
                cnt += 1;
            }
        }

        for (Edge* e2 : this->mst_graph.connected_edges(p2)) {
            if (e2 != e) {
                double edge_weight = e2->weight;
                avg_weight += edge_weight;
                std_dev += edge_weight*edge_weight;
                cnt += 1;
            }
        }

        avg_weight /= cnt;
        std_dev = std::sqrt((std_dev/cnt) - avg_weight*avg_weight);

        // discards the considered edge if its std deviation is too large 
        edge_std_dev = std::abs(e->weight - avg_weight);
        if (edge_std_dev >= cutoff*std_dev) {
            add_edge = false;
        }

        // adds the edge if it is consistent
        if (add_edge == true) {
            uf.Union(p1, p2);
        }
    }

    // builds a map from all the nodes to their representatives 
    std::unordered_map<Node*, Node*> clusters;

    for (Node* node : nodes) {
        Node* rep = uf.Find(node);

        clusters.insert(std::pair<Node*, Node*>(node, rep));
    }

    return clusters;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x290, %rsp            # imm = 0x290
movq %rdi, -0x258(%rbp)
movq %rdi, %rax
movq %rax, -0x250(%rbp)
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movsd %xmm0, -0x18(%rbp)
movq -0x10(%rbp), %rax
movq %rax, -0x248(%rbp)
testb $0x1, 0x100(%rax)
jne 0x26c09
movq -0x248(%rbp), %rdi
movq (%rdi), %rax
callq *(%rax)
movq -0x248(%rbp), %rsi
addq $0x10, %rsi
movq %rsi, -0x260(%rbp)
leaq -0x58(%rbp), %rdi
callq 0xd660
movq -0x260(%rbp), %rsi
leaq -0x70(%rbp), %rdi
callq 0xd530
jmp 0x26c36
leaq -0x148(%rbp), %rdi
leaq -0x70(%rbp), %rsi
callq 0x27940
jmp 0x26c48
leaq -0x130(%rbp), %rdi
leaq -0x148(%rbp), %rsi
callq 0x30fd0
jmp 0x26c5d
leaq -0x148(%rbp), %rdi
callq 0xded0
leaq -0x58(%rbp), %rax
movq %rax, -0x150(%rbp)
movq -0x150(%rbp), %rdi
callq 0xe520
movq %rax, -0x158(%rbp)
movq -0x150(%rbp), %rdi
callq 0xe550
movq %rax, -0x160(%rbp)
leaq -0x158(%rbp), %rdi
leaq -0x160(%rbp), %rsi
callq 0xe580
testb $0x1, %al
jne 0x26cb6
jmp 0x27089
leaq -0x158(%rbp), %rdi
callq 0xe5b0
movq -0x248(%rbp), %rdi
movq (%rax), %rax
movq %rax, -0x168(%rbp)
movb $0x1, -0x169(%rbp)
movq -0x168(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x178(%rbp)
movq -0x168(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x180(%rbp)
movq $0x0, -0x188(%rbp)
movq $0x0, -0x190(%rbp)
movq $0x0, -0x198(%rbp)
movq $0x0, -0x1a0(%rbp)
addq $0x10, %rdi
movq -0x178(%rbp), %rsi
callq 0xd4e0
movq %rax, -0x268(%rbp)
jmp 0x26d42
movq -0x268(%rbp), %rax
movq %rax, -0x1a8(%rbp)
movq -0x1a8(%rbp), %rdi
callq 0x27b20
movq %rax, -0x1b0(%rbp)
movq -0x1a8(%rbp), %rdi
callq 0x27b50
movq %rax, -0x1b8(%rbp)
leaq -0x1b0(%rbp), %rdi
leaq -0x1b8(%rbp), %rsi
callq 0xe580
testb $0x1, %al
jne 0x26d92
jmp 0x26e8d
leaq -0x1b0(%rbp), %rdi
callq 0x27b80
movq (%rax), %rax
movq %rax, -0x1c0(%rbp)
movq -0x1c0(%rbp), %rax
cmpq -0x168(%rbp), %rax
je 0x26e7a
movq -0x1c0(%rbp), %rax
movsd 0x10(%rax), %xmm0
movsd %xmm0, -0x1c8(%rbp)
movsd -0x1c8(%rbp), %xmm0
addsd -0x188(%rbp), %xmm0
movsd %xmm0, -0x188(%rbp)
movsd -0x1c8(%rbp), %xmm0
movsd -0x1c8(%rbp), %xmm2
movsd -0x190(%rbp), %xmm1
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movsd %xmm0, -0x190(%rbp)
movsd 0x10290(%rip), %xmm0    # 0x370a8
addsd -0x198(%rbp), %xmm0
movsd %xmm0, -0x198(%rbp)
jmp 0x26e7a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x78(%rbp)
movl %eax, -0x7c(%rbp)
jmp 0x2720b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x78(%rbp)
movl %eax, -0x7c(%rbp)
jmp 0x27202
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x78(%rbp)
movl %eax, -0x7c(%rbp)
leaq -0x148(%rbp), %rdi
callq 0xded0
jmp 0x27202
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x78(%rbp)
movl %eax, -0x7c(%rbp)
jmp 0x271f6
jmp 0x26e7c
leaq -0x1b0(%rbp), %rdi
callq 0x27bb0
jmp 0x26d76
movq -0x248(%rbp), %rdi
addq $0x10, %rdi
movq -0x180(%rbp), %rsi
callq 0xd4e0
movq %rax, -0x270(%rbp)
jmp 0x26ead
movq -0x270(%rbp), %rax
movq %rax, -0x1d0(%rbp)
movq -0x1d0(%rbp), %rdi
callq 0x27b20
movq %rax, -0x1d8(%rbp)
movq -0x1d0(%rbp), %rdi
callq 0x27b50
movq %rax, -0x1e0(%rbp)
leaq -0x1d8(%rbp), %rdi
leaq -0x1e0(%rbp), %rsi
callq 0xe580
testb $0x1, %al
jne 0x26efd
jmp 0x26fa2
leaq -0x1d8(%rbp), %rdi
callq 0x27b80
movq (%rax), %rax
movq %rax, -0x1e8(%rbp)
movq -0x1e8(%rbp), %rax
cmpq -0x168(%rbp), %rax
je 0x26f8f
movq -0x1e8(%rbp), %rax
movsd 0x10(%rax), %xmm0
movsd %xmm0, -0x1f0(%rbp)
movsd -0x1f0(%rbp), %xmm0
addsd -0x188(%rbp), %xmm0
movsd %xmm0, -0x188(%rbp)
movsd -0x1f0(%rbp), %xmm0
movsd -0x1f0(%rbp), %xmm2
movsd -0x190(%rbp), %xmm1
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movsd %xmm0, -0x190(%rbp)
movsd 0x10129(%rip), %xmm0    # 0x370a8
addsd -0x198(%rbp), %xmm0
movsd %xmm0, -0x198(%rbp)
jmp 0x26f91
leaq -0x1d8(%rbp), %rdi
callq 0x27bb0
jmp 0x26ee1
movsd -0x198(%rbp), %xmm1
movsd -0x188(%rbp), %xmm0
divsd %xmm1, %xmm0
movsd %xmm0, -0x188(%rbp)
movsd -0x190(%rbp), %xmm0
movsd -0x198(%rbp), %xmm1
divsd %xmm1, %xmm0
movsd -0x188(%rbp), %xmm1
mulsd %xmm1, %xmm1
subsd %xmm1, %xmm0
callq 0xa5d0
movsd %xmm0, -0x190(%rbp)
movq -0x168(%rbp), %rax
movsd 0x10(%rax), %xmm0
movsd -0x188(%rbp), %xmm1
subsd %xmm1, %xmm0
callq 0x27be0
movsd %xmm0, -0x278(%rbp)
jmp 0x27016
movsd -0x278(%rbp), %xmm0
movsd %xmm0, -0x1a0(%rbp)
movsd -0x1a0(%rbp), %xmm0
movsd -0x18(%rbp), %xmm1
mulsd -0x190(%rbp), %xmm1
ucomisd %xmm1, %xmm0
jb 0x27048
movb $0x0, -0x169(%rbp)
movb -0x169(%rbp), %al
andb $0x1, %al
movzbl %al, %eax
cmpl $0x1, %eax
jne 0x27076
movq -0x178(%rbp), %rsi
movq -0x180(%rbp), %rdx
leaq -0x130(%rbp), %rdi
callq 0x31410
jmp 0x27074
jmp 0x27076
jmp 0x27078
leaq -0x158(%rbp), %rdi
callq 0xe5e0
jmp 0x26c9a
movq -0x258(%rbp), %rdi
movb $0x0, -0x1f1(%rbp)
callq 0x27a30
leaq -0x70(%rbp), %rax
movq %rax, -0x200(%rbp)
movq -0x200(%rbp), %rdi
callq 0x15bd0
movq %rax, -0x208(%rbp)
movq -0x200(%rbp), %rdi
callq 0x15a20
movq %rax, -0x210(%rbp)
leaq -0x208(%rbp), %rdi
leaq -0x210(%rbp), %rsi
callq 0x27a50
testb $0x1, %al
jne 0x270e9
jmp 0x271ac
leaq -0x208(%rbp), %rdi
callq 0x15fb0
movq (%rax), %rax
movq %rax, -0x218(%rbp)
movq -0x218(%rbp), %rsi
leaq -0x130(%rbp), %rdi
callq 0x31360
movq %rax, -0x280(%rbp)
jmp 0x2711b
movq -0x280(%rbp), %rax
movq %rax, -0x220(%rbp)
leaq -0x230(%rbp), %rdi
leaq -0x218(%rbp), %rsi
leaq -0x220(%rbp), %rdx
callq 0x27ad0
jmp 0x27145
movq -0x258(%rbp), %rdi
leaq -0x230(%rbp), %rsi
callq 0x27a90
movb %dl, -0x289(%rbp)
movq %rax, -0x288(%rbp)
jmp 0x27167
movb -0x289(%rbp), %al
movq -0x288(%rbp), %rcx
movq %rcx, -0x240(%rbp)
movb %al, -0x238(%rbp)
leaq -0x208(%rbp), %rdi
callq 0x27b00
jmp 0x270cd
movq -0x258(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x78(%rbp)
movl %eax, -0x7c(%rbp)
callq 0x1be30
jmp 0x271f6
movb $0x1, -0x1f1(%rbp)
testb $0x1, -0x1f1(%rbp)
jne 0x271c8
movq -0x258(%rbp), %rdi
callq 0x1be30
leaq -0x130(%rbp), %rdi
callq 0x311e0
leaq -0x70(%rbp), %rdi
callq 0xded0
leaq -0x58(%rbp), %rdi
callq 0xdf50
movq -0x250(%rbp), %rax
addq $0x290, %rsp            # imm = 0x290
popq %rbp
retq
leaq -0x130(%rbp), %rdi
callq 0x311e0
leaq -0x70(%rbp), %rdi
callq 0xded0
leaq -0x58(%rbp), %rdi
callq 0xdf50
movq -0x78(%rbp), %rdi
callq 0xade0
nopl (%rax)