// Multinomials over Z. // e.g. [[1, 2], 3, [4, [5, 6]]] means // (1 + 2y) + 3 x + (4 + (5 + 6z)y)x^2 // Convenient interchange format for different groups, rings, and fields. // TODO: Canonicalize, e.g. [[1]], 0, 0] --> 1. #include #include #include // for intptr_t #include #include #include "pbc_utils.h" #include "pbc_field.h" #include "pbc_multiz.h" #include "pbc_random.h" #include "pbc_fp.h" #include "pbc_memory.h" #include "misc/darray.h" // Per-element data. struct multiz_s { // Either it's an mpz, or a list of mpzs. char type; union { mpz_t z; darray_t a; }; }; enum { T_MPZ, T_ARR, }; static multiz multiz_new_empty_list(void) { multiz ep = pbc_malloc(sizeof(*ep)); ep->type = T_ARR; darray_init(ep->a); return ep; } void multiz_append(element_ptr x, element_ptr e) { multiz l = x->data; darray_append(l->a, e->data); } static multiz multiz_new(void) { multiz ep = pbc_malloc(sizeof(*ep)); ep->type = T_MPZ; mpz_init(ep->z); return ep; } static void f_init(element_ptr e) { e->data = multiz_new(); } static void multiz_free(multiz ep) { switch(ep->type) { case T_MPZ: mpz_clear(ep->z); break; default: PBC_ASSERT(T_ARR == ep->type, "no such type"); darray_forall(ep->a, (void(*)(void*))multiz_free); darray_clear(ep->a); break; } pbc_free(ep); } static void f_clear(element_ptr e) { multiz_free(e->data); } element_ptr multiz_new_list(element_ptr e) { element_ptr x = pbc_malloc(sizeof(*x)); element_init_same_as(x, e); multiz_free(x->data); x->data = multiz_new_empty_list(); multiz_append(x, e); return x; } static void f_set_si(element_ptr e, signed long int op) { multiz_free(e->data); f_init(e); multiz ep = e->data; mpz_set_si(ep->z, op); } static void f_set_mpz(element_ptr e, mpz_ptr z) { multiz_free(e->data); f_init(e); multiz ep = e->data; mpz_set(ep->z, z); } static void f_set0(element_ptr e) { multiz_free(e->data); f_init(e); } static void f_set1(element_ptr e) { multiz_free(e->data); f_init(e); multiz ep = e->data; mpz_set_ui(ep->z, 1); } static size_t multiz_out_str(FILE *stream, int base, multiz ep) { switch(ep->type) { case T_MPZ: return mpz_out_str(stream, base, ep->z); default: PBC_ASSERT(T_ARR == ep->type, "no such type"); fputc('[', stream); size_t res = 1; int n = darray_count(ep->a); int i; for(i = 0; i < n; i++) { if (i) res += 2, fputs(", ", stream); res += multiz_out_str(stream, base, darray_at(ep->a, i)); } fputc(']', stream); res++; return res; } } static size_t f_out_str(FILE *stream, int base, element_ptr e) { return multiz_out_str(stream, base, e->data); } void multiz_to_mpz(mpz_ptr z, multiz ep) { while(ep->type == T_ARR) ep = darray_at(ep->a, 0); PBC_ASSERT(T_MPZ == ep->type, "no such type"); mpz_set(z, ep->z); } static void f_to_mpz(mpz_ptr z, element_ptr a) { multiz_to_mpz(z, a->data); } static int multiz_sgn(multiz ep) { while(ep->type == T_ARR) ep = darray_at(ep->a, 0); PBC_ASSERT(T_MPZ == ep->type, "no such type"); return mpz_sgn(ep->z); } static int f_sgn(element_ptr a) { return multiz_sgn(a->data); } static void add_to_x(void *data, multiz x, void (*fun)(mpz_t, const mpz_t, void *scope_ptr), void *scope_ptr); static multiz multiz_new_unary(const multiz y, void (*fun)(mpz_t, const mpz_t, void *scope_ptr), void *scope_ptr) { multiz x = pbc_malloc(sizeof(*x)); switch(y->type) { case T_MPZ: x->type = T_MPZ; mpz_init(x->z); fun(x->z, y->z, scope_ptr); break; default: PBC_ASSERT(T_ARR == ep->type, "no such type"); x->type = T_ARR; darray_init(x->a); darray_forall4(y->a, (void(*)(void*,void*,void*,void*))add_to_x, x, fun, scope_ptr); break; } return x; } static void add_to_x(void *data, multiz x, void (*fun)(mpz_t, const mpz_t, void *scope_ptr), void *scope_ptr) { darray_append(x->a, multiz_new_unary(data, fun, scope_ptr)); } static void mpzset(mpz_t dst, const mpz_t src, void *scope_ptr) { UNUSED_VAR(scope_ptr); mpz_set(dst, src); } static multiz multiz_clone(multiz y) { return multiz_new_unary(y, (void(*)(mpz_t, const mpz_t, void *))mpzset, NULL); } static multiz multiz_new_bin(const multiz a, const multiz b, void (*fun)(mpz_t, const mpz_t, const mpz_t)) { if (T_MPZ == a->type) { if (T_MPZ == b->type) { multiz x = multiz_new(); fun(x->z, a->z, b->z); return x; } else { multiz x = multiz_clone(b); multiz z = x; PBC_ASSERT(T_ARR == z->type, "no such type"); while(z->type == T_ARR) z = darray_at(z->a, 0); fun(z->z, a->z, z->z); return x; } } else { PBC_ASSERT(T_ARR == a->type, "no such type"); if (T_MPZ == b->type) { multiz x = multiz_clone(a); multiz z = x; PBC_ASSERT(T_ARR == z->type, "no such type"); while(z->type == T_ARR) z = darray_at(z->a, 0); fun(z->z, b->z, z->z); return x; } else { PBC_ASSERT(T_ARR == b->type, "no such type"); int m = darray_count(a->a); int n = darray_count(b->a); int min = m < n ? m : n; int max = m > n ? m : n; multiz x = multiz_new_empty_list(); int i; for(i = 0; i < min; i++) { multiz z = multiz_new_bin(darray_at(a->a, i), darray_at(b->a, i), fun); darray_append(x->a, z); } multiz zero = multiz_new(); for(; i < max; i++) { multiz z = multiz_new_bin(m > n ? darray_at(a->a, i) : zero, n > m ? darray_at(b->a, i) : zero, fun); darray_append(x->a, z); } multiz_free(zero); return x; } } } static multiz multiz_new_add(const multiz a, const multiz b) { return multiz_new_bin(a, b, mpz_add); } static void f_add(element_ptr n, element_ptr a, element_ptr b) { multiz delme = n->data; n->data = multiz_new_add(a->data, b->data); multiz_free(delme); } static multiz multiz_new_sub(const multiz a, const multiz b) { return multiz_new_bin(a, b, mpz_sub); } static void f_sub(element_ptr n, element_ptr a, element_ptr b) { multiz delme = n->data; n->data = multiz_new_sub(a->data, b->data); multiz_free(delme); } static void mpzmul(mpz_t x, const mpz_t y, const mpz_t z) { mpz_mul(x, y, z); } static multiz multiz_new_mul(const multiz a, const multiz b) { if (T_MPZ == a->type) { // Multiply each coefficient of b by a->z. return multiz_new_unary(b, (void(*)(mpz_t, const mpz_t, void *))mpzmul, a->z); } else { PBC_ASSERT(T_ARR == a->type, "no such type"); if (T_MPZ == b->type) { // Multiply each coefficient of a by b->z. return multiz_new_unary(a, (void(*)(mpz_t, const mpz_t, void *))mpzmul, b->z); } else { PBC_ASSERT(T_ARR == b->type, "no such type"); int m = darray_count(a->a); int n = darray_count(b->a); int max = m + n - 1; multiz x = multiz_new_empty_list(); int i; multiz zero = multiz_new(); for(i = 0; i < max; i++) { multiz z = multiz_new(); int j; for (j = 0; j <= i; j++) { multiz y = multiz_new_mul(j < m ? darray_at(a->a, j) : zero, i - j < n ? darray_at(b->a, i - j) : zero); multiz t = multiz_new_add(z, y); multiz_free(y); multiz_free(z); z = t; } darray_append(x->a, z); } multiz_free(zero); return x; } } } static void f_mul(element_ptr n, element_ptr a, element_ptr b) { multiz delme = n->data; n->data = multiz_new_mul(a->data, b->data); multiz_free(delme); } static void f_mul_mpz(element_ptr n, element_ptr a, mpz_ptr z) { multiz delme = n->data; n->data = multiz_new_unary(a->data, (void(*)(mpz_t, const mpz_t, void *))mpzmul, z); multiz_free(delme); } static void mulsi(mpz_t x, const mpz_t y, signed long *i) { mpz_mul_si(x, y, *i); } static void f_mul_si(element_ptr n, element_ptr a, signed long int z) { multiz delme = n->data; n->data = multiz_new_unary(a->data, (void(*)(mpz_t, const mpz_t, void *))mulsi, &z); multiz_free(delme); } static void mpzneg(mpz_t dst, const mpz_t src, void *scope_ptr) { UNUSED_VAR(scope_ptr); mpz_neg(dst, src); } static multiz multiz_new_neg(multiz z) { return multiz_new_unary(z, (void(*)(mpz_t, const mpz_t, void *))mpzneg, NULL); } static void f_set(element_ptr n, element_ptr a) { multiz delme = n->data; n->data = multiz_clone(a->data); multiz_free(delme); } static void f_neg(element_ptr n, element_ptr a) { multiz delme = n->data; n->data = multiz_new_neg(a->data); multiz_free(delme); } static void f_div(element_ptr c, element_ptr a, element_ptr b) { mpz_t d; mpz_init(d); element_to_mpz(d, b); multiz delme = c->data; c->data = multiz_new_unary(a->data, (void(*)(mpz_t, const mpz_t, void *))mpz_tdiv_q, d); mpz_clear(d); multiz_free(delme); } // Doesn't make sense if order is infinite. static void f_random(element_ptr n) { multiz delme = n->data; f_init(n); multiz_free(delme); } static void f_from_hash(element_ptr n, void *data, int len) { mpz_t z; mpz_init(z); mpz_import(z, len, -1, 1, -1, 0, data); f_set_mpz(n, z); mpz_clear(z); } static int f_is1(element_ptr n) { multiz ep = n->data; return ep->type == T_MPZ && !mpz_cmp_ui(ep->z, 1); } int multiz_is0(multiz m) { return m->type == T_MPZ && mpz_is0(m->z); } static int f_is0(element_ptr n) { return multiz_is0(n->data); } static int f_item_count(element_ptr e) { multiz z = e->data; if (T_MPZ == z->type) return 0; return darray_count(z->a); } // TODO: Redesign multiz so this doesn't leak. static element_ptr f_item(element_ptr e, int i) { multiz z = e->data; if (T_MPZ == z->type) return NULL; element_ptr r = malloc(sizeof(*r)); r->field = e->field; r->data = darray_at(z->a, i); return r; } // Usual meaning when both are integers. // Otherwise, compare coefficients. static int multiz_cmp(multiz a, multiz b) { if (T_MPZ == a->type) { if (T_MPZ == b->type) { // Simplest case: both are integers. return mpz_cmp(a->z, b->z); } // Leading coefficient of b. while(T_ARR == b->type) b = darray_last(b->a); PBC_ASSERT(T_MPZ == b->type, "no such type"); return -mpz_sgn(b->z); } PBC_ASSERT(T_ARR == a->type, "no such type"); if (T_MPZ == b->type) { // Leading coefficient of a. while(T_ARR == a->type) a = darray_last(a->a); PBC_ASSERT(T_MPZ == a->type, "no such type"); return mpz_sgn(a->z); } PBC_ASSERT(T_ARR == b->type, "no such type"); int m = darray_count(a->a); int n = darray_count(b->a); if (m > n) { // Leading coefficient of a. while(T_ARR == a->type) a = darray_last(a->a); PBC_ASSERT(T_MPZ == a->type, "no such type"); return mpz_sgn(a->z); } if (n > m) { // Leading coefficient of b. while(T_ARR == b->type) b = darray_last(b->a); PBC_ASSERT(T_MPZ == b->type, "no such type"); return -mpz_sgn(b->z); } for(n--; n >= 0; n--) { int i = multiz_cmp(darray_at(a->a, n), darray_at(b->a, n)); if (i) return i; } return 0; } static int f_cmp(element_ptr x, element_ptr y) { return multiz_cmp(x->data, y->data); } static void f_field_clear(field_t f) { UNUSED_VAR (f); } // OpenSSL convention: // 4 bytes containing length // followed by number in big-endian, most-significant bit set if negative // (prepending null byte if necessary) // Positive numbers also the same as mpz_out_raw. static int z_to_bytes(unsigned char *data, element_t e) { mpz_ptr z = e->data; size_t msb = mpz_sizeinbase(z, 2); size_t n = 4; size_t i; if (!(msb % 8)) { data[4] = 0; n++; } if (mpz_sgn(z) < 0) { mpz_export(data + n, NULL, 1, 1, 1, 0, z); data[4] |= 128; } else { mpz_export(data + n, NULL, 1, 1, 1, 0, z); } n += (msb + 7) / 8 - 4; for (i=0; i<4; i++) { data[i] = (n >> 8 * (3 - i)); } n += 4; return n; } static int z_from_bytes(element_t e, unsigned char *data) { unsigned char *ptr; size_t i, n; mpz_ptr z = e->data; mpz_t z1; int neg = 0; mpz_init(z1); mpz_set_ui(z, 0); ptr = data; n = 0; for (i=0; i<4; i++) { n += ((unsigned int) *ptr) << 8 * (3 - i); ptr++; } if (data[4] & 128) { neg = 1; data[4] &= 127; } for (i=0; idata, 2) + 7) / 8 + 4; } static void f_out_info(FILE *out, field_ptr f) { UNUSED_VAR(f); fprintf(out, "Z multinomials"); } static int f_set_str(element_ptr e, const char *s, int base) { // TODO: Square brackets. mpz_t z; mpz_init(z); int result = pbc_mpz_set_str(z, s, base); f_set_mpz(e, z); mpz_clear(z); return result; } static void f_set_multiz(element_ptr e, multiz m) { multiz delme = e->data; e->data = multiz_clone(m); multiz_free(delme); } void field_init_multiz(field_ptr f) { field_init(f); f->init = f_init; f->clear = f_clear; f->set_si = f_set_si; f->set_mpz = f_set_mpz; f->set_multiz = f_set_multiz; f->set_str = f_set_str; f->out_str = f_out_str; f->sign = f_sgn; f->add = f_add; f->sub = f_sub; f->set = f_set; f->mul = f_mul; f->mul_mpz = f_mul_mpz; f->mul_si = f_mul_si; f->neg = f_neg; f->cmp = f_cmp; f->div = f_div; f->random = f_random; f->from_hash = f_from_hash; f->is1 = f_is1; f->is0 = f_is0; f->set0 = f_set0; f->set1 = f_set1; f->field_clear = f_field_clear; f->to_bytes = z_to_bytes; f->from_bytes = z_from_bytes; f->to_mpz = f_to_mpz; f->length_in_bytes = z_length_in_bytes; f->item = f_item; f->item_count = f_item_count; f->out_info = f_out_info; mpz_set_ui(f->order, 0); f->data = NULL; f->fixed_length_in_bytes = -1; } int multiz_is_z(multiz m) { return T_MPZ == m->type; } int multiz_count(multiz m) { if (T_ARR != m->type) return -1; return darray_count(m->a); } multiz multiz_at(multiz m, int i) { PBC_ASSERT(T_ARR == m->type, "wrong type"); PBC_ASSERT(darray_count(m->a) > i, "out of bounds"); return darray_at(m->a, i); }