Task: N-smooth numbers
C: #include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

void* xmalloc(size_t n) {
    void* ptr = malloc(n);
    if (ptr == NULL) {
        fprintf(stderr, "Out of memory\n");
        exit(1);
    }
    return ptr;
}

void* xrealloc(void* p, size_t n) {
    void* ptr = realloc(p, n);
    if (ptr == NULL) {
        fprintf(stderr, "Out of memory\n");
        exit(1);
    }
    return ptr;
}

bool is_prime(uint32_t n) {
    if (n == 2)
        return true;
    if (n < 2 || n % 2 == 0)
        return false;
    for (uint32_t p = 3; p * p <= n; p += 2) {
        if (n % p == 0)
            return false;
    }
    return true;
}



uint32_t find_primes(uint32_t from, uint32_t to, uint32_t** primes) {
    uint32_t count = 0, buffer_length = 16;
    uint32_t* buffer = xmalloc(sizeof(uint32_t) * buffer_length);
    for (uint32_t p = from; p <= to; ++p) {
        if (is_prime(p)) {
            if (count >= buffer_length) {
                uint32_t new_length = buffer_length * 2;
                if (new_length < count + 1)
                    new_length = count + 1;
                buffer = xrealloc(buffer, sizeof(uint32_t) * new_length);
                buffer_length = new_length;
            }
            buffer[count++] = p;
        }
    }
    *primes = buffer;
    return count;
}

void free_numbers(mpz_t* numbers, size_t count) {
    for (size_t i = 0; i < count; ++i)
        mpz_clear(numbers[i]);
    free(numbers);
}


mpz_t* find_nsmooth_numbers(uint32_t n, uint32_t count) {
    uint32_t* primes = NULL;
    uint32_t num_primes = find_primes(2, n, &primes);
    mpz_t* numbers = xmalloc(sizeof(mpz_t) * count);
    mpz_t* queue = xmalloc(sizeof(mpz_t) * num_primes);
    uint32_t* index = xmalloc(sizeof(uint32_t) * num_primes);
    for (uint32_t i = 0; i < num_primes; ++i) {
        index[i] = 0;
        mpz_init_set_ui(queue[i], primes[i]);
    }
    for (uint32_t i = 0; i < count; ++i)
        mpz_init(numbers[i]);
    mpz_set_ui(numbers[0], 1);
    for (uint32_t i = 1; i < count; ++i) {
        for (uint32_t p = 0; p < num_primes; ++p) {
            if (mpz_cmp(queue[p], numbers[i - 1]) == 0)
                mpz_mul_ui(queue[p], numbers[++index[p]], primes[p]);
        }
        uint32_t min_index = 0;
        for (uint32_t p = 1; p < num_primes; ++p) {
            if (mpz_cmp(queue[min_index], queue[p]) > 0)
                min_index = p;
        }
        mpz_set(numbers[i], queue[min_index]);
    }
    free_numbers(queue, num_primes);
    free(primes);
    free(index);
    return numbers;
}

void print_nsmooth_numbers(uint32_t n, uint32_t begin, uint32_t count) {
    uint32_t num = begin + count;
    mpz_t* numbers = find_nsmooth_numbers(n, num);
    printf("%u: ", n);
    mpz_out_str(stdout, 10, numbers[begin]);
    for (uint32_t i = 1; i < count; ++i) {
        printf(", ");
        mpz_out_str(stdout, 10, numbers[begin + i]);
    }
    printf("\n");
    free_numbers(numbers, num);
}

int main() {
    printf("First 25 n-smooth numbers for n = 2 -> 29:\n");
    for (uint32_t n = 2; n <= 29; ++n) {
        if (is_prime(n))
            print_nsmooth_numbers(n, 0, 25);
    }
    printf("\n3 n-smooth numbers starting from 3000th for n = 3 -> 29:\n");
    for (uint32_t n = 3; n <= 29; ++n) {
        if (is_prime(n))
            print_nsmooth_numbers(n, 2999, 3);
    }
    printf("\n20 n-smooth numbers starting from 30,000th for n = 503 -> 521:\n");
    for (uint32_t n = 503; n <= 521; ++n) {
        if (is_prime(n))
            print_nsmooth_numbers(n, 29999, 20);
    }
    return 0;
}

C++: #include <algorithm>
#include <iostream>
#include <vector>

std::vector<uint64_t> primes;
std::vector<uint64_t> smallPrimes;

template <typename T>
std::ostream &operator <<(std::ostream &os, const std::vector<T> &v) {
    auto it = v.cbegin();
    auto end = v.cend();

    os << '[';

    if (it != end) {
        os << *it;
        it = std::next(it);
    }

    for (; it != end; it = std::next(it)) {
        os << ", " << *it;
    }

    return os << ']';
}

bool isPrime(uint64_t value) {
    if (value < 2) return false;

    if (value % 2 == 0) return value == 2;
    if (value % 3 == 0) return value == 3;

    if (value % 5 == 0) return value == 5;
    if (value % 7 == 0) return value == 7;

    if (value % 11 == 0) return value == 11;
    if (value % 13 == 0) return value == 13;

    if (value % 17 == 0) return value == 17;
    if (value % 19 == 0) return value == 19;

    if (value % 23 == 0) return value == 23;

    uint64_t t = 29;
    while (t * t < value) {
        if (value % t == 0) return false;
        value += 2;

        if (value % t == 0) return false;
        value += 4;
    }

    return true;
}

void init() {
    primes.push_back(2);
    smallPrimes.push_back(2);

    uint64_t i = 3;
    while (i <= 521) {
        if (isPrime(i)) {
            primes.push_back(i);
            if (i <= 29) {
                smallPrimes.push_back(i);
            }
        }
        i += 2;
    }
}

std::vector<uint64_t> nSmooth(uint64_t n, size_t size) {
    if (n < 2 || n>521) {
        throw std::runtime_error("n must be between 2 and 521");
    }
    if (size <= 1) {
        throw std::runtime_error("size must be at least 1");
    }

    uint64_t bn = n;
    if (primes.cend() == std::find(primes.cbegin(), primes.cend(), bn)) {
        throw std::runtime_error("n must be a prime number");
    }

    std::vector<uint64_t> ns(size, 0);
    ns[0] = 1;

    std::vector<uint64_t> next;
    for (auto prime : primes) {
        if (prime > bn) {
            break;
        }
        next.push_back(prime);
    }

    std::vector<size_t> indicies(next.size(), 0);
    for (size_t m = 1; m < size; m++) {
        ns[m] = *std::min_element(next.cbegin(), next.cend());
        for (size_t i = 0; i < indicies.size(); i++) {
            if (ns[m] == next[i]) {
                indicies[i]++;
                next[i] = primes[i] * ns[indicies[i]];
            }
        }
    }

    return ns;
}

int main() {
    init();

    for (auto i : smallPrimes) {
        std::cout << "The first " << i << "-smooth numbers are:\n";
        std::cout << nSmooth(i, 25) << '\n';
        std::cout << '\n';
    }

    
    for (size_t i = 0; i < smallPrimes.size(); i++) {
        if (i < 1) continue;
        auto p = smallPrimes[i];

        auto v = nSmooth(p, 3002);
        v.erase(v.begin(), v.begin() + 2999);

        std::cout << "The 30,000th to 30,019th " << p << "-smooth numbers are:\n";
        std::cout << v << '\n';
        std::cout << '\n';
    }

    return 0;
}