#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>

#define BUFFER_SIZE 100000  // Koľko prvočísel uložíme v pamäti pred zápisom
#define MAX_CISLO 1000000000 // Horná hranica hľadania

int main() {
    long long int *buffer = (long long int *)malloc(BUFFER_SIZE * sizeof(long long int));
    if (buffer == NULL) {
        printf("Chyba pri alokacii pamate!\n");
        return 1;
    }

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

    long long int pocet_celkovo = 0;
    int v_buffri = 0;

    printf("Hladam prvocisla pomocou %d jadier a zapisujem do suboru 'prvocisla.txt'...\n\n", omp_get_max_threads());

    // Hlavný cyklus rozdelený na bloky pre paralelizáciu
    // Hľadáme od 3 a skáčeme po 2 (párne čísla preskakujeme)
    // Prvé prvočíslo 2 zapíšeme manuálne
    fprintf(f, "1:2\n");
    pocet_celkovo = 1;

    for (long long int start_bloku = 3; start_bloku <= MAX_CISLO; start_bloku += 100000) {
        long long int koniec_bloku = start_bloku + 100000;
        if (koniec_bloku > MAX_CISLO) koniec_bloku = MAX_CISLO;

        // Paralelná časť: Každé jadro preveruje časť bloku
        #pragma omp parallel
        {
            // Lokálny buffer pre každé vlákno, aby sa navzájom neblokovali
            long long int *local_buffer = (long long int *)malloc(100000 * sizeof(long long int));
            int local_count = 0;

            #pragma omp for nowait
            for (long long int n = start_bloku; n < koniec_bloku; 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) {
                    local_buffer[local_count++] = n;
                }
            }

            // Kritická sekcia: Sem vstupuje vždy len jedno vlákno naraz, aby bezpečne zapísalo dáta
            #pragma omp critical
            {
                for (int i = 0; i < local_count; i++) {
                    buffer[v_buffri++] = local_buffer[i];
                    
                    // Ak je hlavný buffer plný, šup s ním na disk
                    if (v_buffri >= BUFFER_SIZE) {
                        for (int j = 0; j < v_buffri; j++) {
                            fprintf(f, "%lld:%lld\n", ++pocet_celkovo, buffer[j]);
                        }
                        v_buffri = 0;
                        printf("Najdenych %lld prvocisiel...\n", pocet_celkovo);
                    }
                }
            }
            free(local_buffer);
        }
    }

    // Zápis zvyšku buffra po skončení cyklu
    for (int j = 0; j < v_buffri; j++) {
        fprintf(f, "%lld:%lld\n", ++pocet_celkovo, buffer[j]);
    }

    fclose(f);
    free(buffer);
    printf("Hotovo! Celkovo najdenych: %lld\n", pocet_celkovo);

    return 0;
}
