ex_gmp_factorials.c
#define EX_UTILS_NO_USE_INPUT 1
#define EX_UTILS_NO_USE_OPEN 1
#include "ex_utils.h"
#include <limits.h>
#include <gmp.h>
#include <locale.h>
#define EX_GMP_FACT_USE_FIELDWIDTH 1
#define EX_GMP_FACT_USE_REFS 1
static int ex__usr_mpz_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *spec,
const mpz_t val)
{
int flags = VSTR_FLAG_SC_FMT_CB_BEG_OBJ_NUM;
size_t len = 0;
int ret = FALSE;
char ui_buf[sizeof(unsigned long) * CHAR_BIT];
char *buf = NULL;
char *out_buf = ui_buf;
if (mpz_sgn(val) == -1)
flags |= VSTR_FLAG_SC_FMT_CB_BEG_OBJ_NEG;
if (mpz_fits_ulong_p(val))
len = vstr_sc_conv_num10_ulong(ui_buf, sizeof(ui_buf), mpz_get_ui(val));
else
{
len = mpz_sizeinbase(val, 10);
out_buf = buf = mpz_get_str(NULL, 10, val);
if (mpz_sgn(val) == -1) ++out_buf;
if (!out_buf[len - 1]) --len;
}
ASSERT(strlen(out_buf) == len);
if (!vstr_sc_fmt_cb_beg(base, &pos, spec, &len, flags))
goto mem_fail;
if (spec->fmt_quote)
ret = vstr_sc_add_grpnum_buf(base, pos, out_buf, len);
else if (!EX_GMP_FACT_USE_REFS || !buf)
ret = vstr_add_buf(base, pos, out_buf, len);
else
{
Vstr_ref *ref = vstr_ref_make_ptr(buf, vstr_ref_cb_free_ptr_ref);
if (!ref)
goto mem_fail;
ret = vstr_add_ref(base, pos, ref, out_buf - buf, len);
buf = NULL;
vstr_ref_del(ref);
}
if (!ret || !vstr_sc_fmt_cb_end(base, pos, spec, len))
goto mem_fail;
free(buf);
return (TRUE);
mem_fail:
free(buf);
return (FALSE);
}
static int ex_usr_mpz_cb(Vstr_base *base, size_t pos, Vstr_fmt_spec *spec)
{
void *mpz = VSTR_FMT_CB_ARG_PTR(spec, 0);
return (ex__usr_mpz_cb(base, pos, spec, mpz));
}
static void ex_gmp_fact(mpz_t bignum_ret, mpz_t bignum_cnt, mpz_t bignum_for,
int out, Vstr_base *s1, int ret_max_sz, int cnt_max_sz)
{
while (mpz_cmp(bignum_cnt, bignum_for) <= 0)
{
int w_state = IO_OK;
mpz_mul(bignum_ret, bignum_ret, bignum_cnt);
if (out)
{
vstr_add_fmt(s1, s1->len, "$'*<MPZ:%*p>%s %c $'*<MPZ:%*p>\n",
cnt_max_sz, (void *)bignum_cnt, "!", '=',
ret_max_sz, (void *)bignum_ret);
if (s1->conf->malloc_bad)
errno = ENOMEM, err(EXIT_FAILURE, "Add string data");
w_state = io_put(s1, STDOUT_FILENO);
if ((w_state == IO_BLOCK) && (s1->len > EX_MAX_W_DATA_INCORE))
io_block(-1, STDOUT_FILENO);
}
mpz_add_ui(bignum_cnt, bignum_cnt, 1);
}
}
int main(int argc, char *argv[])
{
Vstr_base *s1 = ex_init(NULL);
mpz_t bignum_ret;
mpz_t bignum_for;
mpz_t bignum_cnt;
int cnt_max_sz = 1;
int ret_max_sz = 1;
const char *loc_num_name = NULL;
if (argc < 2)
errx(EXIT_FAILURE, "No count specified");
vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_FMT_CHAR_ESC, '$');
vstr_fmt_add(s1->conf, "<MPZ:%p>", ex_usr_mpz_cb,
VSTR_TYPE_FMT_PTR_VOID, VSTR_TYPE_FMT_END);
vstr_fmt_add(s1->conf, "<MPZ:%*p>", ex_usr_mpz_cb,
VSTR_TYPE_FMT_PTR_VOID, VSTR_TYPE_FMT_END);
setlocale(LC_ALL, "");
loc_num_name = setlocale(LC_NUMERIC, NULL);
if (!vstr_cntl_conf(s1->conf, VSTR_CNTL_CONF_SET_LOC_CSTR_AUTO_NAME_NUMERIC,
loc_num_name))
errx(EXIT_FAILURE, "Couldn't change numeric locale info");
mpz_init_set_str(bignum_for, argv[1], 0);
mpz_init_set_str(bignum_ret, "1", 0);
mpz_init_set_str(bignum_cnt, "1", 0);
if (EX_GMP_FACT_USE_FIELDWIDTH)
{
vstr_add_fmt(s1, s1->len, "$'<MPZ:%p>", (void *)bignum_for);
if (s1->conf->malloc_bad)
errno = ENOMEM, err(EXIT_FAILURE, "Add string data");
cnt_max_sz = s1->len; vstr_del(s1, 1, s1->len);
if (mpz_fits_ulong_p(bignum_for))
mpz_fac_ui(bignum_ret, mpz_get_ui(bignum_for));
else
ex_gmp_fact(bignum_ret, bignum_cnt, bignum_for, FALSE, NULL, 0, 0);
if (!vstr_add_fmt(s1, s1->len, "$'<MPZ:%p>", (void *)bignum_ret))
errno = ENOMEM, err(EXIT_FAILURE, "Add string data");
ret_max_sz = s1->len; vstr_del(s1, 1, s1->len);
mpz_init_set_str(bignum_ret, "1", 0);
mpz_init_set_str(bignum_cnt, "1", 0);
}
if (mpz_sgn(bignum_for) >= 0)
if (!vstr_add_fmt(s1, s1->len, "%*u%s %c %*u\n\n",
cnt_max_sz, 0, "!", '=', ret_max_sz, 1))
errno = ENOMEM, err(EXIT_FAILURE, "Add string data");
ex_gmp_fact(bignum_ret, bignum_cnt, bignum_for,
TRUE, s1, ret_max_sz, cnt_max_sz);
io_put_all(s1, STDOUT_FILENO);
exit (ex_exit(s1, NULL));
}