// File: /workspace/github_cpp_makefile_star2_O2/sebastienmeyer2[P]mst-clustering/src/mst_kruskal.cpp
// Function: KruskalAlgorithm::compute_mst()
void KruskalAlgorithm::compute_mst()
{
    /*!
     * @brief Computes the Minimum Spanning Tree of the graph associated to 
     * this instance of MSTAlgorithm, using Kruskal's algorithm. More specifically,
     * sorts all the edges by their weight, and uses an Union-Find data structure
     * to select which components to union at each iteration, until there is 
     * only one left, which is the MST.
     * 
     */

    this->mst_weight = 0.0;
    std::cout << "\nComputing the MST using Kruskal's algorithm...";
    std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();

    int n = this->initial_graph->get_number_of_nodes();
    std::vector<Node*> nodes = this->initial_graph->get_nodes();
    std::unordered_set<Edge*> edges = this->initial_graph->get_edges();

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

            if (w1 < w2) {
                return false; // reversed in order to follow priority queue's implementation
            } else if (w1 > w2) {
                return true;
            } else { // allows multiple keys for same weight values (arbitrary comp)
                intptr_t p1 = reinterpret_cast<intptr_t>(e1);
                intptr_t p2 = reinterpret_cast<intptr_t>(e2);

                return p1 < p2;
            }
        };

    // initialization of the algorithm
    std::priority_queue<Edge*, std::vector<Edge*>, decltype(cmp)> pq(cmp);
    for (Edge* e : edges) {
        pq.push(e);
    }

    UnionFind uf = UnionFind(nodes);

    // while there is more than one component, we add edges that are shared 
    int k = uf.get_num_classes();
    while (k > 1) {

        Edge* min_edge = pq.top();
        pq.pop();

        Node* rep1 = uf.Find(min_edge->p1);
        Node* rep2 = uf.Find(min_edge->p2);

        if (rep1 != rep2) {
            this->mst_graph.add_edge(min_edge);
            this->mst_weight += min_edge->weight;
            uf.Union(rep1, rep2);
        }

        k = uf.get_num_classes();
    }

    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
    std::cout << "[OK]" << std::endl;
    this->treatment_done();

    std::cout << "Total weight of the MST: " << this->mst_weight << std::endl;

    std::cout << "Time spent by the algorithm: " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << " µs" << std::endl;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x148, %rsp            # imm = 0x148
movq %rdi, %r14
andq $0x0, 0xf8(%rdi)
movq 0xbbd3(%rip), %rdi      # 0x16fb0
leaq 0x4410(%rip), %rsi      # 0xf7f4
callq 0x32a0
callq 0x33a0
movq %rax, 0x20(%rsp)
movq 0x8(%r14), %rdi
callq 0x465a
movq 0x8(%r14), %rsi
leaq 0x40(%rsp), %rdi
callq 0x465e
movq 0x8(%r14), %rsi
leaq 0x58(%rsp), %rdi
callq 0x46e4
xorpd %xmm0, %xmm0
movq %rsp, %r15
movapd %xmm0, (%r15)
andq $0x0, 0x10(%r15)
leaq 0x90(%rsp), %r12
movapd %xmm0, (%r12)
andq $0x0, 0x10(%r12)
movq %r12, %rdi
callq 0x9aba
leaq 0x68(%rsp), %rbx
movq (%rbx), %rbx
testq %rbx, %rbx
je 0xb489
movq 0x8(%rbx), %rax
movq %rax, 0x90(%rsp)
movq %r15, %rdi
movq %r12, %rsi
callq 0x9aee
movq (%rsp), %rdi
movq 0x8(%rsp), %rsi
movq -0x8(%rsi), %rcx
subq %rdi, %rsi
sarq $0x3, %rsi
decq %rsi
xorl %edx, %edx
callq 0xb734
jmp 0xb44a
leaq 0x28(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x9a6c
leaq 0x90(%rsp), %rdi
leaq 0x28(%rsp), %rsi
callq 0xc084
leaq 0x28(%rsp), %rdi
callq 0x5d78
leaq 0x90(%rsp), %rdi
callq 0xc242
leaq 0x10(%r14), %r15
leaq 0x90(%rsp), %r12
cmpl $0x2, %eax
jl 0xb577
movq (%rsp), %rdi
movq 0x8(%rsp), %rax
movq (%rdi), %r13
movq %rax, %rcx
subq %rdi, %rcx
cmpq $0x9, %rcx
jl 0xb50d
leaq -0x8(%rax), %rdx
movq -0x8(%rax), %rcx
movq %r13, -0x8(%rax)
subq %rdi, %rdx
sarq $0x3, %rdx
xorl %esi, %esi
callq 0xb6a7
movq 0x8(%rsp), %rax
addq $-0x8, %rax
movq %rax, 0x8(%rsp)
movq (%r13), %rsi
movq %r12, %rdi
callq 0xc24a
movq %rax, %rbp
movq 0x8(%r13), %rsi
movq %r12, %rdi
callq 0xc24a
movq %rax, %rbx
cmpq %rax, %rbp
je 0xb56a
movq %r15, %rdi
movq %r13, %rsi
callq 0x4250
movsd 0x10(%r13), %xmm0
addsd 0xf8(%r14), %xmm0
movsd %xmm0, 0xf8(%r14)
movq %r12, %rdi
movq %rbp, %rsi
movq %rbx, %rdx
callq 0xc2c4
movq %r12, %rdi
callq 0xc242
jmp 0xb4cd
callq 0x33a0
movq %rax, %r15
movq 0xba2a(%rip), %rdi      # 0x16fb0
leaq 0x41d6(%rip), %rsi      # 0xf763
callq 0x32a0
movq %rax, %rdi
callq 0x3130
movq %r14, %rdi
callq 0x944c
movq 0xba07(%rip), %rdi      # 0x16fb0
leaq 0x41b8(%rip), %rsi      # 0xf768
callq 0x32a0
movsd 0xf8(%r14), %xmm0
movq %rax, %rdi
callq 0x3430
movq %rax, %rdi
callq 0x3130
movq 0xb9db(%rip), %rdi      # 0x16fb0
leaq 0x41a6(%rip), %rsi      # 0xf782
callq 0x32a0
movq %rax, %rdi
subq 0x20(%rsp), %r15
movl $0x3e8, %ecx            # imm = 0x3E8
movq %r15, %rax
cqto
idivq %rcx
movq %rax, %rsi
callq 0x3290
leaq 0x419b(%rip), %rsi      # 0xf7a0
movq %rax, %rdi
callq 0x32a0
movq %rax, %rdi
callq 0x3130
leaq 0x90(%rsp), %rdi
callq 0xc19a
movq %rsp, %rdi
callq 0x9aba
leaq 0x58(%rsp), %rdi
callq 0x4cfe
leaq 0x40(%rsp), %rdi
callq 0x5d78
addq $0x148, %rsp            # imm = 0x148
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0xb673
movq %rax, %rbx
leaq 0x28(%rsp), %rdi
callq 0x5d78
jmp 0xb683
jmp 0xb66e
movq %rax, %rbx
jmp 0xb695
jmp 0xb673
jmp 0xb673
jmp 0xb673
movq %rax, %rbx
jmp 0xb683
movq %rax, %rbx
leaq 0x90(%rsp), %rdi
callq 0xc19a
movq %rsp, %rdi
callq 0x9aba
leaq 0x58(%rsp), %rdi
callq 0x4cfe
leaq 0x40(%rsp), %rdi
callq 0x5d78
movq %rbx, %rdi
callq 0x34b0