#include <string.h>
#include "perm.h"
#include "acb.h"
#include "acb_mat.h"
static void
_apply_permutation(acb_mat_t A, const slong * P, slong num_rows)
{
if (num_rows != 0)
{
acb_ptr Atmp;
slong i;
slong row_offset = 0;
slong col_offset = 0;
slong num_cols = A->c;
Atmp = flint_malloc(sizeof(acb_struct) * num_rows * num_cols);
for (i = 0; i < num_rows; i++)
memcpy(Atmp + i * num_cols, acb_mat_entry(A, P[i] + row_offset, col_offset), sizeof(acb_struct) * num_cols);
for (i = 0; i < num_rows; i++)
memcpy(acb_mat_entry(A, i + row_offset, col_offset), Atmp + i * num_cols, sizeof(acb_struct) * num_cols);
flint_free(Atmp);
}
}
static void
acb_mat_det_one_gershgorin(acb_t det, const acb_mat_t A)
{
slong n, i, j;
acb_t t;
mag_t r, e, f;
n = acb_mat_nrows(A);
acb_init(t);
mag_init(r);
mag_init(e);
mag_init(f);
for (i = 0; i < n; i++)
{
mag_zero(e);
for (j = 0; j < n; j++)
{
if (i == j)
{
acb_sub_ui(t, acb_mat_entry(A, i, j), 1, MAG_BITS);
acb_get_mag(f, t);
}
else
{
acb_get_mag(f, acb_mat_entry(A, i, j));
}
mag_add(e, e, f);
}
mag_max(r, r, e);
}
mag_mul_ui(r, r, n);
mag_expm1(r, r);
acb_one(det);
mag_set(arb_radref(acb_realref(det)), r);
mag_set(arb_radref(acb_imagref(det)), r);
acb_clear(t);
mag_clear(r);
mag_clear(e);
mag_clear(f);
}
void
acb_mat_det_precond(acb_t det, const acb_mat_t A, slong prec)
{
acb_mat_t LU, Linv, Uinv;
acb_t detU;
slong n;
slong *P;
n = acb_mat_nrows(A);
if (n == 0)
{
acb_one(det);
return;
}
P = _perm_init(n);
acb_mat_init(LU, n, n);
if (!acb_mat_approx_lu(P, LU, A, prec))
{
acb_mat_det_lu(det, A, prec);
}
else
{
acb_mat_init(Linv, n, n);
acb_mat_init(Uinv, n, n);
acb_init(detU);
acb_mat_one(Linv);
acb_mat_approx_solve_tril(Linv, LU, Linv, 1, prec);
acb_mat_one(Uinv);
acb_mat_approx_solve_triu(Uinv, LU, Uinv, 0, prec);
acb_mat_diag_prod(detU, Uinv, prec);
acb_mat_mul(LU, A, Uinv, prec);
_apply_permutation(LU, P, n);
acb_mat_mul(Uinv, Linv, LU, prec);
acb_mat_det_one_gershgorin(det, Uinv);
if (acb_mat_is_real(A))
arb_zero(acb_imagref(det));
if (_perm_parity(P, n))
acb_neg(det, det);
acb_div(det, det, detU, prec);
if (acb_contains_zero(det))
{
mag_t rad1, rad2;
acb_mat_det_lu(detU, A, prec);
mag_init(rad1);
mag_init(rad2);
mag_hypot(rad1, arb_radref(acb_realref(detU)), arb_radref(acb_imagref(detU)));
mag_hypot(rad2, arb_radref(acb_realref(det)), arb_radref(acb_imagref(det)));
if (mag_cmp(rad1, rad2) < 0)
acb_set(det, detU);
mag_clear(rad1);
mag_clear(rad2);
}
acb_mat_clear(Linv);
acb_mat_clear(Uinv);
acb_clear(detU);
}
_perm_clear(P);
acb_mat_clear(LU);
}