#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <omp.h>

#define V 10000  // Количество вершин в графе
#define E 50000  // Примерное количество рёбер в графе

typedef struct Edge {
    int src, dest, weight;
} Edge;

typedef struct Graph {
    int numVertices, numEdges;
    Edge* edges;
} Graph;

Graph* createGraph(int numVertices, int numEdges) {
    Graph* graph = (Graph*)malloc(sizeof(Graph));
    graph->numVertices = numVertices;
    graph->numEdges = numEdges;
    graph->edges = (Edge*)malloc(numEdges * sizeof(Edge));
    return graph;
}

void generateRandomGraph(Graph* graph) {
    srand(time(0));
    #pragma omp parallel for
    for (int i = 0; i < graph->numEdges; i++) {
        graph->edges[i].src = rand() % V;
        graph->edges[i].dest = rand() % V;
        graph->edges[i].weight = rand() % 100 + 1;  // Случайный вес от 1 до 100
    }
}

int find(int parent[], int i) {
    if (parent[i] == -1)
        return i;
    return find(parent, parent[i]);
}

void unionSets(int parent[], int x, int y) {
    int xroot = find(parent, x);
    int yroot = find(parent, y);
    if (xroot != yroot)
        parent[xroot] = yroot;
}

int compare(const void* a, const void* b) {
    Edge* edge1 = (Edge*)a;
    Edge* edge2 = (Edge*)b;
    return edge1->weight - edge2->weight;
}

void boruvkaMST(Graph* graph) {
    int parent[V];
    int mstWeight = 0;
    int numComponents = V;

    for (int i = 0; i < V; i++)
        parent[i] = -1;

    while (numComponents > 1) {
        int* cheapest = (int*)malloc(V * sizeof(int));
        #pragma omp parallel for
        for (int i = 0; i < V; i++)
            cheapest[i] = -1;

        #pragma omp parallel for
        for (int i = 0; i < graph->numEdges; i++) {
            int root1 = find(parent, graph->edges[i].src);
            int root2 = find(parent, graph->edges[i].dest);

            if (root1 != root2) {
                if (cheapest[root1] == -1 || graph->edges[cheapest[root1]].weight > graph->edges[i].weight)
                    cheapest[root1] = i;
                if (cheapest[root2] == -1 || graph->edges[cheapest[root2]].weight > graph->edges[i].weight)
                    cheapest[root2] = i;
            }
        }

        #pragma omp parallel for
        for (int i = 0; i < V; i++) {
            if (cheapest[i] != -1) {
                int root1 = find(parent, graph->edges[cheapest[i]].src);
                int root2 = find(parent, graph->edges[cheapest[i]].dest);

                if (root1 != root2) {
                    #pragma omp critical
                    {
                        printf("Edge %d-%d weight %d\n", graph->edges[cheapest[i]].src, graph->edges[cheapest[i]].dest, graph->edges[cheapest[i]].weight);
                        mstWeight += graph->edges[cheapest[i]].weight;
                        unionSets(parent, root1, root2);
                        numComponents--;
                    }
                }
            }
        }

        free(cheapest);
    }

    printf("Total weight of MST: %d\n", mstWeight);
}

int main() {
    Graph* graph = createGraph(V, E);
    generateRandomGraph(graph);

    boruvkaMST(graph);

    free(graph->edges);
    free(graph);

    return 0;
}

