// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// example_rational_demo.cpp
// Multi-precision rational number demo: showcasing Rational's power in 4 scenarios
//
// Build:
//   cl /std:c++latest /EHsc /O2 /MD /utf-8 /I<sangi>/include examples\example_rational_demo.cpp
//      /link sangi_rational.lib sangi_float.lib sangi_int.lib /MACHINE:X64

#include <math/core/mp/Rational.hpp>
#include <math/core/mp/Rational/RationalConstants.hpp>
#include <iostream>
#include <iomanip>
#include <string>
#include <cmath>
#include <vector>

using namespace sangi;

// ============================================================================
// Demo 1: Floating-Point Trap vs Exact Rational
//   -- Examples where double fails in everyday calculations
// ============================================================================
static void demo_float_trap() {
    std::cout << "============================================================\n";
    std::cout << " Demo 1: Floating-Point Trap vs Exact Rational\n";
    std::cout << "============================================================\n\n";

    // 0.1 + 0.2 == 0.3 ?
    {
        double da = 0.1, db = 0.2, dc = 0.3;
        Rational ra(1, 10), rb(2, 10), rc(3, 10);

        std::cout << "  [double]   0.1 + 0.2 == 0.3 ? "
                  << ((da + db == dc) ? "true" : "FALSE") << std::endl;
        std::cout << std::setprecision(20);
        std::cout << "             0.1 + 0.2 = " << (da + db) << std::endl;
        std::cout << "             0.3       = " << dc << std::endl;

        std::cout << "  [Rational] 1/10 + 2/10 == 3/10 ? "
                  << (((ra + rb) <=> rc) == 0 ? "true" : "FALSE") << std::endl;
        std::cout << "             1/10 + 2/10 = " << (ra + rb) << "\n\n";
    }

    // sum(1/k) forward summation vs backward summation vs rational
    {
        constexpr int N = 100;

        // double: forward summation (1->N)
        double fwd = 0.0;
        for (int k = 1; k <= N; ++k) fwd += 1.0 / k;

        // double: backward summation (N->1)
        double bwd = 0.0;
        for (int k = N; k >= 1; --k) bwd += 1.0 / k;

        // Rational: exact value
        Rational exact = harmonicNumber(N);

        std::cout << "  Harmonic number H_" << N << " = 1 + 1/2 + 1/3 + ... + 1/" << N << ":\n";
        std::cout << std::setprecision(17);
        std::cout << "    double (forward sum): " << fwd << std::endl;
        std::cout << "    double (backward sum): " << bwd << std::endl;
        std::cout << "    Rational (exact):      " << exact.toDecimal(17) << std::endl;
        std::cout << "    Exact numerator digits:   " << exact.numerator().toString().size() << " digits\n";
        std::cout << "    Exact denominator digits: " << exact.denominator().toString().size() << " digits\n";
    }
    std::cout << std::endl;
}

// ============================================================================
// Demo 2: Best Rational Approximations of Pi via Continued Fractions
//   -- Why is 355/113 so remarkably accurate?
// ============================================================================
static void demo_continued_fraction() {
    std::cout << "============================================================\n";
    std::cout << " Demo 2: Continued Fractions -- Best Approximations of Pi\n";
    std::cout << "============================================================\n\n";

    // Continued fraction expansion of pi: [3; 7, 15, 1, 292, 1, 1, 1, 2, ...]
    // First few terms set manually (since Rational has finite precision)
    std::vector<Int> pi_cf = {
        Int(3), Int(7), Int(15), Int(1), Int(292),
        Int(1), Int(1), Int(1), Int(2), Int(1),
        Int(3), Int(1), Int(14)
    };

    // Approximate value of pi (for comparison, with sufficient digits)
    const std::string pi_str = "3.14159265358979323846264338327950288";

    std::cout << "  pi = [3; 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, ...]\n\n";
    std::cout << std::setw(6) << "terms" << "  "
              << std::setw(20) << "fraction" << "  "
              << std::setw(24) << "decimal" << "  "
              << "correct digits" << std::endl;
    std::cout << std::string(75, '-') << std::endl;

    for (size_t depth = 1; depth <= pi_cf.size(); ++depth) {
        // Truncate continued fraction to depth terms and reconstruct rational
        std::vector<Int> partial(pi_cf.begin(), pi_cf.begin() + depth);
        Rational approx = Rational::fromContinuedFraction(partial);

        // Decimal expansion
        std::string dec = approx.toDecimal(20);

        // Count correct digits (after decimal point)
        int correct = 0;
        size_t start = 2;  // after "3."
        for (size_t i = start; i < std::min(dec.size(), pi_str.size()); ++i) {
            if (dec[i] == pi_str[i]) ++correct;
            else break;
        }

        // Fraction display
        std::string frac = approx.numerator().toString() + "/"
                         + approx.denominator().toString();

        std::cout << std::setw(6) << depth << "  "
                  << std::setw(20) << frac << "  "
                  << std::setw(24) << dec << "  "
                  << correct << " digits" << std::endl;
    }

    std::cout << "\n  Note: Adding term [292] after 355/113 dramatically improves precision.\n";
    std::cout << "  The larger the coefficient, the better the preceding approximation.\n";
    std::cout << "  355/113 gives 6 correct decimal digits with a 3-digit denominator -- discovered by Zu Chongzhi in ancient China.\n";
    std::cout << std::endl;
}

// ============================================================================
// Demo 3: Exact Harmonic Numbers H_n & Bernoulli Numbers
//   -- Computing gems of number theory with multi-precision rationals
// ============================================================================
static void demo_harmonic_bernoulli() {
    std::cout << "============================================================\n";
    std::cout << " Demo 3: Harmonic Numbers & Bernoulli Numbers\n";
    std::cout << "============================================================\n\n";

    // Harmonic numbers
    std::cout << "  --- Harmonic Numbers H_n (exact) ---\n";
    for (int n : {1, 5, 10, 20, 50}) {
        Rational Hn = harmonicNumber(n);
        std::cout << "  H_" << std::setw(2) << n << " = "
                  << std::setw(8) << Hn.toDecimal(12) << "...";
        if (n <= 10) {
            std::cout << "  = " << Hn;
        }
        std::cout << std::endl;
    }

    // Bernoulli numbers
    std::cout << "\n  --- Bernoulli Numbers B_n (exact) ---\n";
    std::cout << "  (B_1 = -1/2, B_n = 0 for odd n > 1)\n\n";
    for (int n : {0, 1, 2, 4, 6, 8, 10, 12, 20, 30}) {
        Rational Bn = bernoulli(n);
        std::cout << "  B_" << std::setw(2) << n << " = " << Bn << std::endl;
    }

    std::cout << "\n  B_30 has a huge numerator and denominator:\n";
    Rational B30 = bernoulli(30);
    std::cout << "    Numerator:   " << B30.numerator() << std::endl;
    std::cout << "    Denominator: " << B30.denominator() << std::endl;

    std::cout << std::endl;
}

// ============================================================================
// Demo 4: Stern-Brocot Tree & Farey Sequence
//   -- Exploring the beautiful structure of rationals
// ============================================================================
static void demo_stern_brocot_farey() {
    std::cout << "============================================================\n";
    std::cout << " Demo 4: Stern-Brocot Tree & Farey Sequence\n";
    std::cout << "============================================================\n\n";

    // Stern-Brocot tree: enumerate using nextMinimal
    std::cout << "  --- Stern-Brocot Order (first 20, ascending by height) ---\n  ";
    {
        Rational x(1, 1);
        std::cout << x;
        for (int i = 1; i < 20; ++i) {
            x = nextMinimal(x);
            std::cout << ", " << x;
        }
        std::cout << ", ...\n\n";
    }

    // Calkin-Wilf sequence
    std::cout << "  --- Calkin-Wilf Sequence (first 15) ---\n  ";
    {
        Rational x(1, 1);
        std::cout << x;
        for (int i = 1; i < 15; ++i) {
            x = nextCalkinWilf(x);
            std::cout << ", " << x;
        }
        std::cout << ", ...\n\n";
    }

    // Farey sequence F_7
    std::cout << "  --- Farey Sequence F_7 (irreducible fractions with denominator <= 7) ---\n  ";
    {
        // Build from 0/1 using mediant
        std::vector<Rational> farey;
        farey.push_back(Rational(0, 1));
        farey.push_back(Rational(1, 7));

        // Enumerate 0 < p/q <= 1, q <= 7 using fareyNeighbors
        Rational x(0, 1);
        farey.clear();
        farey.push_back(x);
        for (;;) {
            auto [left, right] = fareyNeighbors(x, Int(7));
            if (right > Rational(1)) break;
            farey.push_back(right);
            x = right;
        }

        for (size_t i = 0; i < farey.size(); ++i) {
            if (i > 0) std::cout << ", ";
            std::cout << farey[i];
        }
        std::cout << "\n";
        std::cout << "  Total " << farey.size() << " irreducible fractions\n\n";
    }

    // simplestBetween
    std::cout << "  --- simplestBetween: simplest rational between two rationals ---\n";
    {
        struct { Rational a, b; } cases[] = {
            {Rational(3, 7),  Rational(2, 5)},
            {Rational(1, 3),  Rational(1, 2)},
            {Rational(7, 10), Rational(5, 7)},
            {Rational(99, 100), Rational(100, 101)},
        };
        for (auto& [a, b] : cases) {
            Rational lo = (a < b) ? a : b;
            Rational hi = (a < b) ? b : a;
            Rational s = simplestBetween(lo, hi);
            std::cout << "    simplestBetween(" << lo << ", " << hi << ") = " << s
                      << std::endl;
        }
    }

    std::cout << std::endl;
}

// ============================================================================
// main
// ============================================================================
int main() {
    std::cout << "###########################################################\n";
    std::cout << "#  sangi Rational Demo -- Power of Multi-Precision Rationals #\n";
    std::cout << "###########################################################\n\n";

    demo_float_trap();
    demo_continued_fraction();
    demo_harmonic_bernoulli();
    demo_stern_brocot_farey();

    std::cout << "###########################################################\n";
    std::cout << "#  All demos completed                                    #\n";
    std::cout << "###########################################################\n";

    return 0;
}
