#include "acb_poly.h"
#include "acb_theta.h"
static void
acb_theta_g2_transvectant_lead(acb_t res, const acb_poly_t g, const acb_poly_t h,
slong m, slong n, slong k, slong prec)
{
acb_ptr s, t;
fmpz_t num, f;
slong j;
s = _acb_vec_init(k + 1);
t = _acb_vec_init(k + 1);
fmpz_init(num);
fmpz_init(f);
for (j = 0; j <= k; j++)
{
acb_poly_get_coeff_acb(&s[j], g, m - j);
acb_poly_get_coeff_acb(&t[j], h, n - k + j);
fmpz_fac_ui(num, m - j);
fmpz_fac_ui(f, n - k + j);
fmpz_mul(num, num, f);
if ((k - j) % 2 == 1)
{
fmpz_neg(num, num);
}
acb_mul_fmpz(&s[j], &s[j], num, prec);
}
acb_dot(res, NULL, 0, s, 1, t, 1, k + 1, prec);
fmpz_fac_ui(num, k);
acb_set_fmpz(t, num);
fmpz_fac_ui(num, m);
fmpz_fac_ui(f, n);
fmpz_mul(num, num, f);
acb_div_fmpz(t, t, num, prec);
acb_mul(res, res, t, prec);
_acb_vec_clear(s, k + 1);
_acb_vec_clear(t, k + 1);
fmpz_clear(num);
fmpz_clear(f);
}
static void
acb_theta_g2_transvectant_poly(acb_poly_t res, const acb_poly_t g, const acb_poly_t h,
slong m, slong n, slong k, slong prec)
{
acb_poly_t aux, s, t;
acb_t x;
fmpz_t num, f;
slong i, j;
acb_poly_init(aux);
acb_poly_init(s);
acb_poly_init(t);
acb_init(x);
fmpz_init(num);
fmpz_init(f);
for (j = 0; j <= k; j++)
{
acb_poly_zero(s);
for (i = 0; i <= m - k; i++)
{
fmpz_fac_ui(num, i + (k - j));
fmpz_fac_ui(f, (m - k - i) + j);
fmpz_mul(num, num, f);
fmpz_bin_uiui(f, m - k, i);
fmpz_mul(num, num, f);
acb_poly_get_coeff_acb(x, g, i + (k - j));
acb_mul_fmpz(x, x, num, prec);
acb_poly_set_coeff_acb(s, i, x);
}
acb_poly_zero(t);
for (i = 0; i <= n - k; i++)
{
fmpz_fac_ui(num, i + j);
fmpz_fac_ui(f, (n - k - i) + (k - j));
fmpz_mul(num, num, f);
fmpz_bin_uiui(f, n - k, i);
fmpz_mul(num, num, f);
acb_poly_get_coeff_acb(x, h, i + j);
acb_mul_fmpz(x, x, num, prec);
acb_poly_set_coeff_acb(t, i, x);
}
acb_poly_mul(s, s, t, prec);
fmpz_bin_uiui(f, k, j);
if ((k - j) % 2 == 1)
{
fmpz_neg(f, f);
}
acb_set_fmpz(x, f);
acb_poly_scalar_mul(s, s, x, prec);
acb_poly_add(aux, aux, s, prec);
}
fmpz_fac_ui(num, m);
fmpz_fac_ui(f, n);
fmpz_mul(num, num, f);
acb_one(x);
acb_div_fmpz(x, x, num, prec);
acb_poly_scalar_mul(res, aux, x, prec);
acb_poly_clear(aux);
acb_poly_clear(s);
acb_poly_clear(t);
acb_clear(x);
fmpz_clear(num);
fmpz_clear(f);
}
void
acb_theta_g2_transvectant(acb_poly_t res, const acb_poly_t g, const acb_poly_t h,
slong m, slong n, slong k, int lead, slong prec)
{
if (lead)
{
acb_t x;
acb_init(x);
acb_theta_g2_transvectant_lead(x, g, h, m, n, k, prec);
acb_poly_zero(res);
acb_poly_set_coeff_acb(res, 0, x);
acb_clear(x);
}
else
{
acb_theta_g2_transvectant_poly(res, g, h, m, n, k, prec);
}
}