#include "gr.h"
#include "gr_mat.h"
#include "gr_poly.h"
static int
gr_mat_gr_poly_shift_left(gr_mat_t res, gr_mat_t A, slong s, gr_ctx_t poly_ctx)
{
int status = GR_SUCCESS;
gr_ctx_struct *coeff_ctx = POLYNOMIAL_CTX(poly_ctx)->base_ring;
for (slong i = 0; i < A->r; i++)
{
for (slong j = 0; j < A->c; j++)
{
gr_srcptr entry_A = gr_mat_entry_srcptr(A, i, j, poly_ctx);
gr_ptr entry_res = gr_mat_entry_ptr(res, i, j, poly_ctx);
status |= gr_poly_shift_left(entry_res, entry_A, s, coeff_ctx);
}
}
return status;
}
static int
gr_mat_gr_poly_shift_right(gr_mat_t res, gr_mat_t A, slong s, gr_ctx_t poly_ctx)
{
int status = GR_SUCCESS;
gr_ctx_struct *coeff_ctx = POLYNOMIAL_CTX(poly_ctx)->base_ring;
for (slong i = 0; i < A->r; i++)
{
for (slong j = 0; j < A->c; j++)
{
gr_srcptr entry_A = gr_mat_entry_srcptr(A, i, j, poly_ctx);
gr_ptr entry_res = gr_mat_entry_ptr(res, i, j, poly_ctx);
status |= gr_poly_shift_right(entry_res, entry_A, s, coeff_ctx);
}
}
return status;
}
static int
gr_mat_gr_poly_truncate(gr_mat_t res, gr_mat_t A, slong n, gr_ctx_t poly_ctx)
{
int status = GR_SUCCESS;
gr_ctx_struct *coeff_ctx = POLYNOMIAL_CTX(poly_ctx)->base_ring;
for (slong i = 0; i < A->r; i++)
{
for (slong j = 0; j < A->c; j++)
{
gr_srcptr entry_A = gr_mat_entry_srcptr(A, i, j, poly_ctx);
gr_ptr entry_res = gr_mat_entry_ptr(res, i, j, poly_ctx);
status |= gr_poly_truncate(entry_res, entry_A, n, coeff_ctx);
}
}
return status;
}
static int
gr_mat_gr_poly_mulmid_companion(gr_mat_t res, gr_mat_t A, gr_mat_t B, slong nlo, slong nhi, gr_ctx_t poly_ctx)
{
gr_ctx_struct *coeff_ctx = POLYNOMIAL_CTX(poly_ctx)->base_ring;
gr_mat_t tmp_mat;
gr_poly_t tmp_poly;
int status = GR_SUCCESS;
gr_mat_init(tmp_mat, A->r, A->c, poly_ctx);
gr_poly_init(tmp_poly, coeff_ctx);
for (slong i = 0; i < A->r - 1; i++)
{
for (slong j = 0; j < A->c; j++)
{
gr_srcptr entry_B = gr_mat_entry_srcptr(B, i + 1, j, poly_ctx);
gr_ptr entry_tmp = gr_mat_entry_ptr(tmp_mat, i, j, poly_ctx);
status |= gr_poly_shift_right(entry_tmp, entry_B, nlo, coeff_ctx);
status |= gr_poly_truncate(entry_tmp, entry_tmp, nhi - nlo, coeff_ctx);
}
}
for (slong j = 0; j < A->c; j++)
{
gr_ptr entry_tmp = gr_mat_entry_ptr(tmp_mat, A->r - 1, j, poly_ctx);
for (slong i = 0; i < B->r; i++)
{
gr_srcptr entry_A = gr_mat_entry_srcptr(A, A->r - 1, i, poly_ctx);
gr_srcptr entry_B = gr_mat_entry_srcptr(B, i, j, poly_ctx);
status |= gr_poly_mulmid(tmp_poly, entry_A, entry_B, nlo, nhi, coeff_ctx);
status |= gr_poly_add(entry_tmp, entry_tmp, tmp_poly, coeff_ctx);
}
}
gr_mat_swap(res, tmp_mat, poly_ctx);
gr_poly_clear(tmp_poly, coeff_ctx);
gr_mat_clear(tmp_mat, poly_ctx);
return status;
}
int
_gr_mat_gr_poly_solve_lode_newton_start(gr_mat_t Y, gr_mat_t Z, gr_poly_t A_denominator_inv, const gr_mat_t A_numerator, const gr_poly_t A_denominator, const gr_mat_t Y0, gr_ctx_t sol_poly_ctx)
{
gr_ctx_struct *sol_coeff_ctx = POLYNOMIAL_CTX(sol_poly_ctx)->base_ring;
gr_poly_t tmp_poly;
gr_mat_t Y0inv, tmp_mat;
slong i, j;
int m, status = GR_SUCCESS;
status |= gr_poly_one(A_denominator_inv, sol_coeff_ctx);
if (gr_poly_length(A_denominator, sol_coeff_ctx) > 0)
status |= gr_poly_div_scalar(A_denominator_inv, A_denominator_inv, gr_poly_entry_srcptr(A_denominator, 0, sol_coeff_ctx), sol_coeff_ctx);
else
status = GR_DOMAIN;
if (status != GR_SUCCESS)
return status;
gr_mat_init(Y0inv, Y0->r, Y0->c, sol_coeff_ctx);
status |= gr_mat_inv(Y0inv, Y0, sol_coeff_ctx);
if (status != GR_SUCCESS)
{
gr_mat_clear(Y0inv, sol_coeff_ctx);
return status;
}
gr_mat_init(tmp_mat, Y->r, Y->c, sol_poly_ctx);
gr_poly_init(tmp_poly, sol_coeff_ctx);
status |= gr_mat_set_gr_mat_other(Z, Y0inv, sol_coeff_ctx, sol_poly_ctx);
for (i = 0; i < Y->r; i++)
{
for (j = 0; j < Y->c; j++)
{
gr_srcptr entry_A = gr_mat_entry_srcptr(A_numerator, i, j, sol_poly_ctx);
gr_ptr entry_Y = gr_mat_entry_ptr(Y, i, j, sol_poly_ctx);
status |= gr_poly_truncate(entry_Y, entry_A, 1, sol_coeff_ctx);
status |= gr_poly_shift_left(entry_Y, entry_Y, 1, sol_coeff_ctx);
}
}
status |= gr_mat_mul_scalar(Y, Y, A_denominator_inv, sol_poly_ctx);
status |= gr_mat_add_ui(Y, Y, 1, sol_poly_ctx);
status |= gr_mat_set_gr_mat_other(tmp_mat, Y0, sol_coeff_ctx, sol_poly_ctx);
status |= gr_mat_mul(Y, Y, tmp_mat, sol_poly_ctx);
m = 2;
status |= gr_poly_truncate(tmp_poly, A_denominator, m, sol_coeff_ctx);
status |= gr_poly_mulmid(tmp_poly, tmp_poly, A_denominator_inv, m / 2, m, sol_coeff_ctx);
status |= gr_poly_mullow(tmp_poly, tmp_poly, A_denominator_inv, m / 2, sol_coeff_ctx);
status |= gr_poly_shift_left(tmp_poly, tmp_poly, m / 2, sol_coeff_ctx);
status |= gr_poly_sub(A_denominator_inv, A_denominator_inv, tmp_poly, sol_coeff_ctx);
gr_poly_clear(tmp_poly, sol_coeff_ctx);
gr_mat_clear(tmp_mat, sol_poly_ctx);
gr_mat_clear(Y0inv, sol_coeff_ctx);
return status;
}
int
_gr_mat_gr_poly_solve_lode_newton_step(gr_mat_t Y, gr_mat_t Z, gr_poly_t A_denominator_inv, slong len, const gr_mat_t A_numerator, const gr_poly_t A_denominator, int A_is_companion, gr_ctx_t sol_poly_ctx)
{
gr_ctx_struct *sol_coeff_ctx = POLYNOMIAL_CTX(sol_poly_ctx)->base_ring;
gr_mat_t Err, tmp_mat;
gr_poly_t tmp_poly;
slong i, j, m = (len + 1) / 2;
int status = GR_SUCCESS;
gr_mat_init(Err, Y->r, Y->c, sol_poly_ctx);
gr_mat_init(tmp_mat, Y->r, Y->c, sol_poly_ctx);
gr_poly_init(tmp_poly, sol_coeff_ctx);
status |= gr_mat_mul(tmp_mat, Y, Z, sol_poly_ctx);
status |= gr_mat_sub_ui(tmp_mat, tmp_mat, 1, sol_poly_ctx);
status |= gr_mat_mul(tmp_mat, Z, tmp_mat, sol_poly_ctx);
status |= gr_mat_gr_poly_truncate(tmp_mat, tmp_mat, m, sol_poly_ctx);
status |= gr_mat_sub(Z, Z, tmp_mat, sol_poly_ctx);
status |= gr_poly_mulmid(tmp_poly, A_denominator, A_denominator_inv, m, len, sol_coeff_ctx);
status |= gr_poly_mullow(tmp_poly, tmp_poly, A_denominator_inv, m, sol_coeff_ctx);
status |= gr_poly_shift_left(tmp_poly, tmp_poly, m, sol_coeff_ctx);
status |= gr_poly_sub(A_denominator_inv, A_denominator_inv, tmp_poly, sol_coeff_ctx);
for (i = 0; i < Y->r; i++)
{
for (j = 0; j < Y->c; j++)
{
gr_ptr entry_tmp = gr_mat_entry_ptr(tmp_mat, i, j, sol_poly_ctx);
status |= gr_poly_truncate(entry_tmp, gr_mat_entry_srcptr(A_numerator, i, j, sol_poly_ctx), len - 1, sol_coeff_ctx);
status |= gr_poly_mullow(entry_tmp, entry_tmp, A_denominator_inv, len - 1, sol_coeff_ctx);
}
}
if (A_is_companion)
{
status |= gr_mat_gr_poly_mulmid_companion(tmp_mat, tmp_mat, Y, m - 1, len - 1, sol_poly_ctx);
}
else
{
status |= gr_mat_mul(tmp_mat, tmp_mat, Y, sol_poly_ctx);
status |= gr_mat_gr_poly_truncate(tmp_mat, tmp_mat, len - 1, sol_poly_ctx);
status |= gr_mat_gr_poly_shift_right(tmp_mat, tmp_mat, m - 1, sol_poly_ctx);
}
for (i = 0; i < Y->r; i++)
{
for (j = 0; j < Y->c; j++)
{
gr_srcptr entry_Y = gr_mat_entry_srcptr(Y, i, j, sol_poly_ctx);
gr_ptr entry_Err = gr_mat_entry_ptr(Err, i, j, sol_poly_ctx);
status |= gr_poly_derivative(entry_Err, entry_Y, sol_coeff_ctx);
status |= gr_poly_shift_right(entry_Err, entry_Err, m - 1, sol_coeff_ctx);
}
}
status |= gr_mat_sub(Err, Err, tmp_mat, sol_poly_ctx);
status |= gr_mat_mul(tmp_mat, Z, Err, sol_poly_ctx);
status |= gr_mat_gr_poly_truncate(tmp_mat, tmp_mat, len - m, sol_poly_ctx);
status |= gr_mat_gr_poly_shift_left(tmp_mat, tmp_mat, m - 1, sol_poly_ctx);
for (i = 0; i < Y->r; i++)
{
for (j = 0; j < Y->c; j++)
{
gr_ptr entry_tmp = gr_mat_entry_ptr(tmp_mat, i, j, sol_poly_ctx);
status |= gr_poly_integral(entry_tmp, entry_tmp, sol_coeff_ctx);
}
}
status |= gr_mat_gr_poly_shift_right(tmp_mat, tmp_mat, m, sol_poly_ctx);
status |= gr_mat_mul(tmp_mat, Y, tmp_mat, sol_poly_ctx);
status |= gr_mat_gr_poly_truncate(tmp_mat, tmp_mat, len - m, sol_poly_ctx);
status |= gr_mat_gr_poly_shift_left(tmp_mat, tmp_mat, m, sol_poly_ctx);
status |= gr_mat_sub(Y, Y, tmp_mat, sol_poly_ctx);
gr_poly_clear(tmp_poly, sol_coeff_ctx);
gr_mat_clear(tmp_mat, sol_poly_ctx);
gr_mat_clear(Err, sol_poly_ctx);
return status;
}
int
_gr_mat_gr_poly_solve_lode_newton(gr_mat_t Y, gr_mat_t Z, const gr_mat_t A_numerator, const gr_poly_t A_denominator, const gr_mat_t Y0, slong len, gr_ctx_t A_poly_ctx, gr_ctx_t sol_poly_ctx)
{
gr_ctx_struct *A_coeff_ctx = POLYNOMIAL_CTX(A_poly_ctx)->base_ring;
gr_ctx_struct *sol_coeff_ctx = POLYNOMIAL_CTX(sol_poly_ctx)->base_ring;
gr_mat_t A_numerator_sol;
gr_poly_t A_denominator_sol, A_denominator_inv;
slong i, j, n;
slong a[FLINT_BITS];
int A_is_companion = 1, status = GR_SUCCESS;
for (i = 0; i < A_numerator->r - 1; i++)
{
for (j = 0; j < A_numerator->c; j++)
{
gr_srcptr entry_A = gr_mat_entry_srcptr(A_numerator, i, j, A_poly_ctx);
if (j == i + 1)
A_is_companion &= (gr_poly_equal(entry_A, A_denominator, A_coeff_ctx) == T_TRUE);
else
A_is_companion &= (gr_poly_is_zero(entry_A, A_coeff_ctx) == T_TRUE);
}
}
gr_poly_init(A_denominator_sol, sol_coeff_ctx);
gr_poly_init(A_denominator_inv, sol_coeff_ctx);
if (A_poly_ctx == sol_poly_ctx)
{
status |= gr_poly_set(A_denominator_sol, A_denominator, sol_coeff_ctx);
status |= gr_mat_init_set(A_numerator_sol, A_numerator, sol_poly_ctx);
}
else
{
status |= gr_poly_set_gr_poly_other(A_denominator_sol, A_denominator, A_coeff_ctx, sol_coeff_ctx);
gr_mat_init(A_numerator_sol, A_numerator->r, A_numerator->c, sol_poly_ctx);
status |= gr_mat_set_gr_mat_other(A_numerator_sol, A_numerator, A_poly_ctx, sol_poly_ctx);
}
a[i = 0] = n = len;
while (n > 1)
a[++i] = (n = (n + 1) / 2);
status |= _gr_mat_gr_poly_solve_lode_newton_start(Y, Z, A_denominator_inv, A_numerator_sol, A_denominator_sol, Y0, sol_poly_ctx);
if (status == GR_SUCCESS)
for (i--; i >= 0; i--)
status |= _gr_mat_gr_poly_solve_lode_newton_step(Y, Z, A_denominator_inv, a[i], A_numerator_sol, A_denominator_sol, A_is_companion, sol_poly_ctx);
gr_mat_clear(A_numerator_sol, sol_poly_ctx);
gr_poly_clear(A_denominator_inv, sol_coeff_ctx);
gr_poly_clear(A_denominator_sol, sol_coeff_ctx);
return status;
}
int
gr_mat_gr_poly_solve_lode_newton(gr_mat_t Y, const gr_mat_t A_numerator, const gr_poly_t A_denominator, const gr_mat_t Y0, slong len, gr_ctx_t A_poly_ctx, gr_ctx_t sol_poly_ctx)
{
gr_mat_t Z;
int status;
gr_mat_init(Z, Y0->r, Y0->c, sol_poly_ctx);
status = _gr_mat_gr_poly_solve_lode_newton(Y, Z, A_numerator, A_denominator, Y0, len, A_poly_ctx, sol_poly_ctx);
gr_mat_clear(Z, sol_poly_ctx);
return status;
}