diff --git a/acb_poly.h b/acb_poly.h
index f0e8e221..b867b2ef 100644
--- a/acb_poly.h
+++ b/acb_poly.h
@@ -193,6 +193,10 @@ void _acb_poly_binomial_transform(acb_ptr b, acb_srcptr a, slong alen, slong len
void acb_poly_binomial_transform(acb_poly_t b, const acb_poly_t a, slong len, slong prec);
+void _acb_poly_graeffe_transform(acb_ptr b, acb_srcptr a, slong len, slong prec);
+
+void acb_poly_graeffe_transform(acb_poly_t b, const acb_poly_t a, slong prec);
+
void acb_poly_set(acb_poly_t dest, const acb_poly_t src);
diff --git a/acb_poly/graeffe_transform.c b/acb_poly/graeffe_transform.c
new file mode 100644
index 00000000..def2049e
--- /dev/null
+++ b/acb_poly/graeffe_transform.c
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2012 Fredrik Johansson
+
+ This file is part of Arb.
+
+ Arb is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License (LGPL) as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "acb_poly.h"
+
+void
+_acb_poly_graeffe_transform(acb_ptr b, acb_srcptr a, slong len, slong prec)
+{
+ if (len == 0)
+ return;
+
+ slong q, i;
+
+ q = (len-1)/2+1;
+ acb_ptr pe = _acb_vec_init(q);
+ acb_ptr po = _acb_vec_init(len);
+
+ for (i = len-1; i >= 0; i--)
+ {
+ if (i % 2 == 0)
+ acb_set(pe+i/2, a+i);
+ else
+ acb_set(po+i/2, a+i);
+ }
+
+ _acb_poly_mul(b, po, q, po, q, prec);
+ _acb_poly_shift_left(b, b, len-1, 1);
+ _acb_poly_mul(po, pe, q, pe, q, prec);
+ _acb_vec_sub(b, po, b, len, prec);
+
+ _acb_vec_clear(pe, q);
+ _acb_vec_clear(po, len);
+
+ if (len % 2 == 0)
+ _acb_vec_neg(b, b, len);
+}
+
+void
+acb_poly_graeffe_transform(acb_poly_t b, const acb_poly_t a, slong prec)
+{
+ acb_poly_fit_length(b, a->length);
+ _acb_poly_graeffe_transform(b->coeffs, a->coeffs, a->length, prec);
+ _acb_poly_set_length(b, a->length);
+}
diff --git a/arb_poly.h b/arb_poly.h
index 60da6ad0..1fa2940b 100644
--- a/arb_poly.h
+++ b/arb_poly.h
@@ -512,6 +512,10 @@ void _arb_poly_binomial_transform(arb_ptr b, arb_srcptr a, slong alen, slong len
void arb_poly_binomial_transform(arb_poly_t b, const arb_poly_t a, slong len, slong prec);
+void _arb_poly_graeffe_transform(arb_ptr b, arb_srcptr a, slong len, slong prec);
+
+void arb_poly_graeffe_transform(arb_poly_t b, const arb_poly_t a, slong prec);
+
/* Special functions */
void _arb_poly_pow_ui_trunc_binexp(arb_ptr res,
diff --git a/arb_poly/graeffe_transform.c b/arb_poly/graeffe_transform.c
new file mode 100644
index 00000000..dcc208a3
--- /dev/null
+++ b/arb_poly/graeffe_transform.c
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2012 Fredrik Johansson
+
+ This file is part of Arb.
+
+ Arb is free software: you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License (LGPL) as published
+ by the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version. See .
+*/
+
+#include "arb_poly.h"
+
+void
+_arb_poly_graeffe_transform(arb_ptr b, arb_srcptr a, slong len, slong prec)
+{
+ if (len == 0)
+ return;
+
+ slong q, i;
+
+ q = (len-1)/2+1;
+ arb_ptr pe = _arb_vec_init(q);
+ arb_ptr po = _arb_vec_init(len);
+
+ for (i = len-1; i >= 0; i--)
+ {
+ if (i % 2 == 0)
+ arb_set(pe+i/2, a+i);
+ else
+ arb_set(po+i/2, a+i);
+ }
+
+ _arb_poly_mul(b, po, q, po, q, prec);
+ _arb_poly_shift_left(b, b, len-1, 1);
+ _arb_poly_mul(po, pe, q, pe, q, prec);
+ _arb_vec_sub(b, po, b, len, prec);
+
+ _arb_vec_clear(pe, q);
+ _arb_vec_clear(po, len);
+
+ if (len % 2 == 0)
+ _arb_vec_neg(b, b, len);
+}
+
+void
+arb_poly_graeffe_transform(arb_poly_t b, const arb_poly_t a, slong prec)
+{
+ arb_poly_fit_length(b, a->length);
+ _arb_poly_graeffe_transform(b->coeffs, a->coeffs, a->length, prec);
+ _arb_poly_set_length(b, a->length);
+}