GitHub

@@ -2390,6 +2390,155 @@ static const mp_limb base_limit[34*2] = {

23902390

#endif

23912391

};

239223922393+

/*

2394+

* Divide-and-conquer decimal string conversion.

2395+

* For numbers with > DC_TO_S_THRESHOLD digits, this is O(n log^2 n)

2396+

* instead of O(n^2) for the simple algorithm.

2397+

*/

2398+

#define DC_TO_S_THRESHOLD 1000

2399+2400+

/*

2401+

* Recursive D&C conversion helper.

2402+

* Converts x to decimal string, writing exactly num_digits characters.

2403+

* The caller must ensure num_digits >= actual digits in x.

2404+

* Leading zeros are added if x has fewer digits than num_digits.

2405+

*/

2406+

static void

2407+

mpz_to_s_dc_rec(mpz_ctx_t *ctx, char *s, mpz_t *x, size_t num_digits,

2408+

mpz_t *powers, size_t num_powers)

2409+

{

2410+

/* Base case: use simple conversion for small numbers */

2411+

if (num_digits <= DC_TO_S_THRESHOLD || num_powers == 0) {

2412+

/* Convert to string in reverse order first */

2413+

size_t pos = num_digits;

2414+

mpz_t tmp;

2415+

mpz_init_set(ctx, &tmp, x);

2416+2417+

while (pos > 0 && !zero_p(&tmp)) {

2418+

mpz_t q;

2419+

mpz_init_heap(ctx, &q, tmp.sz);

2420+

mp_dbl_limb r = 0;

2421+2422+

/* Divide by 10 */

2423+

for (size_t i = tmp.sz; i > 0; i--) {

2424+

r = (r << DIG_SIZE) | tmp.p[i-1];

2425+

q.p[i-1] = (mp_limb)(r / 10);

2426+

r %= 10;

2427+

}

2428+

q.sz = tmp.sz;

2429+

q.sn = tmp.sn;

2430+

trim(&q);

2431+2432+

s[--pos] = '0' + (char)r;

2433+

mpz_set(ctx, &tmp, &q);

2434+

mpz_clear(ctx, &q);

2435+

}

2436+2437+

/* Fill remaining positions with zeros */

2438+

while (pos > 0) {

2439+

s[--pos] = '0';

2440+

}

2441+2442+

mpz_clear(ctx, &tmp);

2443+

return;

2444+

}

2445+2446+

/* Find appropriate power of 10 to split on */

2447+

/* We want the largest power that gives roughly half the digits */

2448+

size_t split_idx = 0;

2449+

size_t split_digits = 1;

2450+

for (size_t i = 0; i < num_powers; i++) {

2451+

size_t d = (size_t)1 << i; /* digits for this power */

2452+

if (d * 2 <= num_digits) {

2453+

split_idx = i;

2454+

split_digits = d;

2455+

}

2456+

}

2457+2458+

/* Split: x = hi * 10^split_digits + lo */

2459+

mpz_t hi, lo;

2460+

mpz_init(ctx, &hi);

2461+

mpz_init(ctx, &lo);

2462+2463+

mpz_mdivmod(ctx, &hi, &lo, x, &powers[split_idx]);

2464+

lo.sn = (lo.sn < 0) ? -lo.sn : lo.sn; /* lo is always positive */

2465+2466+

/* Recursively convert high part */

2467+

size_t hi_digits = num_digits - split_digits;

2468+

mpz_to_s_dc_rec(ctx, s, &hi, hi_digits, powers, split_idx);

2469+2470+

/* Recursively convert low part (exactly split_digits digits with padding) */

2471+

mpz_to_s_dc_rec(ctx, s + hi_digits, &lo, split_digits, powers, split_idx);

2472+2473+

mpz_clear(ctx, &hi);

2474+

mpz_clear(ctx, &lo);

2475+

}

2476+2477+

/*

2478+

* D&C decimal string conversion entry point.

2479+

* Returns pointer to start of string (after optional sign).

2480+

*/

2481+

static char*

2482+

mpz_to_s_dc(mpz_ctx_t *ctx, char *s, mpz_t *x)

2483+

{

2484+

mrb_state *mrb = MPZ_MRB(ctx);

2485+2486+

/* Handle sign */

2487+

char *result = s;

2488+

if (x->sn < 0) {

2489+

*s++ = '-';

2490+

}

2491+2492+

/* Calculate number of decimal digits needed */

2493+

/* Use log10(2) ≈ 0.30103, so bits * 0.30103 + 1 gives upper bound */

2494+

size_t bits = digits(x) * DIG_SIZE;

2495+

size_t num_digits = (size_t)(bits * 30103UL / 100000UL) + 2;

2496+2497+

/* Build table of powers of 10: 10^1, 10^2, 10^4, 10^8, ... */

2498+

size_t max_powers = 32; /* Enough for 10^(2^32) which is huge */

2499+

mpz_t *powers = (mpz_t*)mrb_malloc(mrb, max_powers * sizeof(mpz_t));

2500+

size_t num_powers = 0;

2501+2502+

/* 10^1 */

2503+

mpz_init(ctx, &powers[0]);

2504+

mpz_set_int(ctx, &powers[0], 10);

2505+

num_powers = 1;

2506+2507+

/* Build powers by squaring: 10^(2^k) = (10^(2^(k-1)))^2 */

2508+

while (num_powers < max_powers) {

2509+

size_t power_digits = (size_t)1 << num_powers;

2510+

if (power_digits > num_digits) break;

2511+2512+

mpz_init(ctx, &powers[num_powers]);

2513+

mpz_sqr(ctx, &powers[num_powers], &powers[num_powers - 1]);

2514+

num_powers++;

2515+

}

2516+2517+

/* Make a copy of x for conversion (to preserve original) */

2518+

mpz_t tmp;

2519+

mpz_init_set(ctx, &tmp, x);

2520+

tmp.sn = 1; /* Work with absolute value */

2521+2522+

/* Do the recursive conversion */

2523+

mpz_to_s_dc_rec(ctx, s, &tmp, num_digits, powers, num_powers);

2524+2525+

/* Clean up powers table */

2526+

for (size_t i = 0; i < num_powers; i++) {

2527+

mpz_clear(ctx, &powers[i]);

2528+

}

2529+

mrb_free(mrb, powers);

2530+

mpz_clear(ctx, &tmp);

2531+2532+

/* Remove leading zeros (but keep at least one digit) */

2533+

char *p = s;

2534+

while (*p == '0' && *(p+1) != '\0') p++;

2535+

if (p > s) {

2536+

memmove(s, p, strlen(p) + 1);

2537+

}

2538+2539+

return result;

2540+

}

2541+23932542

static char*

23942543

mpz_get_str(mpz_ctx_t *ctx, char *s, mrb_int sz, mrb_int base, mpz_t *x)

23952544

{

@@ -2443,6 +2592,12 @@ mpz_get_str(mpz_ctx_t *ctx, char *s, mrb_int sz, mrb_int base, mpz_t *x)

24432592

mrb_raise(mrb, E_RUNTIME_ERROR, "bigint size too large for string conversion");

24442593

}

244525942595+

/* Use D&C algorithm for large base-10 numbers */

2596+

size_t est_digits = (size_t)(xlen * DIG_SIZE * 30103UL / 100000UL) + 2;

2597+

if (base == 10 && est_digits > DC_TO_S_THRESHOLD) {

2598+

return mpz_to_s_dc(ctx, s, x);

2599+

}

2600+24462601

mp_limb *t = (mp_limb*)mrb_malloc(mrb, xlen * sizeof(mp_limb));

2447260224482603

mp_limb *tend = t + xlen;

Read the original on github.com ↗