aboutsummaryrefslogtreecommitdiffstats
path: root/moon-abe/pbc-0.5.14/arith/poly.c
diff options
context:
space:
mode:
Diffstat (limited to 'moon-abe/pbc-0.5.14/arith/poly.c')
-rw-r--r--moon-abe/pbc-0.5.14/arith/poly.c1724
1 files changed, 1724 insertions, 0 deletions
diff --git a/moon-abe/pbc-0.5.14/arith/poly.c b/moon-abe/pbc-0.5.14/arith/poly.c
new file mode 100644
index 00000000..bd2dad33
--- /dev/null
+++ b/moon-abe/pbc-0.5.14/arith/poly.c
@@ -0,0 +1,1724 @@
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h> // for intptr_t
+#include <stdlib.h>
+#include <gmp.h>
+#include "pbc_utils.h"
+#include "pbc_field.h"
+#include "pbc_multiz.h"
+#include "pbc_poly.h"
+#include "pbc_memory.h"
+#include "misc/darray.h"
+
+// == Polynomial rings ==
+//
+// Per-field data:
+typedef struct {
+ field_ptr field; // Ring where coefficients live.
+ fieldmap mapbase; // Map element from underlying field to constant term.
+} *pfptr;
+
+// Per-element data:
+//TODO: Would we ever need any field besides coeff?
+typedef struct {
+ // The coefficients are held in a darray which is resized as needed.
+ // The last array entry represents the leading coefficient and should be
+ // nonzero. An empty darray represents 0.
+ darray_t coeff;
+} *peptr;
+
+// == Polynomial modulo rings ==
+//
+// Per-field data:
+typedef struct {
+ field_ptr field; // Base field.
+ fieldmap mapbase; // Similar to mapbase above.
+ int n; // Degree of extension.
+ element_t poly; // Polynomial of degree n.
+ element_t *xpwr; // x^n,...,x^{2n-2} mod poly
+} *mfptr;
+// Per-element data: just a pointer to an array of element_t. This array always
+// has size n.
+
+// Add or remove coefficients until there are exactly n of them. Any new
+// coefficients are initialized to zero, which violates the invariant that the
+// leading coefficient must be nonzero. Thus routines calling this function
+// must check for this and fix the polynomial if necessary, e.g. by calling
+// poly_remove_leading_zeroes().
+static void poly_alloc(element_ptr e, int n) {
+ pfptr pdp = e->field->data;
+ peptr p = e->data;
+ element_ptr e0;
+ int k = p->coeff->count;
+ while (k < n) {
+ e0 = pbc_malloc(sizeof(element_t));
+ element_init(e0, pdp->field);
+ darray_append(p->coeff, e0);
+ k++;
+ }
+ while (k > n) {
+ k--;
+ e0 = darray_at(p->coeff, k);
+ element_clear(e0);
+ pbc_free(e0);
+ darray_remove_last(p->coeff);
+ }
+}
+
+static void poly_init(element_ptr e) {
+ peptr p = e->data = pbc_malloc(sizeof(*p));
+ darray_init(p->coeff);
+}
+
+static void poly_clear(element_ptr e) {
+ peptr p = e->data;
+
+ poly_alloc(e, 0);
+ darray_clear(p->coeff);
+ pbc_free(e->data);
+}
+
+// Some operations may zero a leading coefficient, which will cause other
+// routines to fail. After such an operation, this function should be called,
+// as it strips all leading zero coefficients and frees the memory they
+// occupied, reestablishing the guarantee that the last element of the array
+// is nonzero.
+static void poly_remove_leading_zeroes(element_ptr e) {
+ peptr p = e->data;
+ int n = p->coeff->count - 1;
+ while (n >= 0) {
+ element_ptr e0 = p->coeff->item[n];
+ if (!element_is0(e0)) return;
+ element_clear(e0);
+ pbc_free(e0);
+ darray_remove_last(p->coeff);
+ n--;
+ }
+}
+
+static void poly_set0(element_ptr e) {
+ poly_alloc(e, 0);
+}
+
+static void poly_set1(element_ptr e) {
+ peptr p = e->data;
+ element_ptr e0;
+
+ poly_alloc(e, 1);
+ e0 = p->coeff->item[0];
+ element_set1(e0);
+}
+
+static int poly_is0(element_ptr e) {
+ peptr p = e->data;
+ return !p->coeff->count;
+}
+
+static int poly_is1(element_ptr e) {
+ peptr p = e->data;
+ if (p->coeff->count == 1) {
+ return element_is1(p->coeff->item[0]);
+ }
+ return 0;
+}
+
+static void poly_set_si(element_ptr e, signed long int op) {
+ peptr p = e->data;
+ element_ptr e0;
+
+ poly_alloc(e, 1);
+ e0 = p->coeff->item[0];
+ element_set_si(e0, op);
+ poly_remove_leading_zeroes(e);
+}
+
+static void poly_set_mpz(element_ptr e, mpz_ptr op) {
+ peptr p = e->data;
+
+ poly_alloc(e, 1);
+ element_set_mpz(p->coeff->item[0], op);
+ poly_remove_leading_zeroes(e);
+}
+
+static void poly_set_multiz(element_ptr e, multiz op) {
+ if (multiz_is_z(op)) {
+ // TODO: Remove unnecessary copy.
+ mpz_t z;
+ mpz_init(z);
+ multiz_to_mpz(z, op);
+ poly_set_mpz(e, z);
+ mpz_clear(z);
+ return;
+ }
+ peptr p = e->data;
+ int n = multiz_count(op);
+ poly_alloc(e, n);
+ int i;
+ for(i = 0; i < n; i++) {
+ element_set_multiz(p->coeff->item[i], multiz_at(op, i));
+ }
+ poly_remove_leading_zeroes(e);
+}
+
+static void poly_set(element_ptr dst, element_ptr src) {
+ peptr psrc = src->data;
+ peptr pdst = dst->data;
+ int i;
+
+ poly_alloc(dst, psrc->coeff->count);
+ for (i=0; i<psrc->coeff->count; i++) {
+ element_set(pdst->coeff->item[i], psrc->coeff->item[i]);
+ }
+}
+
+static int poly_coeff_count(element_ptr e) {
+ return ((peptr) e->data)->coeff->count;
+}
+
+static element_ptr poly_coeff(element_ptr e, int n) {
+ peptr ep = e->data;
+ PBC_ASSERT(n < poly_coeff_count(e), "coefficient out of range");
+ return (element_ptr) ep->coeff->item[n];
+}
+
+static int poly_sgn(element_ptr f) {
+ int res = 0;
+ int i;
+ int n = poly_coeff_count(f);
+ for (i=0; i<n; i++) {
+ res = element_sgn(poly_coeff(f, i));
+ if (res) break;
+ }
+ return res;
+}
+
+static void poly_add(element_ptr sum, element_ptr f, element_ptr g) {
+ int i, n, n1;
+ element_ptr big;
+
+ n = poly_coeff_count(f);
+ n1 = poly_coeff_count(g);
+ if (n > n1) {
+ big = f;
+ n = n1;
+ n1 = poly_coeff_count(f);
+ } else {
+ big = g;
+ }
+
+ poly_alloc(sum, n1);
+ for (i=0; i<n; i++) {
+ element_add(poly_coeff(sum, i), poly_coeff(f, i), poly_coeff(g, i));
+ }
+ for (; i<n1; i++) {
+ element_set(poly_coeff(sum, i), poly_coeff(big, i));
+ }
+ poly_remove_leading_zeroes(sum);
+}
+
+static void poly_sub(element_ptr diff, element_ptr f, element_ptr g) {
+ int i, n, n1;
+ element_ptr big;
+
+ n = poly_coeff_count(f);
+ n1 = poly_coeff_count(g);
+ if (n > n1) {
+ big = f;
+ n = n1;
+ n1 = poly_coeff_count(f);
+ } else {
+ big = g;
+ }
+
+ poly_alloc(diff, n1);
+ for (i=0; i<n; i++) {
+ element_sub(poly_coeff(diff, i), poly_coeff(f, i), poly_coeff(g, i));
+ }
+ for (; i<n1; i++) {
+ if (big == f) {
+ element_set(poly_coeff(diff, i), poly_coeff(big, i));
+ } else {
+ element_neg(poly_coeff(diff, i), poly_coeff(big, i));
+ }
+ }
+ poly_remove_leading_zeroes(diff);
+}
+
+static void poly_neg(element_ptr f, element_ptr g) {
+ peptr pf = f->data;
+ peptr pg = g->data;
+ int i, n;
+
+ n = pg->coeff->count;
+ poly_alloc(f, n);
+ for (i=0; i<n; i++) {
+ element_neg(pf->coeff->item[i], pg->coeff->item[i]);
+ }
+}
+
+static void poly_double(element_ptr f, element_ptr g) {
+ peptr pf = f->data;
+ peptr pg = g->data;
+ int i, n;
+
+ n = pg->coeff->count;
+ poly_alloc(f, n);
+ for (i=0; i<n; i++) {
+ element_double(pf->coeff->item[i], pg->coeff->item[i]);
+ }
+}
+
+static void poly_mul_mpz(element_ptr f, element_ptr g, mpz_ptr z) {
+ peptr pf = f->data;
+ peptr pg = g->data;
+ int i, n;
+
+ n = pg->coeff->count;
+ poly_alloc(f, n);
+ for (i=0; i<n; i++) {
+ element_mul_mpz(pf->coeff->item[i], pg->coeff->item[i], z);
+ }
+}
+
+static void poly_mul_si(element_ptr f, element_ptr g, signed long int z) {
+ peptr pf = f->data;
+ peptr pg = g->data;
+ int i, n;
+
+ n = pg->coeff->count;
+ poly_alloc(f, n);
+ for (i=0; i<n; i++) {
+ element_mul_si(pf->coeff->item[i], pg->coeff->item[i], z);
+ }
+}
+
+static void poly_mul(element_ptr r, element_ptr f, element_ptr g) {
+ peptr pprod;
+ peptr pf = f->data;
+ peptr pg = g->data;
+ pfptr pdp = r->field->data;
+ int fcount = pf->coeff->count;
+ int gcount = pg->coeff->count;
+ int i, j, n;
+ element_t prod;
+ element_t e0;
+
+ if (!fcount || !gcount) {
+ element_set0(r);
+ return;
+ }
+ element_init(prod, r->field);
+ pprod = prod->data;
+ n = fcount + gcount - 1;
+ poly_alloc(prod, n);
+ element_init(e0, pdp->field);
+ for (i=0; i<n; i++) {
+ element_ptr x = pprod->coeff->item[i];
+ element_set0(x);
+ for (j=0; j<=i; j++) {
+ if (j < fcount && i - j < gcount) {
+ element_mul(e0, pf->coeff->item[j], pg->coeff->item[i - j]);
+ element_add(x, x, e0);
+ }
+ }
+ }
+ poly_remove_leading_zeroes(prod);
+ element_set(r, prod);
+ element_clear(e0);
+ element_clear(prod);
+}
+
+static void polymod_random(element_ptr e) {
+ element_t *coeff = e->data;
+ int i, n = polymod_field_degree(e->field);
+
+ for (i=0; i<n; i++) {
+ element_random(coeff[i]);
+ }
+}
+
+static void polymod_from_hash(element_ptr e, void *data, int len) {
+ // TODO: Improve this.
+ element_t *coeff = e->data;
+ int i, n = polymod_field_degree(e->field);
+ for (i=0; i<n; i++) {
+ element_from_hash(coeff[i], data, len);
+ }
+}
+
+static size_t poly_out_str(FILE *stream, int base, element_ptr e) {
+ int i;
+ int n = poly_coeff_count(e);
+ size_t result = 2, status;
+
+ /*
+ if (!n) {
+ if (EOF == fputs("[0]", stream)) return 0;
+ return 3;
+ }
+ */
+ if (EOF == fputc('[', stream)) return 0;
+ for (i=0; i<n; i++) {
+ if (i) {
+ if (EOF == fputs(", ", stream)) return 0;
+ result += 2;
+ }
+ status = element_out_str(stream, base, poly_coeff(e, i));
+ if (!status) return 0;
+ result += status;
+ }
+ if (EOF == fputc(']', stream)) return 0;
+ return result;
+}
+
+static int poly_snprint(char *s, size_t size, element_ptr e) {
+ int i;
+ int n = poly_coeff_count(e);
+ size_t result = 0, left;
+ int status;
+
+ #define clip_sub() { \
+ result += status; \
+ left = result >= size ? 0 : size - result; \
+ }
+
+ status = snprintf(s, size, "[");
+ if (status < 0) return status;
+ clip_sub();
+
+ for (i=0; i<n; i++) {
+ if (i) {
+ status = snprintf(s + result, left, ", ");
+ if (status < 0) return status;
+ clip_sub();
+ }
+ status = element_snprint(s + result, left, poly_coeff(e, i));
+ if (status < 0) return status;
+ clip_sub();
+ }
+ status = snprintf(s + result, left, "]");
+ if (status < 0) return status;
+ return result + status;
+ #undef clip_sub
+}
+
+static void poly_div(element_ptr quot, element_ptr rem,
+ element_ptr a, element_ptr b) {
+ peptr pq, pr;
+ pfptr pdp = a->field->data;
+ element_t q, r;
+ element_t binv, e0;
+ element_ptr qe;
+ int m, n;
+ int i, k;
+
+ if (element_is0(b)) pbc_die("division by zero");
+ n = poly_degree(b);
+ m = poly_degree(a);
+ if (n > m) {
+ element_set(rem, a);
+ element_set0(quot);
+ return;
+ }
+ element_init(r, a->field);
+ element_init(q, a->field);
+ element_init(binv, pdp->field);
+ element_init(e0, pdp->field);
+ pq = q->data;
+ pr = r->data;
+ element_set(r, a);
+ k = m - n;
+ poly_alloc(q, k + 1);
+ element_invert(binv, poly_coeff(b, n));
+ while (k >= 0) {
+ qe = pq->coeff->item[k];
+ element_mul(qe, binv, pr->coeff->item[m]);
+ for (i=0; i<=n; i++) {
+ element_mul(e0, qe, poly_coeff(b, i));
+ element_sub(pr->coeff->item[i + k], pr->coeff->item[i + k], e0);
+ }
+ k--;
+ m--;
+ }
+ poly_remove_leading_zeroes(r);
+ element_set(quot, q);
+ element_set(rem, r);
+
+ element_clear(q);
+ element_clear(r);
+ element_clear(e0);
+ element_clear(binv);
+}
+
+static void poly_invert(element_ptr res, element_ptr f, element_ptr m) {
+ element_t q, r0, r1, r2;
+ element_t b0, b1, b2;
+ element_t inv;
+
+ element_init(b0, res->field);
+ element_init(b1, res->field);
+ element_init(b2, res->field);
+ element_init(q, res->field);
+ element_init(r0, res->field);
+ element_init(r1, res->field);
+ element_init(r2, res->field);
+ element_init(inv, poly_base_field(res));
+ element_set0(b0);
+ element_set1(b1);
+ element_set(r0, m);
+ element_set(r1, f);
+
+ for (;;) {
+ poly_div(q, r2, r0, r1);
+ if (element_is0(r2)) break;
+ element_mul(b2, b1, q);
+ element_sub(b2, b0, b2);
+ element_set(b0, b1);
+ element_set(b1, b2);
+ element_set(r0, r1);
+ element_set(r1, r2);
+ }
+ element_invert(inv, poly_coeff(r1, 0));
+ poly_const_mul(res, inv, b1);
+ element_clear(inv);
+ element_clear(q);
+ element_clear(r0);
+ element_clear(r1);
+ element_clear(r2);
+ element_clear(b0);
+ element_clear(b1);
+ element_clear(b2);
+}
+
+static void poly_to_polymod_truncate(element_ptr e, element_ptr f) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i;
+ int n;
+ n = poly_coeff_count(f);
+ if (n > p->n) n = p->n;
+
+ for (i=0; i<n; i++) {
+ element_set(coeff[i], poly_coeff(f, i));
+ }
+ for (; i<p->n; i++) {
+ element_set0(coeff[i]);
+ }
+}
+
+static void polymod_to_poly(element_ptr f, element_ptr e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+ poly_alloc(f, n);
+ for (i=0; i<n; i++) {
+ element_set(poly_coeff(f, i), coeff[i]);
+ }
+ poly_remove_leading_zeroes(f);
+}
+
+static void polymod_invert(element_ptr r, element_ptr e) {
+ mfptr p = r->field->data;
+ element_ptr minpoly = p->poly;
+ element_t f, r1;
+
+ element_init(f, minpoly->field);
+ element_init(r1, minpoly->field);
+ polymod_to_poly(f, e);
+
+ poly_invert(r1, f, p->poly);
+
+ poly_to_polymod_truncate(r, r1);
+
+ element_clear(f);
+ element_clear(r1);
+}
+
+static int poly_cmp(element_ptr f, element_ptr g) {
+ int i;
+ int n = poly_coeff_count(f);
+ int n1 = poly_coeff_count(g);
+ if (n != n1) return 1;
+ for (i=0; i<n; i++) {
+ if (element_cmp(poly_coeff(f, i), poly_coeff(g, i))) return 1;
+ }
+ return 0;
+}
+
+static void field_clear_poly(field_ptr f) {
+ pfptr p = f->data;
+ pbc_free(p);
+}
+
+// 2 bytes hold the number of terms, then the terms follow.
+// Bad for sparse polynomials.
+static int poly_length_in_bytes(element_t p) {
+ int count = poly_coeff_count(p);
+ int result = 2;
+ int i;
+ for (i=0; i<count; i++) {
+ result += element_length_in_bytes(poly_coeff(p, i));
+ }
+ return result;
+}
+
+static int poly_to_bytes(unsigned char *buf, element_t p) {
+ int count = poly_coeff_count(p);
+ int result = 2;
+ int i;
+ buf[0] = (unsigned char) count;
+ buf[1] = (unsigned char) (count >> 8);
+ for (i=0; i<count; i++) {
+ result += element_to_bytes(&buf[result], poly_coeff(p, i));
+ }
+ return result;
+}
+
+static int poly_from_bytes(element_t p, unsigned char *buf) {
+ int result = 2;
+ int count = buf[0] + buf[1] * 256;
+ int i;
+ poly_alloc(p, count);
+ for (i=0; i<count; i++) {
+ result += element_from_bytes(poly_coeff(p, i), &buf[result]);
+ }
+ return result;
+}
+
+// Is this useful? This returns to_mpz(constant term).
+static void poly_to_mpz(mpz_t z, element_ptr e) {
+ if (!poly_coeff_count(e)) {
+ mpz_set_ui(z, 0);
+ } else {
+ element_to_mpz(z, poly_coeff(e, 0));
+ }
+}
+
+static void poly_out_info(FILE *str, field_ptr f) {
+ pfptr p = f->data;
+ fprintf(str, "Polynomial ring over ");
+ field_out_info(str, p->field);
+}
+
+static void field_clear_polymod(field_ptr f) {
+ mfptr p = f->data;
+ int i, n = p->n;
+
+ for (i=0; i<n; i++) {
+ element_clear(p->xpwr[i]);
+ }
+ pbc_free(p->xpwr);
+
+ element_clear(p->poly);
+ pbc_free(f->data);
+}
+
+static int polymod_is_sqr(element_ptr e) {
+ int res;
+ mpz_t z;
+ element_t e0;
+
+ element_init(e0, e->field);
+ mpz_init(z);
+ mpz_sub_ui(z, e->field->order, 1);
+ mpz_divexact_ui(z, z, 2);
+
+ element_pow_mpz(e0, e, z);
+ res = element_is1(e0);
+ element_clear(e0);
+ mpz_clear(z);
+ return res;
+}
+
+// Find a square root in a polynomial modulo ring using Cantor-Zassenhaus aka
+// Legendre's method.
+static void polymod_sqrt(element_ptr res, element_ptr a) {
+ // TODO: Use a faster method? See Bernstein.
+ field_t kx;
+ element_t f;
+ element_t r, s;
+ element_t e0;
+ mpz_t z;
+
+ field_init_poly(kx, a->field);
+ mpz_init(z);
+ element_init(f, kx);
+ element_init(r, kx);
+ element_init(s, kx);
+ element_init(e0, a->field);
+
+ poly_alloc(f, 3);
+ element_set1(poly_coeff(f, 2));
+ element_neg(poly_coeff(f, 0), a);
+
+ mpz_sub_ui(z, a->field->order, 1);
+ mpz_divexact_ui(z, z, 2);
+ for (;;) {
+ int i;
+ element_ptr x;
+ element_ptr e1, e2;
+
+ poly_alloc(r, 2);
+ element_set1(poly_coeff(r, 1));
+ x = poly_coeff(r, 0);
+ element_random(x);
+ element_mul(e0, x, x);
+ if (!element_cmp(e0, a)) {
+ element_set(res, x);
+ break;
+ }
+ element_set1(s);
+ //TODO: this can be optimized greatly
+ //since we know r has the form ax + b
+ for (i = mpz_sizeinbase(z, 2) - 1; i >=0; i--) {
+ element_mul(s, s, s);
+ if (poly_degree(s) == 2) {
+ e1 = poly_coeff(s, 0);
+ e2 = poly_coeff(s, 2);
+ element_mul(e0, e2, a);
+ element_add(e1, e1, e0);
+ poly_alloc(s, 2);
+ poly_remove_leading_zeroes(s);
+ }
+ if (mpz_tstbit(z, i)) {
+ element_mul(s, s, r);
+ if (poly_degree(s) == 2) {
+ e1 = poly_coeff(s, 0);
+ e2 = poly_coeff(s, 2);
+ element_mul(e0, e2, a);
+ element_add(e1, e1, e0);
+ poly_alloc(s, 2);
+ poly_remove_leading_zeroes(s);
+ }
+ }
+ }
+ if (poly_degree(s) < 1) continue;
+ element_set1(e0);
+ e1 = poly_coeff(s, 0);
+ e2 = poly_coeff(s, 1);
+ element_add(e1, e1, e0);
+ element_invert(e0, e2);
+ element_mul(e0, e0, e1);
+ element_mul(e2, e0, e0);
+ if (!element_cmp(e2, a)) {
+ element_set(res, e0);
+ break;
+ }
+ }
+
+ mpz_clear(z);
+ element_clear(f);
+ element_clear(r);
+ element_clear(s);
+ element_clear(e0);
+ field_clear(kx);
+}
+
+static int polymod_to_bytes(unsigned char *data, element_t f) {
+ mfptr p = f->field->data;
+ element_t *coeff = f->data;
+ int i, n = p->n;
+ int len = 0;
+ for (i=0; i<n; i++) {
+ len += element_to_bytes(data + len, coeff[i]);
+ }
+ return len;
+}
+
+static int polymod_length_in_bytes(element_t f) {
+ mfptr p = f->field->data;
+ element_t *coeff = f->data;
+ int i, n = p->n;
+ int res = 0;
+
+ for (i=0; i<n; i++) {
+ res += element_length_in_bytes(coeff[i]);
+ }
+
+ return res;
+}
+
+static int polymod_from_bytes(element_t f, unsigned char *data) {
+ mfptr p = f->field->data;
+ element_t *coeff = f->data;
+ int i, n = p->n;
+ int len = 0;
+
+ for (i=0; i<n; i++) {
+ len += element_from_bytes(coeff[i], data + len);
+ }
+ return len;
+}
+
+static void polymod_init(element_t e) {
+ int i;
+ mfptr p = e->field->data;
+ int n = p->n;
+ element_t *coeff;
+ coeff = e->data = pbc_malloc(sizeof(element_t) * n);
+
+ for (i=0; i<n; i++) {
+ element_init(coeff[i], p->field);
+ }
+}
+
+static void polymod_clear(element_t e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_clear(coeff[i]);
+ }
+ pbc_free(e->data);
+}
+
+static void polymod_set_si(element_t e, signed long int x) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+ element_set_si(coeff[0], x);
+ for (i=1; i<n; i++) {
+ element_set0(coeff[i]);
+ }
+}
+
+static void polymod_set_mpz(element_t e, mpz_t z) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+ element_set_mpz(coeff[0], z);
+ for (i=1; i<n; i++) {
+ element_set0(coeff[i]);
+ }
+}
+
+static void polymod_set(element_t e, element_t f) {
+ mfptr p = e->field->data;
+ element_t *dst = e->data, *src = f->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_set(dst[i], src[i]);
+ }
+}
+
+static void polymod_neg(element_t e, element_t f) {
+ mfptr p = e->field->data;
+ element_t *dst = e->data, *src = f->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_neg(dst[i], src[i]);
+ }
+}
+
+static int polymod_cmp(element_ptr f, element_ptr g) {
+ mfptr p = f->field->data;
+ element_t *c1 = f->data, *c2 = g->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ if (element_cmp(c1[i], c2[i])) return 1;
+ }
+ return 0;
+}
+
+static void polymod_add(element_t r, element_t e, element_t f) {
+ mfptr p = r->field->data;
+ element_t *dst = r->data, *s1 = e->data, *s2 = f->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_add(dst[i], s1[i], s2[i]);
+ }
+}
+
+static void polymod_double(element_t r, element_t f) {
+ mfptr p = r->field->data;
+ element_t *dst = r->data, *s1 = f->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_double(dst[i], s1[i]);
+ }
+}
+
+static void polymod_sub(element_t r, element_t e, element_t f) {
+ mfptr p = r->field->data;
+ element_t *dst = r->data, *s1 = e->data, *s2 = f->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_sub(dst[i], s1[i], s2[i]);
+ }
+}
+
+static void polymod_mul_mpz(element_t e, element_t f, mpz_ptr z) {
+ mfptr p = e->field->data;
+ element_t *dst = e->data, *src = f->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_mul_mpz(dst[i], src[i], z);
+ }
+}
+
+static void polymod_mul_si(element_t e, element_t f, signed long int z) {
+ mfptr p = e->field->data;
+ element_t *dst = e->data, *src = f->data;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ element_mul_si(dst[i], src[i], z);
+ }
+}
+
+// Karatsuba multiplication for degree 2 polynomials.
+static void kar_poly_2(element_t *dst, element_t c3, element_t c4, element_t *s1, element_t *s2, element_t *scratch) {
+ element_ptr c01, c02, c12;
+
+ c12 = scratch[0];
+ c02 = scratch[1];
+ c01 = scratch[2];
+
+ element_add(c3, s1[0], s1[1]);
+ element_add(c4, s2[0], s2[1]);
+ element_mul(c01, c3, c4);
+ element_add(c3, s1[0], s1[2]);
+ element_add(c4, s2[0], s2[2]);
+ element_mul(c02, c3, c4);
+ element_add(c3, s1[1], s1[2]);
+ element_add(c4, s2[1], s2[2]);
+ element_mul(c12, c3, c4);
+
+ element_mul(dst[1], s1[1], s2[1]);
+
+ // Constant term.
+ element_mul(dst[0], s1[0], s2[0]);
+
+ // Coefficient of x^4.
+ element_mul(c4, s1[2], s2[2]);
+
+ // Coefficient of x^3.
+ element_add(c3, dst[1], c4);
+ element_sub(c3, c12, c3);
+
+ // Coefficient of x^2.
+ element_add(dst[2], c4, dst[0]);
+ element_sub(c02, c02, dst[2]);
+ element_add(dst[2], dst[1], c02);
+
+ // Coefficient of x.
+ element_sub(c01, c01, dst[0]);
+ element_sub(dst[1], c01, dst[1]);
+}
+
+// Degree 3, 6 polynomial moduli have dedicated routines for multiplication.
+static void polymod_mul_degree3(element_ptr res, element_ptr e, element_ptr f) {
+ mfptr p = res->field->data;
+ element_t *dst = res->data, *s1 = e->data, *s2 = f->data;
+ element_t c3, c4;
+ element_t p0;
+
+ element_init(p0, res->field);
+ element_init(c3, p->field);
+ element_init(c4, p->field);
+
+ kar_poly_2(dst, c3, c4, s1, s2, p0->data);
+
+ polymod_const_mul(p0, c3, p->xpwr[0]);
+ element_add(res, res, p0);
+ polymod_const_mul(p0, c4, p->xpwr[1]);
+ element_add(res, res, p0);
+
+ element_clear(p0);
+ element_clear(c3);
+ element_clear(c4);
+}
+
+static void polymod_mul_degree6(element_ptr res, element_ptr e, element_ptr f) {
+ mfptr p = res->field->data;
+ element_t *dst = res->data, *s0, *s1 = e->data, *s2 = f->data;
+ element_t *a0, *a1, *b0, *b1;
+ element_t p0, p1, p2, p3;
+
+ a0 = s1;
+ a1 = &s1[3];
+ b0 = s2;
+ b1 = &s2[3];
+
+ element_init(p0, res->field);
+ element_init(p1, res->field);
+ element_init(p2, res->field);
+ element_init(p3, res->field);
+
+ s0 = p0->data;
+ s1 = p1->data;
+ s2 = p2->data;
+ element_add(s0[0], a0[0], a1[0]);
+ element_add(s0[1], a0[1], a1[1]);
+ element_add(s0[2], a0[2], a1[2]);
+
+ element_add(s1[0], b0[0], b1[0]);
+ element_add(s1[1], b0[1], b1[1]);
+ element_add(s1[2], b0[2], b1[2]);
+
+ kar_poly_2(s2, s2[3], s2[4], s0, s1, p3->data);
+ kar_poly_2(s0, s0[3], s0[4], a0, b0, p3->data);
+ kar_poly_2(s1, s1[3], s1[4], a1, b1, p3->data);
+
+ element_set(dst[0], s0[0]);
+ element_set(dst[1], s0[1]);
+ element_set(dst[2], s0[2]);
+
+ element_sub(dst[3], s0[3], s0[0]);
+ element_sub(dst[3], dst[3], s1[0]);
+ element_add(dst[3], dst[3], s2[0]);
+
+ element_sub(dst[4], s0[4], s0[1]);
+ element_sub(dst[4], dst[4], s1[1]);
+ element_add(dst[4], dst[4], s2[1]);
+
+ element_sub(dst[5], s2[2], s0[2]);
+ element_sub(dst[5], dst[5], s1[2]);
+
+ // Start reusing part of s0 as scratch space(!)
+ element_sub(s0[0], s2[3], s0[3]);
+ element_sub(s0[0], s0[0], s1[3]);
+ element_add(s0[0], s0[0], s1[0]);
+
+ element_sub(s0[1], s2[4], s0[4]);
+ element_sub(s0[1], s0[1], s1[4]);
+ element_add(s0[1], s0[1], s1[1]);
+
+ polymod_const_mul(p3, s0[0], p->xpwr[0]);
+ element_add(res, res, p3);
+ polymod_const_mul(p3, s0[1], p->xpwr[1]);
+ element_add(res, res, p3);
+ polymod_const_mul(p3, s1[2], p->xpwr[2]);
+ element_add(res, res, p3);
+ polymod_const_mul(p3, s1[3], p->xpwr[3]);
+ element_add(res, res, p3);
+ polymod_const_mul(p3, s1[4], p->xpwr[4]);
+ element_add(res, res, p3);
+
+ element_clear(p0);
+ element_clear(p1);
+ element_clear(p2);
+ element_clear(p3);
+}
+
+// General polynomial modulo ring multiplication.
+static void polymod_mul(element_ptr res, element_ptr e, element_ptr f) {
+ mfptr p = res->field->data;
+ int n = p->n;
+ element_t *dst;
+ element_t *s1 = e->data, *s2 = f->data;
+ element_t prod, p0, c0;
+ int i, j;
+ element_t *high; // Coefficients of x^n, ..., x^{2n-2}.
+
+ high = pbc_malloc(sizeof(element_t) * (n - 1));
+ for (i=0; i<n-1; i++) {
+ element_init(high[i], p->field);
+ element_set0(high[i]);
+ }
+ element_init(prod, res->field);
+ dst = prod->data;
+ element_init(p0, res->field);
+ element_init(c0, p->field);
+
+ for (i=0; i<n; i++) {
+ int ni = n - i;
+ for (j=0; j<ni; j++) {
+ element_mul(c0, s1[i], s2[j]);
+ element_add(dst[i + j], dst[i + j], c0);
+ }
+ for (;j<n; j++) {
+ element_mul(c0, s1[i], s2[j]);
+ element_add(high[j - ni], high[j - ni], c0);
+ }
+ }
+
+ for (i=0; i<n-1; i++) {
+ polymod_const_mul(p0, high[i], p->xpwr[i]);
+ element_add(prod, prod, p0);
+ element_clear(high[i]);
+ }
+ pbc_free(high);
+
+ element_set(res, prod);
+ element_clear(prod);
+ element_clear(p0);
+ element_clear(c0);
+}
+
+static void polymod_square_degree3(element_ptr res, element_ptr e) {
+ // TODO: Investigate if squaring is significantly cheaper than
+ // multiplication. If so convert to Karatsuba.
+ element_t *dst = res->data;
+ element_t *src = e->data;
+ mfptr p = res->field->data;
+ element_t p0;
+ element_t c0, c2;
+ element_ptr c1, c3;
+
+ element_init(p0, res->field);
+ element_init(c0, p->field);
+ element_init(c2, p->field);
+
+ c3 = p0->data;
+ c1 = c3 + 1;
+
+ element_mul(c3, src[0], src[1]);
+ element_mul(c1, src[0], src[2]);
+ element_square(dst[0], src[0]);
+
+ element_mul(c2, src[1], src[2]);
+ element_square(c0, src[2]);
+ element_square(dst[2], src[1]);
+
+ element_add(dst[1], c3, c3);
+
+ element_add(c1, c1, c1);
+ element_add(dst[2], dst[2], c1);
+
+ polymod_const_mul(p0, c0, p->xpwr[1]);
+ element_add(res, res, p0);
+
+ element_add(c2, c2, c2);
+ polymod_const_mul(p0, c2, p->xpwr[0]);
+ element_add(res, res, p0);
+
+ element_clear(p0);
+ element_clear(c0);
+ element_clear(c2);
+}
+
+static void polymod_square(element_ptr res, element_ptr e) {
+ element_t *dst;
+ element_t *src = e->data;
+ mfptr p = res->field->data;
+ int n = p->n;
+ element_t prod, p0, c0;
+ int i, j;
+ element_t *high; // Coefficients of x^n,...,x^{2n-2}.
+
+ high = pbc_malloc(sizeof(element_t) * (n - 1));
+ for (i=0; i<n-1; i++) {
+ element_init(high[i], p->field);
+ element_set0(high[i]);
+ }
+
+ element_init(prod, res->field);
+ dst = prod->data;
+ element_init(p0, res->field);
+ element_init(c0, p->field);
+
+ for (i=0; i<n; i++) {
+ int twicei = 2 * i;
+ element_square(c0, src[i]);
+ if (twicei < n) {
+ element_add(dst[twicei], dst[twicei], c0);
+ } else {
+ element_add(high[twicei - n], high[twicei - n], c0);
+ }
+
+ for (j=i+1; j<n-i; j++) {
+ element_mul(c0, src[i], src[j]);
+ element_add(c0, c0, c0);
+ element_add(dst[i + j], dst[i + j], c0);
+ }
+ for (;j<n; j++) {
+ element_mul(c0, src[i], src[j]);
+ element_add(c0, c0, c0);
+ element_add(high[i + j - n], high[i + j - n], c0);
+ }
+ }
+
+ for (i=0; i<n-1; i++) {
+ polymod_const_mul(p0, high[i], p->xpwr[i]);
+ element_add(prod, prod, p0);
+ element_clear(high[i]);
+ }
+ pbc_free(high);
+
+ element_set(res, prod);
+ element_clear(prod);
+ element_clear(p0);
+ element_clear(c0);
+}
+
+static int polymod_is0(element_ptr e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+
+ for (i=0; i<n; i++) {
+ if (!element_is0(coeff[i])) return 0;
+ }
+ return 1;
+}
+
+static int polymod_is1(element_ptr e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+
+ if (!element_is1(coeff[0])) return 0;
+ for (i=1; i<n; i++) {
+ if (!element_is0(coeff[i])) return 0;
+ }
+ return 1;
+}
+
+static void polymod_set0(element_ptr e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+
+ for (i=0; i<n; i++) {
+ element_set0(coeff[i]);
+ }
+}
+
+static void polymod_set1(element_ptr e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+
+ element_set1(coeff[0]);
+ for (i=1; i<n; i++) {
+ element_set0(coeff[i]);
+ }
+}
+
+static int polymod_sgn(element_ptr e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int res = 0;
+ int i, n = p->n;
+ for (i=0; i<n; i++) {
+ res = element_sgn(coeff[i]);
+ if (res) break;
+ }
+ return res;
+}
+
+static size_t polymod_out_str(FILE *stream, int base, element_ptr e) {
+ size_t result = 2, status;
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+
+ if (EOF == fputc('[', stream)) return 0;
+ for (i=0; i<n; i++) {
+ if (i) {
+ if (EOF == fputs(", ", stream)) return 0;
+ result += 2;
+ }
+ status = element_out_str(stream, base, coeff[i]);
+ if (!status) return 0;
+ result += status;
+ }
+ if (EOF == fputc(']', stream)) return 0;
+ return result;
+}
+
+static int polymod_snprint(char *s, size_t size, element_ptr e) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+ size_t result = 0, left;
+ int status;
+
+ #define clip_sub(void) { \
+ result += status; \
+ left = result >= size ? 0 : size - result; \
+ }
+
+ status = snprintf(s, size, "[");
+ if (status < 0) return status;
+ clip_sub();
+
+ for (i=0; i<n; i++) {
+ if (i) {
+ status = snprintf(s + result, left, ", ");
+ if (status < 0) return status;
+ clip_sub();
+ }
+ status = element_snprint(s + result, left, coeff[i]);
+ if (status < 0) return status;
+ clip_sub();
+ }
+ status = snprintf(s + result, left, "]");
+ if (status < 0) return status;
+ return result + status;
+ #undef clip_sub
+}
+
+static void polymod_set_multiz(element_ptr e, multiz m) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+ if (multiz_is_z(m)) {
+ element_set_multiz(coeff[0], m);
+ for (i = 1; i < n; i++) element_set0(coeff[i]);
+ return;
+ }
+ int max = multiz_count(m);
+ for (i = 0; i < n; i++) {
+ if (i >= max) element_set0(coeff[i]);
+ else element_set_multiz(coeff[i], multiz_at(m, i));
+ }
+}
+
+static int polymod_set_str(element_ptr e, const char *s, int base) {
+ mfptr p = e->field->data;
+ element_t *coeff = e->data;
+ int i, n = p->n;
+ const char *cp = s;
+ element_set0(e);
+ while (*cp && isspace(*cp)) cp++;
+ if (*cp++ != '[') return 0;
+ for (i=0; i<n; i++) {
+ cp += element_set_str(coeff[i], cp, base);
+ while (*cp && isspace(*cp)) cp++;
+ if (i<n-1 && *cp++ != ',') return 0;
+ }
+ if (*cp++ != ']') return 0;
+ return cp - s;
+}
+
+static int polymod_coeff_count(element_ptr e) {
+ UNUSED_VAR(e);
+ mfptr p = e->field->data;
+ return p->n;
+}
+
+static element_ptr polymod_coeff(element_ptr e, int i) {
+ element_t *coeff = e->data;
+ return coeff[i];
+}
+
+static void polymod_to_mpz(mpz_t z, element_ptr e) {
+ element_to_mpz(z, polymod_coeff(e, 0));
+}
+
+// Compute x^n,...,x^{2n-2} mod poly.
+static void compute_x_powers(field_ptr field, element_ptr poly) {
+ mfptr p = field->data;
+ element_t p0;
+ element_ptr pwrn;
+ element_t *coeff, *coeff1;
+ int i, j;
+ int n = p->n;
+ element_t *xpwr;
+
+ xpwr = p->xpwr;
+
+ element_init(p0, field);
+ for (i=0; i<n; i++) {
+ element_init(xpwr[i], field);
+ }
+ pwrn = xpwr[0];
+ poly_to_polymod_truncate(pwrn, poly);
+ element_neg(pwrn, pwrn);
+
+ for (i=1; i<n; i++) {
+ coeff = xpwr[i-1]->data;
+ coeff1 = xpwr[i]->data;
+
+ element_set0(coeff1[0]);
+ for (j=1; j<n; j++) {
+ element_set(coeff1[j], coeff[j - 1]);
+ }
+ polymod_const_mul(p0, coeff[n - 1], pwrn);
+ element_add(xpwr[i], xpwr[i], p0);
+ }
+ element_clear(p0);
+}
+
+static void polymod_out_info(FILE *str, field_ptr f) {
+ mfptr p = f->data;
+ element_fprintf(str, "Extension, poly = %B, base field = ", p->poly);
+ field_out_info(str, p->field);
+}
+
+// Sets d = gcd(f, g).
+static void poly_gcd(element_ptr d, element_ptr f, element_ptr g) {
+ element_t a, b, q, r;
+ element_init(a, d->field);
+ element_init(b, d->field);
+ element_init(q, d->field);
+ element_init(r, d->field);
+
+ element_set(a, f);
+ element_set(b, g);
+ for(;;) {
+ //TODO: don't care about q
+ poly_div(q, r, a, b);
+ if (element_is0(r)) break;
+ element_set(a, b);
+ element_set(b, r);
+ }
+ element_set(d, b);
+ element_clear(a);
+ element_clear(b);
+ element_clear(q);
+ element_clear(r);
+}
+
+// Sets f = c g where c is the inverse of the leading coefficient of g.
+static void poly_make_monic(element_t f, element_t g) {
+ int n = poly_coeff_count(g);
+ int i;
+ element_ptr e0;
+ poly_alloc(f, n);
+ if (!n) return;
+
+ e0 = poly_coeff(f, n - 1);
+ element_invert(e0, poly_coeff(g, n - 1));
+ for (i=0; i<n-1; i++) {
+ element_mul(poly_coeff(f, i), poly_coeff(g, i), e0);
+ }
+ element_set1(e0);
+}
+
+// The above should be static.
+
+void field_init_poly(field_ptr f, field_ptr base_field) {
+ field_init(f);
+ pfptr p = f->data = pbc_malloc(sizeof(*p));
+ p->field = base_field;
+ p->mapbase = element_field_to_poly;
+ f->field_clear = field_clear_poly;
+ f->init = poly_init;
+ f->clear = poly_clear;
+ f->set_si = poly_set_si;
+ f->set_multiz = poly_set_multiz;
+ f->set_mpz = poly_set_mpz;
+ f->to_mpz = poly_to_mpz;
+ f->out_str = poly_out_str;
+ f->snprint = poly_snprint;
+ f->set = poly_set;
+ f->sign = poly_sgn;
+ f->add = poly_add;
+ f->doub = poly_double;
+ f->is0 = poly_is0;
+ f->is1 = poly_is1;
+ f->set0 = poly_set0;
+ f->set1 = poly_set1;
+ f->sub = poly_sub;
+ f->neg = poly_neg;
+ f->mul = poly_mul;
+ f->mul_mpz = poly_mul_mpz;
+ f->mul_si = poly_mul_si;
+ f->cmp = poly_cmp;
+ f->out_info = poly_out_info;
+ f->item_count = poly_coeff_count;
+ f->item = poly_coeff;
+
+ f->to_bytes = poly_to_bytes;
+ f->from_bytes = poly_from_bytes;
+ f->fixed_length_in_bytes = -1;
+ f->length_in_bytes = poly_length_in_bytes;
+}
+
+void poly_set_coeff(element_ptr e, element_ptr a, int n) {
+ peptr p = e->data;
+ if (p->coeff->count < n + 1) {
+ poly_alloc(e, n + 1);
+ }
+ element_ptr e0 = p->coeff->item[n];
+ element_set(e0, a);
+ if (p->coeff->count == n + 1 && element_is0(a)) poly_remove_leading_zeroes(e);
+}
+
+void poly_set_coeff0(element_ptr e, int n) {
+ peptr p = e->data;
+ if (n < p->coeff->count) {
+ element_set0(p->coeff->item[n]);
+ if (n == p->coeff->count - 1) poly_remove_leading_zeroes(e);
+ }
+}
+
+void poly_set_coeff1(element_ptr e, int n) {
+ peptr p = e->data;
+ if (p->coeff->count < n + 1) {
+ poly_alloc(e, n + 1);
+ }
+ element_set1(p->coeff->item[n]);
+}
+
+void poly_setx(element_ptr f) {
+ poly_alloc(f, 2);
+ element_set1(poly_coeff(f, 1));
+ element_set0(poly_coeff(f, 0));
+}
+
+void poly_const_mul(element_ptr res, element_ptr a, element_ptr poly) {
+ int i, n = poly_coeff_count(poly);
+ poly_alloc(res, n);
+ for (i=0; i<n; i++) {
+ element_mul(poly_coeff(res, i), a, poly_coeff(poly, i));
+ }
+ poly_remove_leading_zeroes(res);
+}
+
+void poly_random_monic(element_ptr f, int deg) {
+ int i;
+ poly_alloc(f, deg + 1);
+ for (i=0; i<deg; i++) {
+ element_random(poly_coeff(f, i));
+ }
+ element_set1(poly_coeff(f, i));
+}
+
+int polymod_field_degree(field_t f) {
+ mfptr p = f->data;
+ return p->n;
+}
+
+void field_init_polymod(field_ptr f, element_ptr poly) {
+ pfptr pdp = poly->field->data;
+ field_init(f);
+ mfptr p = f->data = pbc_malloc(sizeof(*p));
+ p->field = pdp->field;
+ p->mapbase = element_field_to_poly;
+ element_init(p->poly, poly->field);
+ element_set(p->poly, poly);
+ int n = p->n = poly_degree(p->poly);
+ f->field_clear = field_clear_polymod;
+ f->init = polymod_init;
+ f->clear = polymod_clear;
+ f->set_si = polymod_set_si;
+ f->set_mpz = polymod_set_mpz;
+ f->out_str = polymod_out_str;
+ f->snprint = polymod_snprint;
+ f->set_multiz = polymod_set_multiz;
+ f->set_str = polymod_set_str;
+ f->set = polymod_set;
+ f->sign = polymod_sgn;
+ f->add = polymod_add;
+ f->doub = polymod_double;
+ f->sub = polymod_sub;
+ f->neg = polymod_neg;
+ f->is0 = polymod_is0;
+ f->is1 = polymod_is1;
+ f->set0 = polymod_set0;
+ f->set1 = polymod_set1;
+ f->cmp = polymod_cmp;
+ f->to_mpz = polymod_to_mpz;
+ f->item_count = polymod_coeff_count;
+ f->item = polymod_coeff;
+ switch(n) {
+ case 3:
+ f->mul = polymod_mul_degree3;
+ f->square = polymod_square_degree3;
+ break;
+ case 6:
+ f->mul = polymod_mul_degree6;
+ f->square = polymod_square;
+ break;
+ default:
+ f->mul = polymod_mul;
+ f->square = polymod_square;
+ break;
+ }
+
+ f->mul_mpz = polymod_mul_mpz;
+ f->mul_si = polymod_mul_si;
+ f->random = polymod_random;
+ f->from_hash = polymod_from_hash;
+ f->invert = polymod_invert;
+ f->is_sqr = polymod_is_sqr;
+ f->sqrt = polymod_sqrt;
+ f->to_bytes = polymod_to_bytes;
+ f->from_bytes = polymod_from_bytes;
+ f->out_info = polymod_out_info;
+
+ if (pdp->field->fixed_length_in_bytes < 0) {
+ f->fixed_length_in_bytes = -1;
+ f->length_in_bytes = polymod_length_in_bytes;
+ } else {
+ f->fixed_length_in_bytes = pdp->field->fixed_length_in_bytes * poly_degree(poly);
+ }
+ mpz_pow_ui(f->order, p->field->order, n);
+
+ p->xpwr = pbc_malloc(sizeof(element_t) * n);
+ compute_x_powers(f, poly);
+}
+
+field_ptr poly_base_field(element_t f) {
+ return ((pfptr) f->field->data)->field;
+}
+
+void polymod_const_mul(element_ptr res, element_ptr a, element_ptr e) {
+ // a lies in R, e in R[x].
+ element_t *coeff = e->data, *dst = res->data;
+ int i, n = polymod_field_degree(e->field);
+
+ for (i=0; i<n; i++) {
+ element_mul(dst[i], coeff[i], a);
+ }
+}
+
+struct checkgcd_scope_var {
+ mpz_ptr z, deg;
+ field_ptr basef;
+ element_ptr xpow, x, f, g;
+};
+
+// Returns 0 if gcd(x^q^{n/d} - x, f) = 1, 1 otherwise.
+static int checkgcd(mpz_ptr fac, unsigned int mul, struct checkgcd_scope_var *v) {
+ UNUSED_VAR(mul);
+ mpz_divexact(v->z, v->deg, fac);
+ mpz_pow_ui(v->z, v->basef->order, mpz_get_ui(v->z));
+ element_pow_mpz(v->xpow, v->x, v->z);
+ element_sub(v->xpow, v->xpow, v->x);
+ if (element_is0(v->xpow)) return 1;
+ polymod_to_poly(v->g, v->xpow);
+ poly_gcd(v->g, v->f, v->g);
+ return poly_degree(v->g) != 0;
+}
+
+// Returns 1 if polynomial is irreducible, 0 otherwise.
+// A polynomial f(x) is irreducible in F_q[x] if and only if:
+// (1) f(x) | x^{q^n} - x, and
+// (2) gcd(f(x), x^{q^{n/d}} - x) = 1 for all primes d | n.
+// (Recall GF(p) is the splitting field for x^p - x.)
+int poly_is_irred(element_ptr f) {
+ int res = 0;
+ element_t xpow, x, g;
+ field_ptr basef = poly_base_field(f);
+ field_t rxmod;
+
+ // 0, units are not irreducibles.
+ // Assume coefficients are from a field.
+ if (poly_degree(f) <= 0) return 0;
+ // Degree 1 polynomials are always irreducible.
+ if (poly_degree(f) == 1) return 1;
+
+ field_init_polymod(rxmod, f);
+ element_init(xpow, rxmod);
+ element_init(x, rxmod);
+ element_init(g, f->field);
+ element_set1(polymod_coeff(x, 1));
+
+ // The degree fits in an unsigned int but I'm lazy and want to use my
+ // mpz trial division code.
+ mpz_t deg, z;
+ mpz_init(deg);
+ mpz_init(z);
+ mpz_set_ui(deg, poly_degree(f));
+
+ struct checkgcd_scope_var v = {.z = z, .deg = deg, .basef = basef,
+ .xpow = xpow, .x = x, .f = f, .g = g};
+ if (!pbc_trial_divide((int(*)(mpz_t,unsigned,void*))checkgcd, &v, deg, NULL)) {
+ // By now condition (2) has been satisfied. Check (1).
+ mpz_pow_ui(z, basef->order, poly_degree(f));
+ element_pow_mpz(xpow, x, z);
+ element_sub(xpow, xpow, x);
+ if (element_is0(xpow)) res = 1;
+ }
+
+ mpz_clear(deg);
+ mpz_clear(z);
+ element_clear(g);
+ element_clear(xpow);
+ element_clear(x);
+ field_clear(rxmod);
+ return res;
+}
+
+void element_field_to_poly(element_ptr f, element_ptr g) {
+ poly_alloc(f, 1);
+ element_set(poly_coeff(f, 0), g);
+ poly_remove_leading_zeroes(f);
+}
+
+void element_field_to_polymod(element_ptr f, element_ptr g) {
+ mfptr p = f->field->data;
+ element_t *coeff = f->data;
+ int i, n = p->n;
+ element_set(coeff[0], g);
+ for (i=1; i<n; i++) {
+ element_set0(coeff[i]);
+ }
+}
+
+// Returns 0 when a root exists and sets root to one of the roots.
+int poly_findroot(element_ptr root, element_ptr poly) {
+ // Compute gcd(x^q - x, poly).
+ field_t fpxmod;
+ element_t p, x, r, fac, g;
+ mpz_t q;
+
+ mpz_init(q);
+ mpz_set(q, poly_base_field(poly)->order);
+
+ field_init_polymod(fpxmod, poly);
+ element_init(p, fpxmod);
+ element_init(x, fpxmod);
+ element_init(g, poly->field);
+ element_set1(((element_t *) x->data)[1]);
+pbc_info("findroot: degree %d...", poly_degree(poly));
+ element_pow_mpz(p, x, q);
+ element_sub(p, p, x);
+
+ polymod_to_poly(g, p);
+ element_clear(p);
+ poly_gcd(g, g, poly);
+ poly_make_monic(g, g);
+ element_clear(x);
+ field_clear(fpxmod);
+
+ if (!poly_degree(g)) {
+ printf("no roots!\n");
+ mpz_clear(q);
+ element_clear(g);
+ return -1;
+ }
+
+ // Cantor-Zassenhaus algorithm.
+ element_init(fac, g->field);
+ element_init(x, g->field);
+ element_set_si(x, 1);
+ mpz_sub_ui(q, q, 1);
+ mpz_divexact_ui(q, q, 2);
+ element_init(r, g->field);
+ for (;;) {
+ if (poly_degree(g) == 1) break; // Found a root!
+step_random:
+ poly_random_monic(r, 1);
+ // TODO: evaluate at g instead of bothering with gcd
+ poly_gcd(fac, r, g);
+
+ if (poly_degree(fac) > 0) {
+ poly_make_monic(g, fac);
+ } else {
+ field_init_polymod(fpxmod, g);
+ int n;
+ element_init(p, fpxmod);
+
+ poly_to_polymod_truncate(p, r);
+pbc_info("findroot: degree %d...", poly_degree(g));
+ element_pow_mpz(p, p, q);
+
+ polymod_to_poly(r, p);
+ element_clear(p);
+ field_clear(fpxmod);
+
+ element_add(r, r, x);
+ poly_gcd(fac, r, g);
+ n = poly_degree(fac);
+ if (n > 0 && n < poly_degree(g)) {
+ poly_make_monic(g, fac);
+ } else {
+ goto step_random;
+ }
+ }
+ }
+pbc_info("findroot: found root");
+ element_neg(root, poly_coeff(g, 0));
+ element_clear(r);
+ mpz_clear(q);
+ element_clear(x);
+ element_clear(g);
+ element_clear(fac);
+ return 0;
+}