#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>
#include <algorithm> // Pre std::sort

#define VELKOST_BLOKU 1000000 // Spracujeme milión čísel naraz v RAM

int main() {
	
	#pragma omp parallel
    {
        int id = omp_get_thread_num();
        printf("Ahoj z vlakna %d\n", id);
    }
    
    printf("\n");

    FILE *f = fopen("prvocisla.txt", "w");
    if (f == NULL) return 1;

    fprintf(f, "1:2\n");
    long long int pocet_celkovo = 1;
    
    printf("Hladam prvocisla vsetkymi %d jadrami CPU...\n\nVysledky ukladam do suboru 'prvocisla.txt'...\n\n", omp_get_max_threads());

    // Hlavný cyklus po veľkých blokoch
    for (long long int start = 3; start <= 1000000000; start += VELKOST_BLOKU) {
        long long int koniec = start + VELKOST_BLOKU;
        
        // Dočasné pole pre prvočísla nájdené v tomto bloku
        long long int *najdene_v_bloku = (long long int *)malloc((VELKOST_BLOKU / 2) * sizeof(long long int));
        int zdielany_index = 0;

        #pragma omp parallel
        {
            // Každé vlákno si zbiera svoje výsledky lokálne
            long long int *lokalny_list = (long long int *)malloc((VELKOST_BLOKU / 4) * sizeof(long long int));
            int lokalny_pocet = 0;

            #pragma omp for nowait
            for (long long int n = start; n < koniec; n += 2) {
                int je_prvo = 1;
                long long int odmocnina = (long long int)sqrt(n);
                for (long long int i = 3; i <= odmocnina; i += 2) {
                    if (n % i == 0) { je_prvo = 0; break; }
                }
                if (je_prvo) lokalny_list[lokalny_pocet++] = n;
            }

            // Keď vlákno skončí, pridá svoje výsledky do bloku (kritická sekcia)
            #pragma omp critical
            {
                for (int i = 0; i < lokalny_pocet; i++) {
                    najdene_v_bloku[zdielany_index++] = lokalny_list[i];
                }
            }
            free(lokalny_list);
        }

        // --- Teraz sme mimo paralelnej sekcie, spracujeme blok ---
        
        // 1. Zoradíme čísla v bloku (lebo jadrá ich našli na preskačku)
        std::sort(najdene_v_bloku, najdene_v_bloku + zdielany_index);

        // 2. Zapíšeme celý blok naraz na disk
        for (int i = 0; i < zdielany_index; i++) {
            fprintf(f, "%lld:%lld\n", ++pocet_celkovo, najdene_v_bloku[i]);
        }

 //       printf("Spracovany blok do %lld | Celkovo prvocisiel: %lld\r", koniec, pocet_celkovo);
        printf("Spracovany blok do %lld | Celkovo prvocisiel: %lld\r", koniec, pocet_celkovo);
        fflush(stdout);
        
        free(najdene_v_bloku);
    }

    fclose(f);
    return 0;
}
