saxbophone / arby

Arbitrary precision arithmetic in C++, even at compile-time
https://saxbophone.com/arby/
Mozilla Public License 2.0
8 stars 1 forks source link

Write constexpr tests #54

Open saxbophone opened 2 years ago

saxbophone commented 2 years ago

Note: you can't return a constexpr Uint value from a constexpr function and have it persist into runtime due to memory allocation (you can't access memory "allocated" at compile time from runtime), however you can return a static-allocated value computed from a Uint such as the uintmax_t cast result from a calculation.

It might also be worth trying to "unpack" compile-time Uint objects into a fixed-size array, as we can work out how big it needs to be at compile-time.

saxbophone commented 2 years ago

An ideal test for this feature is Finding an ideal ratio for converting between two number bases (i.e. what basest.core._encoding_ratio() does).

This is ideal because if we constrain base_from, base_to and max_chunk_size to all be uintmax_t, the results will never exceed uintmax_t, but may require intermediate values beyond it, and that's where we can prove that Nat works at compile-time without needing to store it at compile-time (which doesn't work due to extensively tested reasons beyond this discussion).

Prototype:

constexpr std::pair<uintmax_t, uintmax_t> best_ratio(uintmax_t base_from, uintmax_t base_t, uintmax_t max_chunk_size);
saxbophone commented 1 year ago

Sample demo program:

using namespace com::saxbophone;

constexpr std::pair<uintmax_t, uintmax_t> best_ratio(uintmax_t base_from, uintmax_t base_to, uintmax_t max_chunk_size) {
    std::pair<uintmax_t, uintmax_t> current_best = {0, 1}; // theoretical worst ratio
    for (std::uintmax_t chunk_size = 1; chunk_size <= max_chunk_size; chunk_size++) {
        auto space_size = arby::ipow(base_from, chunk_size);
        auto match = arby::ilog(base_to, space_size).second; // take ceiling of ilog()
        std::pair<uintmax_t, uintmax_t> ratio = {chunk_size, (uintmax_t)match};
        if (((double)ratio.first / (double)ratio.second) > ((double)current_best.first / (double)current_best.second)) {
            current_best = ratio;
        }
    }
    return current_best;
}

#include <iostream>

int main() {
    static_assert(best_ratio(256, 255, 10) == std::pair<uintmax_t, uintmax_t>{10, 11});
    constexpr auto r = best_ratio(256, 85, 10);
    std::cout << r.first << " " << r.second << std::endl;
}