// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// example_doc_int_samples.cpp
// Verification harness for selected samples in api/Int.html (ja+en).

#include <math/core/mp/Int.hpp>
#include <math/core/mp/Int/IntCombinatorics.hpp>
#include <math/core/mp/Int/IntSqrt.hpp>
#include <math/core/mp/Int/IntModular.hpp>
#include <iomanip>
#include <iostream>

using namespace sangi;

int main() {
    std::cout << std::setprecision(20);

    // ---- big-number arithmetic (sec. Arithmetic operators) -------
    {
        std::cout << "[arithmetic on 30-digit numbers]\n";
        Int a("123456789012345678901234567890");
        Int b("987654321098765432109876543210");
        std::cout << "  a + b   = " << (a + b).toString() << "\n";
        std::cout << "  a * b   = " << (a * b).toString() << "\n";
        std::cout << "  b / a   = " << (b / a).toString() << "\n";
        std::cout << "  b % a   = " << (b % a).toString() << "\n";
        std::cout << "  2^100   = " << pow(Int(2), 100u).toString() << "\n";
    }

    // ---- IntOps: divmod / floorDiv / ceilDiv ---------------------
    {
        std::cout << "[IntOps divmod / floorDiv / ceilDiv]\n";
        Int a(17), b(5);
        Int rem;
        Int q = IntOps::divmod(a, b, rem);
        std::cout << "  17 / 5  = " << q.toString() << " rem " << rem.toString() << "\n";
        std::cout << "  floorDiv(-7, 3) = " << IntOps::floorDiv(Int(-7), Int(3)).toString() << "\n";
        std::cout << "  ceilDiv(7, 3)   = " << IntOps::ceilDiv(Int(7), Int(3)).toString() << "\n";
    }

    // ---- GCD / LCM -----------------------------------------------
    {
        std::cout << "[gcd / lcm]\n";
        Int a(48), b(36);
        std::cout << "  gcd(48, 36) = " << gcd(a, b).toString() << "  (= 12)\n";
        std::cout << "  lcm(48, 36) = " << lcm(a, b).toString() << "  (= 144)\n";
    }

    // ---- modular: powerMod + inverseMod --------------------------
    {
        std::cout << "[modular]\n";
        Int b(7), e(20), m(13);
        std::cout << "  7^20 mod 13      = " << IntModular::powerMod(b, e, m).toString() << "\n";
        Int inv = IntModular::inverseMod(Int(7), Int(11));
        std::cout << "  inverseMod(7,11) = " << inv.toString()
                  << "   (check: 7*" << inv.toString() << " mod 11 = "
                  << ((Int(7) * inv) % Int(11)).toString() << ")\n";
    }

    // ---- primes --------------------------------------------------
    {
        std::cout << "[isProbablePrime]\n";
        Int m61 = pow(Int(2), 61u) - Int(1);   // Mersenne prime M_61
        std::cout << "  isProbablePrime(2^61-1) = " << std::boolalpha
                  << isProbablePrime(m61) << "  (Mersenne M_61, expected true)\n";
        Int m61_minus2 = pow(Int(2), 61u) - Int(3);
        std::cout << "  isProbablePrime(2^61-3) = "
                  << isProbablePrime(m61_minus2) << "\n";
    }

    // ---- factorial / binomial ------------------------------------
    {
        std::cout << "[combinatorics]\n";
        std::cout << "  20! = " << IntCombinatorics::factorial(Int(20)).toString() << "\n";
        std::cout << "  C(10,3) = " << IntCombinatorics::binomial(Int(10), Int(3)).toString()
                  << "  (expected 120)\n";
    }

    // ---- sqrt (integer square root) ------------------------------
    {
        std::cout << "[IntSqrt::sqrt]\n";
        Int big = pow(Int(10), 40u);   // 10^40
        std::cout << "  sqrt(10^40) = " << IntSqrt::sqrt(big).toString() << "  (= 10^20)\n";
    }

    // ---- base-2 / base-16 string ---------------------------------
    {
        std::cout << "[base conversion]\n";
        Int big("18446744073709551617");  // 2^64 + 1
        std::cout << "  2^64+1 hex = " << big.toString(16) << "\n";
        std::cout << "  2^64+1 bin (head) = "
                  << big.toString(2).substr(0, 20) << "...\n";
    }

    return 0;
}
