/* $Id: t_dibigdBasic.cpp $ */

/*
* Copyright (C) 2026 David Ireland, D.I. Management Services Pty Limited
* <https://di-mgt.com.au/contact/> <https://di-mgt.com.au/bigdigits.html>
* SPDX-License-Identifier: MPL-2.0
*
* Last updated:
* $Date: 2026-05-05 02:02 $
* $Revision: 1.0.1 $
* $Author: dai $
*/

/* Some basic tests using dibigd C++ OOP interface to BigDigits. */

#ifdef NDEBUG
#undef NDEBUG
#endif
#include <iostream>
#include <stdexcept>
#include <typeinfo>
#include <assert.h>
#include <time.h>
#include "dibigd.hpp"
using std::cout;
using std::endl;
using namespace dibigd;


/* TESTS */

void test_version_info() {
    cout << "CompileTime=[" << dibigd::compile_time() << "]" << endl;
    cout << "This C++ interface version is " << dibigd::version_interface() << endl;
    cout << "using BigDigits core version " << dibigd::version_core() << endl;
}

void test_arithmetic() {
    cout << endl << "Testing arithmetic..." << endl;
    bool f;
    // Assign two BigDigit objects
    BigDigit bd1(666);
    bd1.print("bd1=");
    BigDigit bd2(333);
    bd2.print("bd2=");

    // Add them
    BigDigit sum;
    sum = bd1 + bd2;
    sum.print("sum=");

    // Copy a BigDigit object
    BigDigit bd3;
    bd3 = bd1;
    bd3.print();
    cout << "bd3 is " << bd3.to_str() << endl;

    // Add an integer (maybe negative)
    bd3 = bd3 + 5;
    bd3.print("bd3+5=");
    bd3 = -5 + bd3;
    bd3.print("-5+bd3=");
    bd3 = bd3 - 5;
    bd3.print("bd3-5=");

    sum = bd1 + bd2 + bd3;
    sum.print("sum=");

    bd3 = 0x1000;
    bd3.print("bd3=");
    bd3 = 1 << 24;
    bd3.print("bd3=");
    cout << "bd3 in hex is " << bd3.to_strhex() << endl;

    BigDigit product = bd1 * bd2;
    product.print("product=");


    bd1 = 10;
    bd1.print("bd1=");
    sum = bd1 - 4;
    sum.print("bd1-4=");
    sum = bd1 - -4;
    sum.print("bd1--4=");
    sum = 21 - bd1;
    sum.print("21-bd1=");

    BigDigit a = 0x10000;
    BigDigit b = 0x100;
    sum = a / b;
    sum.print("a/b=");

    a.print("a=");
    b.print("b=");
    a += b;
    a.print("a+=b=");
    a += 10;
    a.print("a+=b=");
    a += 0;
    a.print("a+=0=");


    /* Test multiply equal and divide equal */
    a = 0x1000;
    b = 0x100;
    a.print("a=");
    b.print("b=");
    a *= b;
    a.print("a*=b=");
    a *= b;
    a.print("a*=b=");
    a /= b;
    a.print("a/=b=");
    // Increment prefix and postfix
    a.print("a=");
    b = ++a;
    a.print("++a=");
    b.print("++a returns ");
    b = a++;
    a.print("a++=");
    b.print("a++ returns ");
    a.print("Before decrement a=");
    b = --a;
    a.print("--a=");
    b.print("--a returns ");
    b = a--;
    a.print("a--=");
    b.print("a-- returns ");
    // Equality operator
    f = (a == b);
    cout << std::boolalpha << "a==b returns " << f << endl;
    f = (a == a);
    cout << std::boolalpha << "a==a returns " << f << endl;
    a = 666;
    f = (a == 666);
    cout << std::boolalpha << "a==666 returns " << f << endl;
    f = (666 == a);
    cout << std::boolalpha << "666==a returns " << f << endl;
    // Constant-time comparisons
    cout << "Constant-time comparisons..." << endl;
    a = 1000;
    b = 999;
    a.print("a="); b.print("b=");
    int cmp;
    cmp = a.cmp_ct(b);
    cout << "a.cmp_ct(b) returns " << std::showpos << cmp << endl;
    assert(cmp > 0);
    cmp = b.cmp_ct(a);
    cout << "b.cmp_ct(a) returns " << std::showpos << cmp << endl;
    assert(cmp < 0);
    cmp = a.cmp_ct(1000);
    cout << "a.cmp_ct(1000) returns " << std::showpos << cmp << endl;
    assert(cmp == 0);
    BigDigit Z; // Set as zero
    cmp = Z.cmp_ct(0);
    cout << "Z.cmp_ct(0) returns " << std::showpos << cmp << endl;
    assert(cmp == 0);


    // Overflow digit boundary
    a = 0xffffffff;
    b = 0xffffffff;
    a.printhex("a=");
    b.printhex("b=");
    sum = a + b;
    sum.printhex("a+b="); // 0x1fffffffe

    // Modulo operator
    a = 101;
    b = a % 9;
    cout << a.to_str();
    b.print(" % 9=");
    a %= 9;
    a.print("a %= 9=");

    a = 0x10000001;
    cout << a.to_str() << " is " << (a.is_even() ? "even" : "odd") << endl;
    cout << a.to_str() << " is " << (a.is_odd() ? "odd" : "even") << endl;
    a--;
    cout << a.to_str() << " is " << (a.is_even() ? "even" : "odd") << endl;
    cout << a.to_str() << " is " << (a.is_odd() ? "odd" : "even") << endl;


}

void test_mod_arithmetic() {
    BigDigit u, v, w, m, x, y, p, q, r, g;
    int jac;
    size_t nbits;
    cout << endl << "Test modular arithmetic..." << endl;

    // Generate two random primes
    cout << "Generate two random primes..." << endl;
    p.set_prime(128);
    q.set_prime(128);
    p.printhex("p=");
    q.printhex("q=");
    // Check primality
    cout << "p is " << (p.is_prime() ? "prime (OK)" : "NOT PRIME") << endl;
    cout << "q is " << (q.is_prime() ? "prime (OK)" : "NOT PRIME") << endl;
    //  w = product pq 
    w = p * q;
    w.printhex("pq=");
    // Product pq should not be prime
    cout << "pq is " << (w.is_prime() ? "prime" : "NOT PRIME (OK)") << endl;
    assert(!w.is_prime());
    // Check GCD: gcd(pq, p) == p
    g = w.gcd(p);
    g.printhex("gcd(pq,p)=");
    assert(g == p);
    // Alt way to call gcd()
    g = BigDigit::gcd(w, p);
    g.printhex("gcd(pq,p)=");
    assert(g == p);

    cout << "If p,q,r are distinct primes, then the gcd of u=pq and v=pr is p." << endl;
    p.set_prime(128);
    q.set_prime(128);
    r.set_prime(128);
    p.printhex("p="); q.printhex("q="); r.printhex("r=");
    u = p * q;
    v = p * r;
    u.printhex("u=pq="); v.printhex("v=pr=");
    g = BigDigit::gcd(u, v);
    g.printhex("gcd(u,v)=");
    assert(g == p);

    v = 0;
    cout << endl << "MODULAR INVERSION..." << endl;
    cout << "If p is a prime, and u=pm-1 for some integer m, then w, the inverse of u modulo p, equals p-1 and wu mod p equals one." << endl;
    p.printhex("p=");
    // set m to be a small random multiplier
    m.set_rand_number(0xffff);
    m.printhex("multiplier, m=");
    // Set u = pm - 1
    u = m * p - 1;
    u.printhex("u = pm-1 =");
    // compute w = u^-1 mod p
    w = u.mod_inv(p);
    w.printhex("w = u^-1 mod p=");
    // check that wu mod p == 1
    x = w.mod_mult(u, p);
    x.print("wu mod p=");
    assert(x == 1);    // Expecting 1

    cout << "Try mod inversion that should fail (expected result == 0)..." << endl; 
    // Set u = pq so that gcd(u, p) != 1
    u = p * q;
    w = u.mod_inv(p);
    w.print("(pq)^-1(mod p)=");
    assert(w == 0);

    cout << "Add and subtract modulo m..." << endl;
    // mod_sub catches the case where v > u and returns u + p - v
    u = 6;
    v = 7;
    m = 10;
    cout << "u=" << u.to_str() << ", v=" << v.to_str() << ", m=" << m.to_str() << endl;
    w = u.mod_sub(v, m);
    w.print("w=u-v(mod m)=");
    w = w.mod_add(v, m);
    w.print("w=w+v(mod m)=");
    assert(w == u);

    // Select a 192-bit prime number
    p = "0xfffffffffffffffffffffffffffffffeffffffffffffffff";
    p.printhex("p=");
    assert(p.is_prime());
    nbits = p.bitlen();
    cout << "p is " << nbits << " bits and is " << (p.is_prime() ? "prime" : "not prime") << endl;

    // Pick two numbers just smaller than p with v > u
    u.set_rand_bits(nbits - 2);
    v.set_rand_bits(nbits - 1);
    cout << "v > u is " << std::boolalpha << (v > u) << endl;
    u.printhex("u=");
    v.printhex("v=");
    w = u.mod_add(v, p);
    w.printhex("w=u+v(mod p)=");
    // Subtract with borrow u - v < 0
    w = u.mod_sub(v, p);
    w.printhex("w=u-v(mod p)=");
    // Check: add back v modulo p
    w = w.mod_add(v, p);
    w.printhex("w=w+v(mod p)=");
    assert(w == u);


    v <<= 8;  // Set v' to be greater than p
    v.printhex("v'=");
    cout << "v' is " << (v > p ? "greater" : "less") << " than p" << endl;
    w = u.mod_add(v, p);
    w.printhex("w=u+v(mod p)=");
    w = u.mod_sub(v, p);
    w.printhex("w=u-v(mod p)=");
    // Check: add back v modulo p
    w = w.mod_add(v, p);
    w.printhex("w=w+v(mod p)=");
    cout << "w is " << (w == u ? "equal to u (OK)" : "NOT equal to u (NBG!!)") << endl;
    assert(w == u);

    // Test mod_sqrt
    cout << "MODULAR SQUARE ROOT..." << endl;
    u.printhex("u=");
    // Set w = u * u (mod p)
    w = u.mod_square(p);
    w.printhex("w = u^2(mod p) = ");
    /* Check we have a quadratic residue */
    jac = w.jacobi(p);
    cout << "Legendre symbol (w|p)=" << jac << " (expected 1)" << endl;
    /* Compute one modular square root */
    x = w.mod_sqrt(p);
    x.printhex("x=sqrt(w)(mod p)=");
    /* and the other */
    y = p - x;
    y.printhex("other root y=p-x=");
    assert((x == u) || (y == u));
    ((x*x)%p).printhex("x*x%p=");
    ((y*y) % p).printhex("y*y%p=");

    cout << endl << "Find a number that is not a quadratic residue mod p..." << endl;
    do {
        v.set_rand_bits(nbits);
    } while (v.jacobi(p) != -1);
    v.printhex("v=");
    cout << "Legendre symbol (v|p)=" << v.jacobi(p) << " (expected -1)" << endl;
    x = v.mod_sqrt(p);
    x.printhex("v.mod_sqrt(p) returns ");
    cout << "(0 ==> square root does not exist)" << endl;
    assert(x == 0);

    cout << "ADD AND SUBTRACT MODULO M ..." << endl;
    p.printhex("p=");
    /* Add two random numbers w = u + v mod p */
    u.set_rand_bits(nbits - 1);
    v.set_rand_bits(nbits - 1);
    u.printhex("u=");
    v.printhex("v=");
    // w = u + v (mod p)
    w = u.mod_add(v, p);
    w.printhex("w=u+v(mod p)=");
    // y = w - v (mod p)
    y = w.mod_sub(v, p);
    y.printhex("y=w-v(mod p)=");
    // Check y == u
    assert(y == u);
    cout << "Divide an integer by 2 modulo p using mod_halve()..." << endl;
    u.printhex("u=");
    w = u.mod_halve(p);
    w.printhex("w=u/2(mod p)=");
    // Check result
    v = w.mod_add(w, p);
    v.printhex("v=w+w(mod p)=");
    assert(u == v);
}

void test_exceptions() {
    cout << endl << "Test exceptions..." << endl;
    // cope with negative result
    BigDigit a(2);
    BigDigit b(5);
    try {
        cout << "About to compute a negative number..." << endl;
        BigDigit sum = a - b;    // A negative result
        sum.print("sum=");
    }
    catch (const std::runtime_error& e) {
        std::cerr << typeid(e).name() << ": " << e.what() << std::endl;
    }

    // divide by zero
    BigDigit Z;
    try {
        cout << "About to divide by zero..." << endl;
        BigDigit quotient = a / Z;
        quotient.print("quotient=");
    }
    catch (const std::runtime_error& e) {
        std::cerr << typeid(e).name() << ": " << e.what() << std::endl;
    }

    // Pass an invalid string 
    try {
        cout << "Passing an invalid hex string..." << endl;
        BigDigit X = "0xabcG123";
    }
    catch (const std::invalid_argument& e) {
        std::cerr << typeid(e).name() << ": " << e.what() << std::endl;
    }
}

void test_bits() {
    cout << endl << "Testing bit related operations..." << endl;
    BigDigit u, v, w, p, q, r;
    w = 0xfffffffe;
    w.printhex("w=");
    cout << "bitlen(w)=" << w.bitlen() << endl;
    u = w << 2;
    u.printhex("w << 2 = ");
    assert(u.getbit(33) == 1);
    v = u >> 3;
    v.printhex("(w << 2) >> 3 = ");
    assert(v.getbit(31) == 0);
    /* Bigger shift */
    u = w << 128;
    u.printhex("w << 128 = ");
    assert(u.getbit(159) == 1);
    v = u >> 129;
    v.printhex("(w << 128) >> 129 = ");
    assert(v.getbit(31) == 0);

    w = "0b00110101011";
    w.printbits("w=    ");
    // Shift Assign
    w >>= 2;
    w.printbits("w>>=2:");
    w <<= 2;
    w.printbits("w<<=2:");
    w >>= 3;
    w.printbits("w>>=3:");

    /* Use XOR to swop two variables without a temp variable, i.e.
    a = a XOR b; b = a XOR b; a = a XOR b; */
    /* Generate 2 random numbers (u,w) each 144 bits long */
    u.set_rand_bits(144);
    w.set_rand_bits(144);
    u.printhex("u=");
    w.printhex("w=");
    /* remember these for later (p=u, q=w) */
    p = u;
    q = w;
    /* Swop using XOR */
    cout << "Swopping u and w using XOR" << endl;
    u = u ^ w;
    w = u ^ w;
    u = u ^ w;
    u.printhex("u=");
    w.printhex("w=");
    /* check they have swopped accurately */
    assert(u == q);
    assert(w == p);
    // Using XOR Assign
    u ^= w;
    u.printhex("u^=w:");

    // Invert all bits
    cout << "Bitwise NOT..." << endl;
    p = 0xfffe;
    p <<= 32;
    p = p + 0xfffe;
    p.printhex("p=        ");  //     fffe0000fffe
    // Bitwise NOT - NB up to the most significant *digit*
    q = ~p;
    q.printhex("NOT p=");  // ffff0001ffff0001
    u = p & q;
    u.printhex("p AND (NOT p)=");  // 0
    assert(u == 0);
    // Flip bits in p only up to the most significant *bit*
    p = "0x1ffff5ff9";
    p.printhex("p=");
    p.printbits();
    cout << "bitlen(p)=" << p.bitlen() << endl;
    q = (~p);
    q.printhex("NOT p=");
    q.printbits("NOT p=");
    q = q.mod_powerof2(p.bitlen());
    q.printhex("(NOT p)(mod 2^" + std::to_string(p.bitlen()) + ")=");  
    // In one action
    q = (~p).mod_powerof2(p.bitlen());
    q.printhex("(NOT p)(mod 2^" + std::to_string(p.bitlen()) + ")=");

    // Some simple AND and OR ops
    cout << "Set every even bit in u and every odd bit in v..." << endl;
    u = 0;
    v = 0;
    for (int i = 0; i < 144 / 2; i++) {
        u.setbit((i * 2), 1);
        v.setbit((i * 2) + 1, 1);
    }
    u.printbits("u=");
    v.printbits("v=");
    w = u & v;
    w.print("u AND v=");
    w = u | v;
    w.printhex("u OR v =");

    // Check that setbit returns the value it set
    u = "0xffffffffffff";
    u.printhex("u=");
    w = u.setbit(10, 0);
    cout << "Set bit 10 to zero" << endl;
    u.printhex("u=");
    w.printhex("w=");
    assert(w == u);


    /* Compose a 400-bit string by concatenating random 160-bit sections
    and then truncating the result */
    cout << endl << "Bit string concatenation and truncation:" << endl;
    /*
    ALGORITHM: (based on a similar one in RFC 2631/ANSI X.42)
    Set m' = m/160 where / represents integer division with rounding upwards
    Set U = 0
    For i = 0 to m' - 1
      Set R = FUNC_160 and V = FUNC_160 where FUNC_160 generates a unique 160-bit value
      U = U + (R XOR V) * 2^(160 * i)
    Form q from U by computing U mod (2^m) and setting the most
    significant bit (the 2^(m-1) bit) and the least significant bit to 1.
    In terms of boolean operations, q = U OR 2^(m-1) OR 1.
    Note that 2^(m-1) < q < 2^m
    */
    /* In this example, m=400 and m'=3, and we just generate 160-bit random values to demonstrate */
    int m, mc;
    m = 400;
    mc = (m + 160 - 1) / 160;  // m'
    u = 0;
    for (int i = 0; i < mc; i++) {
        r.set_rand_bits(160);
        v.set_rand_bits(160);
        // U = U + (R XOR V) * 2 ^ (160 * i)
        r.printhex("r=");
        v.printhex("v=");
        u += (r ^ v) << (160 * i);
        /* * 2^160i <=> shift left by 160i bits */
        u.printhex("u=");
    }
    u = u.mod_powerof2(m); /* U = U mod (2^m) */
    u.printhex("u mod (2^m)=");
    /* q = U OR 2^(m-1) OR 1 */
    q = u;
    q.setbit(m - 1, 1);
    q.setbit(0, 1);
    q.printhex("q=u OR 2^(m-1) OR 1=");
    cout << "bitlen(q)=" << q.bitlen() << endl;
    /* Check that 2^(m-1) < q < 2^m */
    p = 0;
    p.setbit(m - 1, 1);    /* p = 2^(m-1) */
    assert(p < q);
    r = 0;
    r.setbit(m, 1);        /* r = 2^m */
    assert(q < r);
    cout << "OK, checked that 2^(m-1) < q < 2^m\n" << endl;
}

void test_functions() {
    cout << endl << "Testing functions..." << endl;

    BigDigit a, b, pow, g, n, s;
    a = 0x1000 * 0x1000;
    a.printhex("a=");
    b = a.sqrt();
    b.printhex("a.sqrt()="); // 0x1000

    // Set a = (0x10000)^3
    a = 0x10000;
    a = a * a * a;
    /* NB a = 0x10000 * 0x10000 * 0x10000; // will fail (because > UINT32_MAX) 
    warning C4307: '*' : integral constant overflow
    */
    a.printhex("a=");
    b = a.cbrt();
    b.printhex("a.cbrt()=");  // 0x10000

    a = 0x1000;
    a.printhex("a=");
    pow = a.pow(10);
    pow.printhex("a^10="); // 0x1000000000000000000000000000000

    s = a.square();
    s.printhex("a^2=");

    a = 0x1000;
    b = 0x1001;
    g = BigDigit::gcd(a * 3, b * 3);
    g.print("gcd="); // 3

    {
        BigDigit g = BigDigit::gcd(10, 25, 30, 50);
        g.print("gcd=");  // 5
    }

    g = BigDigit::gcd(17 * 31, 17 * 59, 17 * 71, 17 * 101);
    g.print("gcd(527,1003,1207,1717)=");  // 17
    g = BigDigit::gcd(13 * 31, 13 * 59);
    g.print("gcd()=");  // 13

    g = a.gcd(b);
    g.printhex("a.gcd(b)=");

    // Jacobi - Use numbers from X9.31 Appendix D.5.2
    BigDigit aa("0x6BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
        "BBBBBBBBBBBAA9993E364706816ABA3E25717850"
        "C26C9CD0D89D33CC");
    BigDigit nn("0xCCD34C2F4D95FFAD1420E666C07E39D1450A1330"
        "4C3F5891EDE57595C772A3691AB51D2BECE1476B"
        "8F22AE223365F183BC3EE2D4CACDBA3AD0C4D478"
        "1C523A10EFE6203D6F3BC226BF9A459727B8F122"
        "C482D8C86019F9A869329187096430A6C67CB103"
        "742BCBC66906AD23836EBABB511D5D80AB8CB599"
        "74E9AAC62D785C45");
    aa.printhex("aa=");
    nn.printhex("nn=");
    int j;
    j = aa.jacobi(nn);
    cout << "Jacobi(a/n)=" << j << " expected -1" << endl;
    assert(j == -1);
    // Divide a by 2
    aa /= 2;
    aa.printhex("aa=");
    j = aa.jacobi(nn);
    cout << "Jacobi(a/n)=" << j << " expected +1" << endl;
    // Make n|a
    nn = aa * 7;
    j = aa.jacobi(nn);
    cout << "Jacobi(a/n)=" << j << " expected 0" << endl;
    aa = 2183;
    nn = 9907;
    j = aa.jacobi(nn);
    cout << "Jacobi(" << aa.to_str() << "|" << nn.to_str() << ")=" << j << " expected 1" << endl;
    assert(j == 1);
    aa = 1001;
    j = aa.jacobi(nn);
    cout << "Jacobi(" << aa.to_str() << "|" << nn.to_str() << ")=" << j << " expected -1" << endl;
    assert(j == -1);
    aa = 10000 * 9907;
    j = aa.jacobi(nn);
    cout << "Jacobi(" << aa.to_str() << "|" << nn.to_str() << ")=" << j << " expected 0" << endl;
    assert(j == 0);
}


void test_from_str() {
    cout << endl << "Test from_str() and constructor initialization" << endl;
    // Use from_str() function
    BigDigit a = from_str("0xdeadbeefcafebabe");
    BigDigit b = from_str("16045690984503098046");
    BigDigit c = from_str("0b1101111010101101101111101110111111001010111111101011101010111110");
    a.printhex("a="); b.printhex("b="); c.printhex("c=");
    // a=0xdeadbeefcafebabe
    // b=0xdeadbeefcafebabe
    // c=0xdeadbeefcafebabe

    // Assign using string directly [v1.0.1]
    a = "0xdeadbeefcafebabe";
    b = "16045690984503098046";
    c = "0b1101111010101101101111101110111111001010111111101011101010111110"; 
    a.printhex("a="); b.printhex("b="); c.printhex("c=");

    // Use BigDigit constructor
    BigDigit A("0xdeadbeefcafebabe");
    BigDigit B("16045690984503098046");
    BigDigit C("0b1101111010101101101111101110111111001010111111101011101010111110");
    A.printhex("A="); B.printhex("B="); C.printhex("C=");
    // A=0xdeadbeefcafebabe
    // B=0xdeadbeefcafebabe
    // C=0xdeadbeefcafebabe

}

void test_octets() {
    cout << endl << "Test from_octets and to_octets" << endl;
    // Decode from byte array
    std::vector<unsigned char> bv = { 0xde, 0xad, 0xbe, 0xef };
    BigDigit b = from_octets(bv);
    b.printhex("b=");
    // Check we can do arithmetic on the resulting BigDigit
    (--b).printhex("--b=");
    // Encode from BigDigit
    std::vector<unsigned char> barr = b.to_octets();
    Bvec::print_hex("to_octets: ", barr);
    {  // For documentation
        BigDigit b(0xdeadbeef);
        std::vector<unsigned char> barr = b.to_octets();
        Bvec::print_hex(barr); // de ad be ef
        Bvec::print_hex(barr, ""); // deadbeef
        Bvec::print_hex(barr, ":"); // de:ad:be:ef
        // Specify exact length
        barr = b.to_octets(6);
        Bvec::print_hex(barr); // 00 00 de ad be ef
    }
    BigDigit s("0x103808afb0db2fd4abff6af4149f51b");
    s.printhex("s in network order:  ");
    // Set s as 128-bit little-endian number
    s = s.reverse_octets(128 / 8);
    s.printhex("s as 128-bit number: ");
    // 0x1bf54941aff6bf4afdb20dfb8a800301

}

void test_RSA508() {
    cout << endl << "Test RSA computations using 508-bit RSA key from 'Some Examples of the PKCS Standards'" << endl;
    BigDigit n("0x0A66791DC6988168DE7AB77419BB7FB0C001C62710270075142942E19A8D8C51D053B3E3782A1DE5DC5AF4EBE99468170114A1DFE67CDC9A9AF55D655620BBAB");
    BigDigit e("0x010001");
    BigDigit m("0x0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020205000410DCA9ECF1C15C1BD266AFF9C8799365CD");
    BigDigit d("0x0123C5B61BA36EDB1D3679904199A89EA80C09B9122E1400C09ADCF7784676D01D23356A7D44D6BD8BD50E94BFC723FA87D8862B75177691C11D757692DF8881");
    m.printhex("m =");
    // Sign, i.e. Encrypt with private key, s = m^d mod n
    BigDigit s = m.mod_exp(d, n);
    s.printhex("s =");
    BigDigit s1("0x06DB36CB18D3475B9C01DB3C789528080279BBAEFF2B7D558ED6615987C851863F8A6C2CFFBC89C3F75A18D96B127C717D54D0D8048DA8A0544626D17A2A8FBE");
    s1.printhex("ok=");
    assert(s == s1);
    /* Verify, i.e. Decrypt with public key m' = s^e mod n */
    BigDigit m1 = s.mod_exp(e, n);
    m1.printhex("m1=");
    m.printhex("ok=");
    assert(m == m1);

    // Same again using constant time variant...
    cout << "Using constant time..." << endl;
    s = m.mod_exp_ct(d, n);
    s.printhex("s =");
    assert(s == s1);
    /* Verify, i.e. Decrypt with public key m' = s^e mod n */
    m1 = s.mod_exp_ct(e, n);
    m1.printhex("m1=");
    assert(m == m1);

}

void test_bvec() {
    cout << endl << "Test Bvec functions..." << endl;
    std::vector<unsigned char> bv;
    bv = Bvec::from_hex("DEADBEEF");
    Bvec::print_hex(bv);
    bv = Bvec::from_hex("00DEADBEEF");
    Bvec::print_hex(bv);
    bv = Bvec::from_hex("000000DEADBEEF");
    Bvec::print_hex(bv);
    bv = Bvec::from_hex("0xDEADBEEF");
    Bvec::print_hex(bv, "");
    bv = Bvec::from_hex("0X0000DEADBEEF");
    Bvec::print_hex(bv, "");

}

void do_test() {
    test_version_info();
    test_arithmetic();
    test_exceptions();
    test_mod_arithmetic();
    test_functions();
    test_bits();
    test_RSA508();
    test_from_str();
    test_octets();
    test_bvec();
}


int main() {
    /* MSVC memory leak checking stuff */
#if _MSC_VER >= 1100
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
#endif

    /* Catch any exceptions */
    try {
        do_test();
    }
    catch (const std::exception& e) {
        // Handle standard exceptions with a specific message
        std::cerr << "Standard exception: " << e.what() << std::endl;
    }
    catch (...) {
        std::cerr << "Caught unknown exception." << std::endl;
    }

    cout << endl << "ALL DONE." << endl << endl;

    return 0;
}