// example_float.cpp -- Float API examples (float.html)
#include <math/core/mp/Float.hpp>
#include <iostream>
#include <chrono>

using namespace calx;

// Pi computation
void demo_pi() {
    std::cout << "=== Pi Computation ===\n";
    Float::setDefaultPrecision(1000);
    Float pi = Float::pi(1000);
    std::cout << pi.toDecimalString(1000) << std::endl;
}

// Multi-precision exp / log
void demo_exp_log() {
    std::cout << "\n=== Multi-Precision exp / log ===\n";
    int prec = 30;
    Float::setDefaultPrecision(prec);
    Float x("1.5");

    Float ex = exp(x, prec);          // e^1.5
    Float lx = log(ex, prec);         // ln(e^1.5) == 1.5
    std::cout << "exp(1.5) = " << ex.toDecimalString(prec) << "\n";
    std::cout << "log(exp(1.5)) = " << lx.toDecimalString(prec) << "\n";
}

// Safe propagation of NaN / Infinity
void demo_nan_infinity() {
    std::cout << "\n=== Safe Propagation of NaN / Infinity ===\n";
    Float inf = Float::positiveInfinity();
    Float nan = Float::nan();
    Float z   = Float::zero();

    Float r1 = inf + Float(1);     // +Inf
    Float r2 = inf - inf;          // NaN
    Float r3 = nan + Float(42);    // NaN
    Float r4 = Float(1) / z;      // +Inf

    std::cout << "Inf + 1   = " << r1 << "\n";
    std::cout << "Inf - Inf = " << r2 << "\n";
    std::cout << "NaN + 42  = " << r3 << "\n";
    std::cout << "1 / 0     = " << r4 << "\n";
}

// Precision control and constant caching
void demo_precision_cache() {
    std::cout << "\n=== Precision Control & Constant Cache ===\n";
    using Clock = std::chrono::high_resolution_clock;

    auto t0 = Clock::now();
    Float pi1 = Float::pi(10000);   // first call: computed
    auto t1 = Clock::now();
    Float pi2 = Float::pi(10000);   // second call: returned from cache
    auto t2 = Clock::now();
    Float pi3 = Float::pi(50000);   // different precision: recomputed
    auto t3 = Clock::now();
    Float pi4 = Float::pi(50000);   // returned from cache
    auto t4 = Clock::now();

    auto us = [](auto d) { return std::chrono::duration_cast<std::chrono::microseconds>(d).count(); };

    std::cout << "pi(10000)  1st call (computed):    " << us(t1-t0) << " us\n";
    std::cout << "pi(10000)  2nd call (from cache):  " << us(t2-t1) << " us\n";
    std::cout << "pi(50000)  1st call (recomputed):  " << us(t3-t2) << " us\n";
    std::cout << "pi(50000)  2nd call (from cache):  " << us(t4-t3) << " us\n";

    // Rounding mode: display 1/3 in binary
    // 1/3 (binary) = 0.01010101... (infinite repeating)
    // setPrecision(1) -> internal 12-bit precision, difference appears in trailing bits
    std::cout << "\n--- Rounding Mode (1/3 in binary) ---\n";
    std::cout << "  1/3 (binary) = 0.010101... (infinite)\n\n";
    auto showRound = [](const char* name, RoundingMode mode) {
        Float::setRoundingMode(mode);
        Float pos = Float(1, 30) / Float(3, 30);
        Float neg = Float(-1, 30) / Float(3, 30);
        pos.setPrecision(1);
        neg.setPrecision(1);
        int bits = static_cast<int>(pos.mantissa().bitLength()) + 1;
        std::cout << "  " << name << ":   1/3 -> " << pos.toString(2, bits)
                  << "   -1/3 -> " << neg.toString(2, bits) << "\n";
    };
    showRound("ToNearest (default)", RoundingMode::ToNearest);
    showRound("TowardZero         ", RoundingMode::TowardZero);
    showRound("TowardPositive     ", RoundingMode::TowardPositive);
    showRound("TowardNegative     ", RoundingMode::TowardNegative);

    Float::setRoundingMode(RoundingMode::ToNearest);
}

int main() {
    demo_pi();
    demo_exp_log();
    demo_nan_infinity();
    demo_precision_cache();
    return 0;
}
