0

I am writing a program to create a file of prime numbers. I am new to C, and thus to its I/O system. The input is the number before (and including) which all prime numbers are to be written in a prime.csv file. I am trying to make my code efficient by using old primes. But, the subsequent prime of the last prime in the prime.csv file is not being appended. I have been stuck on this problem for a while now. Any help will be appreciated.

primes.c:

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

int isPrime(FILE *file, unsigned int length, unsigned int number);
void primes(unsigned int number);

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        perror("Usage: a.exe number");
        return 1;
    }

    unsigned int number = atoi(argv[1]);

    primes(number);

    return 0;
}

int isPrime(FILE *file, unsigned int length, unsigned int number)
{
    fseek(file, 0L, SEEK_SET);

    unsigned int factor;
    for (int i = 0; i < length; i++)
    {
        fscanf(file, "%u", &factor);
        
        if (number % factor == 0)
            return 0;
    }

    return 1;
}

void primes(unsigned int number)
{
    // Getting Header Info
    FILE *file_h = fopen("primes_h.csv", "r");
    if (file_h == NULL)
    {
        perror("Was not able to open the file");
        return;
    }

    unsigned int length;
    unsigned int last;
    fscanf(file_h, "%u,%u", &length, &last);

    fclose(file_h);

    // If number already less than or equal to last prime
    if (number <= last + 1)
    {
        puts("Condition already satisfied.");
        return;
    }

    // Adding new primes
    FILE *file = fopen("primes.csv", "a+");
    if (file == NULL)
    {
        perror("Was not able to open the file");
        return;
    }

    unsigned int newLast;
    unsigned int newLength = length;
    for (unsigned int now = last + 2; now <= number; now += 2)
    {
        if (isPrime(file, newLength, now))
        {
            fprintf(file, "%u\n", now);
            newLast = now;
            newLength++;
        }
    }
    fclose(file);

    // Editing Header Info
    FILE *e_file_h = fopen("primes_h.csv", "w");
    if (e_file_h == NULL)
    {
        perror("Was not able to open the file");
        return;
    }

    fprintf(e_file_h, "%u,%u", newLength, newLast);

    fclose(e_file_h);
}

primes.csv:

2 
3 
5 
7 
11
13
17
19
23
29
31
37
41
43
47
53
59
61
67
71
73
79
83
89
97
101
103
107
109

and so on...

primes_h.csv:

669,4999

The primes_h.csv contains the header info about the primes.csv in the format: length, last

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • no matter how I try your code, I get: Condition already satisfied. with my files being empty.. So I'm not sure what your problem is here – Angevil Mar 13 '21 at 19:42
  • Have you tried running your code line by line in a debugger while monitoring the values of all variables, in order to determine at which point your program stops behaving as intended? If you did not try this, then you probably want to read this: [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/q/25385173/12149471) You may also want to read this: [How to debug small programs?](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). – Andreas Wenzel Mar 13 '21 at 20:32
  • Since you're also interested in performance ... `fscanf` is pretty slow. And, having `isPrime` _rescan_ the file is doubly so. I'd preread the file _once_ and create an array of the primes. If that's too large to fit in memory, I'd preread the [text] file and output the binary values to a different file. I'd then `mmap` the binary file. Also, `isPrime` is testing the _entire_ list. It should stop [and fail] if the current list element is greater than the sqrt of `number` – Craig Estey Mar 13 '21 at 21:01

1 Answers1

1

Reading primes from a file to test a number for primality is very inefficient: you will save a number of divisions, but these savings should be small compared to the overhead of reading from the file system and converting the numbers from text to internal representation. Furthermore, you should stop trying the divisors when they become greater than the square root of the number.

You could instead use an array to store the prime numbers found so far. This array does not need to store more than the prime numbers below the square root of the maximum number.

Here is a modified version:

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

int main(int argc, char *argv[]) {
    int primes[46341];  // all primes up to sqrt(INT32_MAX)
    int nprimes = 0;

    if (argc < 2) {
        fprintf(stderr, "primes: missing argument\n");
        return 1;
    }
    int limit = strtol(argv[1], NULL, 0);
    FILE *fp = fopen("primes.csv", "w");
    if (fp == NULL) {
        fprintf(stderr, "primes: cannot open primes.csv\n");
        return 1;
    }
    fprintf(fp, "prime\n");
    if (limit >= 2) {
        primes[nprimes++] = 2;
        fprintf(fp, "2\n");
    }
    for (int n = 3; n <= limit; n += 2) {
        int isprime = 1;
        for (int i = 1; i < nprimes; i++) {
            int p = primes[i];
            if (p * p > n)
                break;
            if (n % p == 0) {
                isprime = 0;
                break;
            }
        }
        if (isprime) {
            if (nprimes < (int)(sizeof(primes) / sizeof(primes[0])))
                primes[nprimes++] = n;
            fprintf(fp, "%d\n", n);
        }
    }
    fclose(fp);
    return 0;
}

Note however that trial division is not very efficient to enumerate primes numbers up to a limit. A Sieve of Eratosthenes is much faster.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • I appreciate the edits, but for now, I just want to know why I'm missing a prime when I write the new primes to the file. – Armaan Randhawa Mar 14 '21 at 06:03
  • Your code has undefined behavior because you must use `fseek()` to switch from reading the file to writing to it. Add `fseek(file, 0L, SEEK_END);` before the `fprintf(file, "%u\n", now);` – chqrlie Mar 15 '21 at 00:32