#include "fmpq.h"
#include "gr.h"
#include "gr_generic.h"
#include "gr_mat.h"
int
gr_mat_pow_ui(gr_mat_t res, const gr_mat_t mat, ulong exp, gr_ctx_t ctx)
{
int status;
slong sz = ctx->sizeof_elem;
slong d;
d = gr_mat_nrows(res, ctx);
if (d != gr_mat_ncols(res, ctx) || d != gr_mat_nrows(mat, ctx)
|| d != gr_mat_ncols(mat, ctx))
{
return GR_DOMAIN;
}
status = GR_SUCCESS;
if (exp <= 2 || d <= 1)
{
if (exp == 0 || d == 0)
{
status |= gr_mat_one(res, ctx);
}
else if (d == 1)
{
status |= gr_pow_ui(GR_MAT_ENTRY(res, 0, 0, sz),
GR_MAT_ENTRY(mat, 0, 0, sz), exp, ctx);
}
else if (exp == 1)
{
status |= gr_mat_set(res, mat, ctx);
}
else if (exp == 2)
{
status |= gr_mat_sqr(res, mat, ctx);
}
}
else
{
gr_ctx_t mctx;
gr_ctx_init_matrix_ring(mctx, ctx, d);
status |= gr_generic_pow_ui(res, mat, exp, mctx);
gr_ctx_clear(mctx);
}
return status;
}
int
gr_mat_pow_fmpz(gr_mat_t res, const gr_mat_t mat, const fmpz_t exp, gr_ctx_t ctx)
{
int status;
slong sz = ctx->sizeof_elem;
slong d;
d = gr_mat_nrows(res, ctx);
if (d != gr_mat_ncols(res, ctx) || d != gr_mat_nrows(mat, ctx)
|| d != gr_mat_ncols(mat, ctx))
{
return GR_DOMAIN;
}
status = GR_SUCCESS;
if (fmpz_is_zero(exp) || d == 0)
{
status |= gr_mat_one(res, ctx);
}
else if (d == 1)
{
status |= gr_pow_fmpz(GR_MAT_ENTRY(res, 0, 0, sz),
GR_MAT_ENTRY(mat, 0, 0, sz), exp, ctx);
}
else if (fmpz_is_one(exp))
{
status |= gr_mat_set(res, mat, ctx);
}
else
{
gr_ctx_t mctx;
gr_ctx_init_matrix_ring(mctx, ctx, d);
status |= gr_generic_pow_fmpz(res, mat, exp, mctx);
gr_ctx_clear(mctx);
}
return status;
}
int
gr_mat_pow_si(gr_mat_t res, const gr_mat_t mat, slong exp, gr_ctx_t ctx)
{
if (exp >= 0)
{
return gr_mat_pow_ui(res, mat, exp, ctx);
}
else
{
fmpz_t n;
int status;
fmpz_init_set_si(n, exp);
status = gr_mat_pow_fmpz(res, mat, n, ctx);
fmpz_clear(n);
return status;
}
}
static int
gr_pow_jet(gr_ptr res, gr_srcptr x, slong len, gr_srcptr c, gr_ctx_t ctx)
{
slong i;
int status;
slong sz = ctx->sizeof_elem;
if (len <= 0)
return GR_SUCCESS;
status = gr_pow(res, x, c, ctx);
if (status == GR_SUCCESS && len > 1)
{
gr_ptr t, u;
GR_TMP_INIT2(t, u, ctx);
status |= gr_inv(t, x, ctx);
for (i = 1; i < len; i++)
{
status |= gr_mul(GR_ENTRY(res, i, sz), GR_ENTRY(res, i - 1, sz), t, ctx);
status |= gr_sub_ui(u, c, i - 1, ctx);
status |= gr_mul(GR_ENTRY(res, i, sz), GR_ENTRY(res, i, sz), u, ctx);
status |= gr_div_ui(GR_ENTRY(res, i, sz), GR_ENTRY(res, i, sz), i, ctx);
}
GR_TMP_CLEAR(t, ctx);
GR_TMP_CLEAR(u, ctx);
}
return status;
}
int
gr_mat_pow_scalar_jordan(gr_mat_t res, const gr_mat_t A, gr_srcptr c, gr_ctx_t ctx)
{
return gr_mat_func_param_jordan(res, A, (gr_method_vec_scalar_op) gr_pow_jet, c, ctx);
}
int
gr_mat_pow_scalar(gr_mat_t res, const gr_mat_t A, gr_srcptr c, gr_ctx_t ctx)
{
slong n;
if (gr_get_si(&n, c, ctx) == GR_SUCCESS)
{
return gr_mat_pow_si(res, A, n, ctx);
}
else
{
int status = gr_mat_pow_scalar_jordan(res, A, c, ctx);
return (status == GR_SUCCESS) ? GR_SUCCESS : GR_UNABLE;
}
}
static int
gr_pow_fmpq_jet(gr_ptr res, gr_srcptr x, slong len, const fmpq_t c, gr_ctx_t ctx)
{
slong i;
int status;
slong sz = ctx->sizeof_elem;
if (len <= 0)
return GR_SUCCESS;
status = gr_pow_fmpq(res, x, c, ctx);
if (status == GR_SUCCESS && len > 1)
{
gr_ptr t;
fmpq_t u;
GR_TMP_INIT(t, ctx);
fmpq_init(u);
status |= gr_inv(t, x, ctx);
for (i = 1; i < len; i++)
{
status |= gr_mul(GR_ENTRY(res, i, sz), GR_ENTRY(res, i - 1, sz), t, ctx);
fmpq_sub_ui(u, c, i - 1);
status |= gr_mul_fmpq(GR_ENTRY(res, i, sz), GR_ENTRY(res, i, sz), u, ctx);
status |= gr_div_ui(GR_ENTRY(res, i, sz), GR_ENTRY(res, i, sz), i, ctx);
}
GR_TMP_CLEAR(t, ctx);
fmpq_clear(u);
}
return status;
}
int
gr_mat_pow_fmpq_jordan(gr_mat_t res, const gr_mat_t A, const fmpq_t c, gr_ctx_t ctx)
{
return gr_mat_func_param_jordan(res, A, (gr_method_vec_scalar_op) gr_pow_fmpq_jet, c, ctx);
}
int
gr_mat_pow_fmpq(gr_mat_t res, const gr_mat_t A, const fmpq_t c, gr_ctx_t ctx)
{
if (fmpz_is_one(fmpq_denref(c)))
return gr_mat_pow_fmpz(res, A, fmpq_numref(c), ctx);
else
{
int status = gr_mat_pow_fmpq_jordan(res, A, c, ctx);
return (status == GR_SUCCESS) ? GR_SUCCESS : GR_UNABLE;
}
}
int
gr_mat_sqrt(gr_mat_t res, const gr_mat_t A, gr_ctx_t ctx)
{
fmpq_t c;
*fmpq_numref(c) = 1;
*fmpq_denref(c) = 2;
return gr_mat_pow_fmpq(res, A, c, ctx);
}
int
gr_mat_rsqrt(gr_mat_t res, const gr_mat_t A, gr_ctx_t ctx)
{
fmpq_t c;
*fmpq_numref(c) = -1;
*fmpq_denref(c) = 2;
return gr_mat_pow_fmpq(res, A, c, ctx);
}