The prime reason your code is slow, is simply that you don't remember earlier calculation on what numbers are prime or not. So you recalculate it every time, for every number, and that is a lot of calculations.
Code review
Your code looks clean enough, and the style is good. I would consider having all of the arguments on the same line as long as you don't have too many arguments. But then again being consistent and writing code according to a defined style does make for clean and readable code.
Your choice of doing a for
loop which adds 6
at a time (within is_prime
) is wise, and rather useful to avoid doing too many tests when you want to display or check a lower number for primality. However this gets very expensive when you want to display all primes below a given \$n\$.
Implement a better algorithm
You should look into using other algorithms for generating prime numbers, and apply one of these. For some theory read Wikipedia: Prime number, and an article on one of the better approaches: Wikipedia: Sieve of Eratosthenes. Another article on the latter is Determine if a Number is Prime.
The base concept of "The Sieve of Eratosthenese" is to have an array of \$n\$ numbers with boolean values, and then you start crossing out all multiples of 2, continue with crossing out all multiples of 3, and then you continue finding the next in the array which isn't crossed out, and remove all multiples of that. In the end you have a list of all primes below \$n\$.
In your case you could output each prime at the start of crossing them out. But be warned that this algorithm does require a lot of space, and it will still take a substantial amount of time and memory to find all the primes below ULLONG_MAX
.
A combined approach
Something which would give you some gain, but not a lot would be to build a temporary array of primes, and use this to eliminate most of the lower cases. I.e. if you have a table of the first 1000 primes, you can avoid checking higher number for all those not being in that list. Which would falsify higher candidate numbers somewhat sooner.
The flip side is that when you get to higher candidate numbers the primes are further apart, so it will get slow very fast, even with such a combined approach.
inline uint32_t get_point (uint32_t x, uint32_t y);
. To avoid long parameter lists running away, useinline uint32_t get_point (uint32_t x, // optional comment descibing parameter
NEW LINEuint32_t y); // optional comment describing parameter
\$\endgroup\$