#include #include #include #include // for intptr_t #include #include #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; icoeff->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 n1) { big = f; n = n1; n1 = poly_coeff_count(f); } else { big = g; } poly_alloc(sum, n1); for (i=0; i n1) { big = f; n = n1; n1 = poly_coeff_count(f); } else { big = g; } poly_alloc(diff, n1); for (i=0; idata; peptr pg = g->data; int i, n; n = pg->coeff->count; poly_alloc(f, n); for (i=0; icoeff->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; icoeff->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; icoeff->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; icoeff->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; icoeff->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; idata; int i, n = polymod_field_degree(e->field); for (i=0; i= size ? 0 : size - result; \ } status = snprintf(s, size, "["); if (status < 0) return status; clip_sub(); for (i=0; ifield->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; in; 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; ifield->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; idata; 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> 8); for (i=0; idata; 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; ixpwr[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; ifield->data; element_t *coeff = f->data; int i, n = p->n; int res = 0; for (i=0; ifield->data; element_t *coeff = f->data; int i, n = p->n; int len = 0; for (i=0; ifield->data; int n = p->n; element_t *coeff; coeff = e->data = pbc_malloc(sizeof(element_t) * n); for (i=0; ifield); } } 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; idata); } 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; ifield->data; element_t *coeff = e->data; int i, n = p->n; element_set_mpz(coeff[0], z); for (i=1; ifield->data; element_t *dst = e->data, *src = f->data; int i, n = p->n; for (i=0; ifield->data; element_t *dst = e->data, *src = f->data; int i, n = p->n; for (i=0; ifield->data; element_t *c1 = f->data, *c2 = g->data; int i, n = p->n; for (i=0; ifield->data; element_t *dst = r->data, *s1 = e->data, *s2 = f->data; int i, n = p->n; for (i=0; ifield->data; element_t *dst = r->data, *s1 = f->data; int i, n = p->n; for (i=0; ifield->data; element_t *dst = r->data, *s1 = e->data, *s2 = f->data; int i, n = p->n; for (i=0; ifield->data; element_t *dst = e->data, *src = f->data; int i, n = p->n; for (i=0; ifield->data; element_t *dst = e->data, *src = f->data; int i, n = p->n; for (i=0; ifield->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; ifield); 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; ixpwr[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; ifield); 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; ixpwr[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; ifield->data; element_t *coeff = e->data; int i, n = p->n; if (!element_is1(coeff[0])) return 0; for (i=1; ifield->data; element_t *coeff = e->data; int i, n = p->n; for (i=0; ifield->data; element_t *coeff = e->data; int i, n = p->n; element_set1(coeff[0]); for (i=1; ifield->data; element_t *coeff = e->data; int res = 0; int i, n = p->n; for (i=0; ifield->data; element_t *coeff = e->data; int i, n = p->n; if (EOF == fputc('[', stream)) return 0; for (i=0; ifield->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; ifield->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; ifield->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; idata; coeff1 = xpwr[i]->data; element_set0(coeff1[0]); for (j=1; jdata; 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; idata = 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; idata; 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; iz, 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; iorder); 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; }