aboutsummaryrefslogtreecommitdiffstats
path: root/moon-abe/pbc-0.5.14/arith/ternary_extension_field.c
diff options
context:
space:
mode:
Diffstat (limited to 'moon-abe/pbc-0.5.14/arith/ternary_extension_field.c')
-rw-r--r--moon-abe/pbc-0.5.14/arith/ternary_extension_field.c950
1 files changed, 950 insertions, 0 deletions
diff --git a/moon-abe/pbc-0.5.14/arith/ternary_extension_field.c b/moon-abe/pbc-0.5.14/arith/ternary_extension_field.c
new file mode 100644
index 00000000..3c79e3bd
--- /dev/null
+++ b/moon-abe/pbc-0.5.14/arith/ternary_extension_field.c
@@ -0,0 +1,950 @@
+/* $GF(3^m) = GF(3)[x]/(x^m + x^t + 2)$
+ $GF(3^{2*m}) = GF(3^m)[x]/(x^2 + 1)$
+ $GF(3^{3*m}) = GF(3^m)[x]/(x^3 - x -1)$
+ $GF(3^{6*m}) = GF(3^{2*m})[x]/(x^3 - x -1)$
+
+ The "gf3_*" functions are for $GF(3)$.
+ The "gf3m_*" functions are for $GF(3^m)$.
+ The "gf32m_*" functions are for $GF(3^{2*m})$.
+ The "gf33m_*" functions are for $GF(3^{3*m})$ and $GF(3^{6*m})$.
+
+ (gf3m field_t).data is a pointer of struct params
+ (gf3m element_t).data is a pointer of unsigned long
+ (gf32m element_t).data is gf32m_ptr
+ (gf33m element_t).data is gf33m_ptr */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <gmp.h>
+#include "pbc_utils.h"
+#include "pbc_memory.h"
+#include "pbc_field.h"
+
+typedef unsigned long gf3;
+
+typedef struct { /* private data of $GF(3^m)$ */
+ unsigned int len; /* the number of native machine integers required to represent one GF(3^m) element */
+ unsigned int m; /* the irreducible polynomial is $x^m + x^t + 2$ */
+ unsigned int t; /* the irreducible polynomial is $x^m + x^t + 2$ */
+ element_ptr p; /* $p$ is the irreducible polynomial. */
+} params;
+
+typedef struct {
+ element_t _0, _1;
+} gf32m_s;
+
+typedef gf32m_s *gf32m_ptr;
+
+typedef struct {
+ element_t _0, _1, _2;
+} gf33m_s;
+
+typedef gf33m_s *gf33m_ptr;
+
+#define W (sizeof(unsigned long)*8) /* number of GF(3) elements in one processor integer */
+#define PARAM(e) ((params *)e->field->data)
+#define LEN(e) (PARAM(e)->len)
+#define SIZE(e) (LEN(e) * 2 * sizeof(unsigned long))
+#define DATA1(e) ((unsigned long*)e->data)
+#define DATA2(e) ((unsigned long*)e->data + LEN(e))
+#define GF32M(e) ((gf32m_s *)e->data)
+#define GF33M(e) ((gf33m_s *)e->data)
+#define BASE(e) ((field_ptr)e->field->data)
+#define print(e) {printf(#e": "); element_out_str(stdout, 10, e); printf("\n");}
+
+static size_t gf3m_out_str(FILE *stream, int base, element_t e) {
+ if (base != 10 && base != 16)
+ pbc_die("only support base 10 and base 16");
+ size_t size = 0;
+ unsigned i;
+ unsigned long *d = DATA1(e);
+ for (i = 0; i < LEN(e) * 2; i++) {
+ if (base == 16)
+ size += fprintf(stream, "0x%lx,", d[i]);
+ else
+ size += fprintf(stream, "%lu,", d[i]);
+ }
+ return size;
+}
+
+/* $a <- 0$ */
+static void gf3m_zero(element_t a) {
+ memset(a->data, 0, SIZE(a));
+}
+
+static void gf3m_init(element_t e) {
+ e->data = pbc_malloc(SIZE(e));
+ gf3m_zero(e);
+}
+
+static void gf3m_clear(element_t e) {
+ pbc_free(e->data);
+}
+
+/* $e <- a$ */
+static void gf3m_assign(element_t e, element_t a) {
+ memcpy(e->data, a->data, SIZE(a));
+}
+
+/* $a <- a/x$. $len$ is the number of elements in $a$ */
+static void shift_down(unsigned int len, unsigned long a[]) {
+ unsigned long h = 0;
+ const unsigned long x = 1ul << (W - 1);
+ int i;
+ for (i = len - 1; i >= 0; i--) {
+ unsigned long l = a[i] & 1;
+ a[i] >>= 1;
+ if (h)
+ a[i] |= x;
+ h = l;
+ }
+}
+
+/* $e <- e/x$ */
+static void gf3m_shift_down(element_t e) {
+ shift_down(LEN(e), DATA1(e));
+ shift_down(LEN(e), DATA2(e));
+}
+
+/* $a <- a*x$. $len$ is the number of elements in $a$ */
+static void shift_up(unsigned int len, unsigned long a[]) {
+ unsigned long l = 0;
+ const unsigned long x = 1ul << (W - 1), y = x - 1;
+ unsigned i;
+ for (i = 0; i < len; i++) {
+ unsigned long h = a[i] & x;
+ a[i] = ((a[i] & y) << 1) | l;
+ l = h ? 1 : 0;
+ }
+}
+
+/* $e <- e*x$ */
+static void gf3m_shift_up(element_t e) {
+ shift_up(LEN(e), DATA1(e));
+ shift_up(LEN(e), DATA2(e));
+}
+
+/* return the coefficient of $x^pos$ in $e$ */
+static unsigned gf3m_get(element_t e, unsigned pos) {
+ unsigned long *a1 = DATA1(e), *a2 = DATA2(e);
+ unsigned x = pos / W;
+ unsigned long y = 1ul << (pos % W), v1 = a1[x] & y, v2 = a2[x] & y;
+ return v1 ? 1 : (v2 ? 2 : 0);
+}
+
+/* set the coefficient of $x^pos$ as 1 */
+static void gf3m_set(element_t e, unsigned pos, unsigned value) {
+ unsigned long *a = DATA1(e);
+ /* assert value == 0, 1 or 2 */
+ if (value == 2)
+ a = DATA2(e);
+ if (value)
+ a[pos / W] |= 1ul << (pos % W);
+}
+
+/* $e <- a+b$ */
+static void gf3m_add(element_t e, element_t a, element_t b) {
+ unsigned long *e1 = DATA1(e), *e2 = DATA2(e), *a1 = DATA1(a),
+ *a2 = DATA2(a), *b1 = DATA1(b), *b2 = DATA2(b);
+ unsigned i;
+ for (i = 0; i < LEN(e); i++, e1++, e2++, a1++, a2++, b1++, b2++) {
+ unsigned long t = (*a1 | *a2) & (*b1 | *b2), c1 = t ^ (*a1 | *b1), c2 =
+ t ^ (*a2 | *b2);
+ *e1 = c1;
+ *e2 = c2;
+ }
+}
+
+/* $e <- x-y$ */
+static void gf3m_sub(element_t e, element_t a, element_t b) {
+ unsigned long *e1 = DATA1(e), *e2 = DATA2(e), *a1 = DATA1(a),
+ *a2 = DATA2(a), *b1 = DATA2(b), *b2 = DATA1(b);
+ unsigned i;
+ for (i = 0; i < LEN(e); i++, e1++, e2++, a1++, a2++, b1++, b2++) {
+ unsigned long t = (*a1 | *a2) & (*b1 | *b2), c1 = t ^ (*a1 | *b1), c2 =
+ t ^ (*a2 | *b2);
+ *e1 = c1;
+ *e2 = c2;
+ }
+}
+
+/* return 0 if $a == b$ in $GF(3^m)$, 1 otherwise. */
+static int gf3m_cmp(element_t a, element_t b) {
+ unsigned long *pa = DATA1(a), *pb = DATA1(b);
+ unsigned i;
+ for (i = 0; i < LEN(a) * 2; i++, pa++, pb++)
+ if (*pa != *pb)
+ return 1;
+ return 0;
+}
+
+/* $a <- 1$ */
+static void gf3m_one(element_t a) {
+ gf3m_zero(a);
+ *DATA1(a) = 1;
+}
+
+static int gf3m_is0(element_t e) {
+ unsigned i;
+ for (i = 0; i < LEN(e) * 2; i++)
+ if (DATA1(e)[i])
+ return 0;
+ return 1;
+}
+
+static int gf3m_is1(element_t e) {
+ unsigned i;
+ if (DATA1(e)[0] != 1)
+ return 0;
+ for (i = 1; i < LEN(e) * 2; i++)
+ if (DATA1(e)[i])
+ return 0;
+ return 1;
+}
+
+/* set $a$ to be a random element in $GF(3^m)$ */
+static void gf3m_random(element_t a) {
+ /* TODO: use uniform distribution? */
+ params *c = PARAM(a);
+ unsigned rm = c->m % W;
+ const unsigned long i1 = ~0ul;
+ unsigned long i2 = (1ul << rm) - 1;
+ unsigned long *a1 = DATA1(a), *a2 = DATA2(a);
+ unsigned i;
+ for (i = 0; i < c->len - 1; i++, a1++, a2++) { /* TODO: if $RAND_MAX < i1$ ? */
+ *a1 = rand() & i1;
+ *a2 = rand() & i1 & ~(*a1); /* assuring there is no bit that a1[x] & a2[x] == 1 */
+ }
+ unsigned long x = rm ? i2 : i1;
+ *a1 = rand() & x;
+ *a2 = rand() & x & ~(*a1);
+}
+
+static void swap(unsigned long *a, unsigned long *b) {
+ *a ^= *b;
+ *b ^= *a;
+ *a ^= *b;
+}
+
+/* $y <- (-x)$ */
+static void gf3m_neg(element_t y, element_t x) {
+ unsigned long *a1 = DATA1(x), *a2 = DATA2(x), *c1 = DATA1(y),
+ *c2 = DATA2(y);
+ if (a1 == c1) {
+ unsigned i;
+ for (i = 0; i < LEN(y); i++, a1++, a2++)
+ swap(a1, a2);
+ } else {
+ memcpy(c1, a2, SIZE(y) / 2);
+ memcpy(c2, a1, SIZE(y) / 2);
+ }
+}
+
+/* doing reduction
+ * The function returns the value of $a$ modulo $the irreducible trinomial$.
+ * $degree$ equals the degree of $a$.
+ * $2*len$ is the number of elements in $a$ */
+static void gf3m_reduct(element_t e, unsigned len, unsigned degree) {
+ // the $len$ argument exists because sometimes $len != p->len$
+ params *p = PARAM(e);
+ unsigned old = p->len;
+ p->len = len;
+ element_t px;
+ element_init(px, e->field);
+ gf3m_set(px, degree, 1);
+ gf3m_set(px, degree - p->m + p->t, 1);
+ gf3m_set(px, degree - p->m, 2);
+ while (degree >= p->m) {
+ unsigned v = gf3m_get(e, degree);
+ if (v == 1)
+ gf3m_sub(e, e, px);
+ else if (v == 2)
+ gf3m_add(e, e, px);
+ degree--;
+ gf3m_shift_down(px);
+ }
+ element_clear(px);
+ p->len = old;
+}
+
+/* doing multiplication of $n \in \{0,1,2\}$ and $a$ in $GF(3^m)$
+ * The function sets $e <- n * a$. */
+static void gf3m_f1(element_t e, unsigned n, element_t a) {
+ /* assert $e$ is not $a$ */
+ if (n == 0)
+ memset(DATA1(e), 0, SIZE(e));
+ else if (n == 1)
+ memcpy(DATA1(e), DATA1(a), SIZE(e));
+ else {
+ memcpy(DATA1(e), DATA2(a), SIZE(e) / 2);
+ memcpy(DATA2(e), DATA1(a), SIZE(e) / 2);
+ }
+}
+
+/* $e <- e*x mod p(x)$ */
+static void gf3m_f2(element_t e) {
+ params *p = PARAM(e);
+ gf3m_shift_up(e);
+ unsigned v = gf3m_get(e, p->m);
+ if (v == 1)
+ gf3m_sub(e, e, p->p);
+ else if (v == 2)
+ gf3m_add(e, e, p->p);
+}
+
+/* doing multiplication in GF(3^m)
+ * The function sets $e == a*b \in GF(3^m)$ */
+static void gf3m_mult(element_t e, element_ptr a, element_t b) {
+ params *p = PARAM(a);
+ element_t aa, t, c;
+ element_init(aa, a->field);
+ element_set(aa, a);
+ a = aa; // clone $a$
+ element_init(t, a->field);
+ element_init(c, a->field);
+ unsigned i;
+ for (i = 0; i < p->m; i++) {
+ unsigned v = gf3m_get(b, i);
+ gf3m_f1(t, v, a); /* t == b[i]*a in GF(3^m) */
+ gf3m_add(c, c, t); /* c += b[i]*a in GF(3^m) */
+ gf3m_f2(a); /* a == a*x in GF(3^m) */
+ }
+ element_set(e, c);
+ element_clear(t);
+ element_clear(c);
+ element_clear(aa);
+}
+
+/* $e <- x^3$ */
+static void gf3m_cubic(element_t e, element_t x) {
+ /* TODO: faster algorithm */
+ params *p = PARAM(x);
+ unsigned old = p->len;
+ unsigned len = (3 * p->m - 2 + W - 1) / W; /* length of $b1 */
+ p->len = len;
+ element_t a;
+ element_init(a, x->field);
+ unsigned i;
+ for (i = 0; i < p->m; i++) {
+ p->len = old;
+ unsigned v = gf3m_get(x, i);
+ p->len = len;
+ gf3m_set(a, 3 * i, v);
+ }
+ gf3m_reduct(a, len, 3 * p->m - 3);
+ p->len = old;
+ memcpy(DATA1(e), DATA1(a), SIZE(e) / 2);
+ memcpy(DATA2(e), DATA1(a) + len, SIZE(e) / 2);
+ element_clear(a);
+}
+
+/* multiplication modulo 3 of two elements in GF(3)
+ * for example, $mult(2,2) == 1$, and $mult(1,2) == 2$ */
+static unsigned gf3_mult(unsigned a, unsigned b) {
+ static const unsigned l[] = { 0, 1, 2, 0, 1 };
+ return l[a * b];
+}
+
+static void gf3m_swap(element_t a, element_t b) {
+ unsigned long *p = DATA1(a);
+ a->data = b->data;
+ b->data = p;
+}
+
+/* computing the inversion of an element $a$ in GF(3^m), i.e., $e <- a^{-1}$
+ The algorithm is by Tim Kerins, Emanuel Popovici and William Marnane
+ in the paper of "Algorithms and Architectures for use in FPGA",
+ Lecture Notes in Computer Science, 2004, Volume 3203/2004, 74-83.
+ Note that $U$ must have an extra bit, i.e, (_m + W - 1) // W == (_m + W) // W */
+static void gf3m_invert(element_t e, element_t a) {
+ struct field_s *f = a->field;
+ params *p = PARAM(a);
+ unsigned lenA = p->len;
+ unsigned lenS = (3 * p->m + W - 1) / W;
+ p->len = lenS;
+ element_t S, R, t, U, V, t2;
+ element_init(S, f);
+ element_init(R, f);
+ element_init(t, f);
+ memcpy(DATA1(S), DATA1(p->p), lenA * sizeof(unsigned long)); /* S = p(x) */
+ memcpy(DATA1(S) + lenS, DATA1(p->p) + lenA, lenA * sizeof(unsigned long));
+ memcpy(DATA1(R), DATA1(a), lenA * sizeof(unsigned long)); /* R = _clone(a) */
+ memcpy(DATA1(R) + lenS, DATA1(a) + lenA, lenA * sizeof(unsigned long));
+ p->len = lenA;
+ element_init(U, f);
+ gf3m_one(U);
+ element_init(V, f);
+ element_init(t2, f);
+ unsigned d = 0, i, r_m, s_m, q, x;
+ for (i = 0; i < p->m * 2; i++) {
+ p->len = lenS;
+ r_m = gf3m_get(R, p->m), s_m = gf3m_get(S, p->m);
+ if (r_m == 0) {
+ gf3m_shift_up(R); /* R = xR */
+ p->len = lenA;
+ gf3m_f2(U); /* U = xU mod p */
+ d++;
+ } else {
+ q = gf3_mult(r_m, s_m);
+ gf3m_f1(t, q, R);
+ gf3m_sub(S, S, t); /* S = S-qR */
+ gf3m_shift_up(S); /* S = xS */
+ p->len = lenA;
+ gf3m_f1(t2, q, U);
+ gf3m_sub(V, V, t2); /* V = V-qU */
+ if (d == 0) {
+ gf3m_swap(S, R);
+ gf3m_swap(U, V);
+ gf3m_f2(U); /* U = xU mod p*/
+ d++;
+ } else {
+ x = gf3m_get(U, 0);
+ if (x == 1) /* assuring x|U */
+ gf3m_add(U, U, p->p);
+ else if (x == 2)
+ gf3m_sub(U, U, p->p);
+ gf3m_shift_down(U); /* divide U by $x$ */
+ d--;
+ }
+ }
+ }
+ p->len = lenS;
+ r_m = gf3m_get(R, p->m); /* assume r_m is not zero */
+ p->len = lenA;
+ if (r_m == 2)
+ gf3m_neg(U, U);
+ memcpy(e->data, U->data, lenA * 2 * sizeof(unsigned long));
+ element_clear(S);
+ element_clear(R);
+ element_clear(U);
+ element_clear(V);
+ element_clear(t);
+ element_clear(t2);
+}
+
+static void gf3m_sqrt(element_t e, element_t a) {
+ field_ptr f = e->field;
+ mpz_t t;
+ mpz_init(t); // t == (field_order + 1) / 4
+ mpz_set(t, f->order);
+ mpz_add_ui(t, t, 1);
+ mpz_tdiv_q_2exp(t, t, 2);
+ element_pow_mpz(e, a, t);
+ mpz_clear(t);
+}
+
+int gf3m_to_bytes(unsigned char *d, element_ptr e) {
+ unsigned long *a = DATA1(e), *b = DATA2(e);
+ unsigned long i, j;
+ for (i = 0; i < LEN(e); i++, a++, b++) {
+ for (j = 0; j < sizeof(unsigned long) * 8; j += 8) {
+ *(d++) = (unsigned char) ((*a) >> j);
+ *(d++) = (unsigned char) ((*b) >> j);
+ }
+ }
+ return SIZE(e);
+}
+
+int gf3m_from_bytes(element_ptr e, unsigned char *d) {
+ unsigned long *a = DATA1(e), *b = DATA2(e);
+ unsigned i;
+ int j;
+ for (i = 0; i < LEN(e); i++, a++, b++, d += sizeof(unsigned long) * 2) {
+ *a = 0, *b = 0;
+ j = 2 * sizeof(unsigned long) - 2;
+ while (j >= 0) {
+ *a <<= 8, *b <<= 8;
+ *a += d[j];
+ *b += d[j + 1];
+ j -= 2;
+ }
+ }
+ return SIZE(e);
+}
+
+static void field_clear_gf3m(field_t f) {
+ params *p = f->data;
+ gf3m_clear(p->p);
+ pbc_free(p->p);
+ pbc_free(p);
+}
+
+/* initialize the finite field as $GF(3^m)$, whose irreducible polynomial is with the degree of $m$ */
+void field_init_gf3m(field_t f, unsigned m, unsigned t) {
+ params *p = pbc_malloc(sizeof(*p));
+ p->len = (m + (W - 1) + 1) / W; /* extra one bit for $_p$ */
+ p->m = m;
+ p->t = t;
+ p->p = pbc_malloc(sizeof(*(p->p)));
+ p->p->field = f;
+ p->p->data = pbc_malloc(2 * sizeof(unsigned long) * p->len);
+ memset(p->p->data, 0, 2 * sizeof(unsigned long) * p->len);
+ unsigned long *p1 = p->p->data, *p2 = p1 + p->len;
+ p2[0] = 1; /* _p == x^m+x^t+2 */
+ unsigned int p_t = p->t;
+ p1[p_t / W] |= 1ul << (p_t % W);
+ p1[m / W] |= 1ul << (m % W);
+
+ field_init(f);
+ f->field_clear = field_clear_gf3m;
+ f->init = gf3m_init;
+ f->clear = gf3m_clear;
+ f->set = gf3m_assign;
+ f->set0 = gf3m_zero;
+ f->set1 = gf3m_one;
+ f->is0 = gf3m_is0;
+ f->is1 = gf3m_is1;
+ f->add = gf3m_add;
+ f->sub = gf3m_sub;
+ f->mul = gf3m_mult;
+ f->cubic = gf3m_cubic;
+ f->invert = gf3m_invert;
+ f->neg = gf3m_neg;
+ f->random = gf3m_random;
+ f->cmp = gf3m_cmp;
+ f->sqrt = gf3m_sqrt;
+ f->from_bytes = gf3m_from_bytes;
+ f->to_bytes = gf3m_to_bytes;
+ f->out_str = gf3m_out_str;
+ f->fixed_length_in_bytes = 2 * sizeof(unsigned long) * p->len;
+ f->data = p;
+ f->name = "GF(3^m)";
+
+ mpz_set_ui(f->order, 3);
+ mpz_pow_ui(f->order, f->order, p->m);
+}
+
+static size_t gf32m_out_str(FILE *stream, int base, element_t e) {
+ UNUSED_VAR(base);
+ element_ptr e0 = GF32M(e)->_0, e1 = GF32M(e)->_1;
+ size_t size = 0;
+ size += element_out_str(stream, base, e0);
+ size += element_out_str(stream, base, e1);
+ return size;
+}
+
+static void gf32m_init(element_t e) {
+ e->data = pbc_malloc(sizeof(gf32m_s));
+ gf32m_ptr p = (gf32m_ptr) e->data;
+ field_ptr base = BASE(e);
+ element_init(p->_0, base);
+ element_init(p->_1, base);
+}
+
+static void gf32m_clear(element_t e) {
+ gf32m_ptr p = (gf32m_ptr) e->data;
+ element_clear(p->_0);
+ element_clear(p->_1);
+ pbc_free(e->data);
+}
+
+static void gf32m_set0(element_t e) {
+ element_ptr e0 = GF32M(e)->_0, e1 = GF32M(e)->_1;
+ element_set0(e0);
+ element_set0(e1);
+}
+
+static void gf32m_set1(element_t e) {
+ element_ptr e0 = GF32M(e)->_0, e1 = GF32M(e)->_1;
+ element_set1(e0);
+ element_set0(e1);
+}
+
+static int gf32m_item_count(element_t e) {
+ UNUSED_VAR(e);
+ return 2;
+}
+
+static element_ptr gf32m_item(element_t a, int i) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1;
+ return i == 0 ? a0 : a1;
+}
+
+static void gf32m_assign(element_t e, element_t a) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1, e0 = GF32M(e)->_0, e1 =
+ GF32M(e)->_1;
+ element_set(e0, a0);
+ element_set(e1, a1);
+}
+
+static void gf32m_random(element_t e) {
+ element_ptr e0 = GF32M(e)->_0, e1 = GF32M(e)->_1;
+ element_random(e0);
+ element_random(e1);
+}
+
+/* return 0 if $a == b$, 1 otherwise */
+static int gf32m_cmp(element_t a, element_t b) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1, b0 = GF32M(b)->_0, b1 =
+ GF32M(b)->_1;
+ return element_cmp(a0, b0) || element_cmp(a1, b1);
+}
+
+/* $c <- a+b$ */
+static void gf32m_add(element_t c, element_t a, element_t b) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1, b0 = GF32M(b)->_0, b1 =
+ GF32M(b)->_1, c0 = GF32M(c)->_0, c1 = GF32M(c)->_1;
+ element_add(c0, a0, b0);
+ element_add(c1, a1, b1);
+}
+
+/* $c <- a-b$ */
+static void gf32m_sub(element_t c, element_t a, element_t b) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1, b0 = GF32M(b)->_0, b1 =
+ GF32M(b)->_1, c0 = GF32M(c)->_0, c1 = GF32M(c)->_1;
+ element_sub(c0, a0, b0);
+ element_sub(c1, a1, b1);
+}
+
+/* $c <- (-a)$ */
+static void gf32m_neg(element_t c, element_t a) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1, c0 = GF32M(c)->_0, c1 =
+ GF32M(c)->_1;
+ element_neg(c0, a0);
+ element_neg(c1, a1);
+}
+
+/* $e<- a*b$ */
+static void gf32m_mult(element_t e, element_t a, element_t b) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1, b0 = GF32M(b)->_0, b1 =
+ GF32M(b)->_1, e0 = GF32M(e)->_0, e1 = GF32M(e)->_1;
+ field_ptr base = BASE(a);
+ element_t a0b0, a1b1, t0, t1, c1;
+ element_init(a0b0, base);
+ element_init(a1b1, base);
+ element_init(t0, base);
+ element_init(t1, base);
+ element_init(c1, base);
+ element_mul(a0b0, a0, b0);
+ element_mul(a1b1, a1, b1);
+ element_add(t0, a1, a0);
+ element_add(t1, b1, b0);
+ element_mul(c1, t0, t1); // c1 == (a1+a0)*(b1+b0)
+ element_sub(c1, c1, a1b1);
+ element_sub(c1, c1, a0b0);
+ element_ptr c0 = a0b0;
+ element_sub(c0, c0, a1b1); // c0 == a0*b0 - a1*b1
+ element_set(e0, c0);
+ element_set(e1, c1);
+ element_clear(a0b0);
+ element_clear(a1b1);
+ element_clear(t0);
+ element_clear(t1);
+ element_clear(c1);
+}
+
+/* $e <- a^3$ */
+static void gf32m_cubic(element_t e, element_t a) {
+ element_ptr a0 = GF32M(a)->_0, a1 = GF32M(a)->_1, e0 = GF32M(e)->_0, e1 =
+ GF32M(e)->_1;
+ field_ptr base = BASE(a);
+ element_t c0, c1;
+ element_init(c0, base);
+ element_init(c1, base);
+ element_cubic(c0, a0);
+ element_cubic(c1, a1);
+ element_neg(c1, c1); // c1 == -(a1^3)
+ element_set(e0, c0);
+ element_set(e1, c1);
+ element_clear(c0);
+ element_clear(c1);
+}
+
+void field_clear_gf32m(field_t f) {
+ UNUSED_VAR(f);
+}
+
+/* initialize the finite field as $base_field[x]/(x^2 + 1)$, whose base field is $b$ */
+void field_init_gf32m(field_t f, field_t b) {
+ field_init(f);
+ f->data = b;
+ f->field_clear = field_clear_gf32m;
+ f->init = gf32m_init;
+ f->clear = gf32m_clear;
+ f->set = gf32m_assign;
+ f->set0 = gf32m_set0;
+ f->set1 = gf32m_set1;
+ f->random = gf32m_random;
+ f->cmp = gf32m_cmp;
+ f->add = gf32m_add;
+ f->sub = gf32m_sub;
+ f->neg = gf32m_neg;
+ f->mul = gf32m_mult;
+ f->cubic = gf32m_cubic;
+ f->item_count = gf32m_item_count;
+ f->item = gf32m_item;
+ f->out_str = gf32m_out_str;
+ mpz_pow_ui(f->order, b->order, 2);
+ f->name = "GF(3^{2*m})";
+}
+
+static size_t gf33m_out_str(FILE *stream, int base, element_t e) {
+ UNUSED_VAR(base);
+ element_ptr e0 = GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ size_t size = 0;
+ size += element_out_str(stream, base, e0);
+ size += element_out_str(stream, base, e1);
+ size += element_out_str(stream, base, e2);
+ return size;
+}
+
+static void gf33m_init(element_t e) {
+ e->data = pbc_malloc(sizeof(gf33m_s));
+ gf33m_ptr p = (gf33m_ptr) e->data;
+ field_ptr base = BASE(e);
+ element_init(p->_0, base);
+ element_init(p->_1, base);
+ element_init(p->_2, base);
+}
+
+static void gf33m_clear(element_t e) {
+ gf33m_ptr p = (gf33m_ptr) e->data;
+ element_clear(p->_0);
+ element_clear(p->_1);
+ element_clear(p->_2);
+ pbc_free(e->data);
+}
+
+static void gf33m_set0(element_t e) {
+ element_ptr e0 = GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ element_set0(e0);
+ element_set0(e1);
+ element_set0(e2);
+}
+
+static void gf33m_set1(element_t e) {
+ element_ptr e0 = GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ element_set1(e0);
+ element_set0(e1);
+ element_set0(e2);
+}
+
+static int gf33m_item_count(element_t e) {
+ UNUSED_VAR(e);
+ return 3;
+}
+
+static element_ptr gf33m_item(element_t a, int i) {
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2;
+ return i == 0 ? a0 : (i == 1 ? a1 : a2);
+}
+
+static void gf33m_assign(element_t e, element_t a) {
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2, e0 =
+ GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ element_set(e0, a0);
+ element_set(e1, a1);
+ element_set(e2, a2);
+}
+
+static void gf33m_random(element_t e) {
+ element_ptr e0 = GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ element_random(e0);
+ element_random(e1);
+ element_random(e2);
+}
+
+/* return 0 if $a == b$, 1 otherwise */
+static int gf33m_cmp(element_t a, element_t b) {
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2, b0 =
+ GF33M(b)->_0, b1 = GF33M(b)->_1, b2 = GF33M(b)->_2;
+ return element_cmp(a0, b0) || element_cmp(a1, b1) || element_cmp(a2, b2);
+}
+
+/* $c <- a+b$ */
+static void gf33m_add(element_t c, element_t a, element_t b) {
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2, b0 =
+ GF33M(b)->_0, b1 = GF33M(b)->_1, b2 = GF33M(b)->_2, c0 =
+ GF33M(c)->_0, c1 = GF33M(c)->_1, c2 = GF33M(c)->_2;
+ element_add(c0, a0, b0);
+ element_add(c1, a1, b1);
+ element_add(c2, a2, b2);
+}
+
+/* $c <- a-b$ */
+static void gf33m_sub(element_t c, element_t a, element_t b) {
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2, b0 =
+ GF33M(b)->_0, b1 = GF33M(b)->_1, b2 = GF33M(b)->_2, c0 =
+ GF33M(c)->_0, c1 = GF33M(c)->_1, c2 = GF33M(c)->_2;
+ element_sub(c0, a0, b0);
+ element_sub(c1, a1, b1);
+ element_sub(c2, a2, b2);
+}
+
+/* $c <- a*b$ */
+static void gf33m_mult(element_t e, element_t a, element_t b) {
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2, b0 =
+ GF33M(b)->_0, b1 = GF33M(b)->_1, b2 = GF33M(b)->_2, e0 =
+ GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ field_ptr base = BASE(e);
+ element_t t0, t1, c1, a0b0, a1b1, a2b2;
+ element_init(t0, base);
+ element_init(t1, base);
+ element_init(c1, base);
+ element_init(a0b0, base);
+ element_init(a1b1, base);
+ element_init(a2b2, base);
+ element_mul(a0b0, a0, b0);
+ element_mul(a1b1, a1, b1);
+ element_mul(a2b2, a2, b2);
+ element_ptr d0 = a0b0;
+ element_add(t0, a1, a0);
+ element_add(t1, b1, b0);
+ element_t d1;
+ element_init(d1, base);
+ element_mul(d1, t0, t1);
+ element_sub(d1, d1, a1b1);
+ element_sub(d1, d1, a0b0);
+ element_add(t0, a2, a0);
+ element_add(t1, b2, b0);
+ element_t d2;
+ element_init(d2, base);
+ element_mul(d2, t0, t1);
+ element_add(d2, d2, a1b1);
+ element_sub(d2, d2, a2b2);
+ element_sub(d2, d2, a0b0);
+ element_add(t0, a2, a1);
+ element_add(t1, b2, b1);
+ element_t d3;
+ element_init(d3, base);
+ element_mul(d3, t0, t1);
+ element_sub(d3, d3, a2b2);
+ element_sub(d3, d3, a1b1);
+ element_ptr d4 = a2b2;
+ element_add(t0, d0, d3);
+ element_ptr c0 = t0;
+ element_add(c1, d1, d3);
+ element_add(c1, c1, d4);
+ element_add(t1, d2, d4);
+ element_ptr c2 = t1;
+ element_set(e0, c0);
+ element_set(e1, c1);
+ element_set(e2, c2);
+ element_clear(t0);
+ element_clear(t1);
+ element_clear(c1);
+ element_clear(a0b0);
+ element_clear(a1b1);
+ element_clear(a2b2);
+ element_clear(d1);
+ element_clear(d2);
+ element_clear(d3);
+}
+
+/* $e <- a^3$ */
+static void gf33m_cubic(element_t e, element_t a) {
+ field_ptr base = BASE(a);
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2, e0 =
+ GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ element_t a03, a13, a23;
+ element_init(a03, base);
+ element_init(a13, base);
+ element_init(a23, base);
+ element_cubic(a03, a0);
+ element_cubic(a13, a1);
+ element_cubic(a23, a2);
+ element_add(a03, a03, a13);
+ element_add(a03, a03, a23);
+ element_ptr c0 = a03;
+ element_sub(a13, a13, a23);
+ element_ptr c1 = a13;
+ element_ptr c2 = a23;
+ element_set(e0, c0);
+ element_set(e1, c1);
+ element_set(e2, c2);
+ element_clear(a03);
+ element_clear(a13);
+ element_clear(a23);
+}
+
+/* $e <- a^{-1}$ */
+static void gf33m_invert(element_t e, element_t a) {
+ element_ptr a0 = GF33M(a)->_0, a1 = GF33M(a)->_1, a2 = GF33M(a)->_2, e0 =
+ GF33M(e)->_0, e1 = GF33M(e)->_1, e2 = GF33M(e)->_2;
+ field_ptr base = BASE(e);
+ element_t a02, a12, a22;
+ element_init(a02, base);
+ element_init(a12, base);
+ element_init(a22, base);
+ element_mul(a02, a0, a0);
+ element_mul(a12, a1, a1);
+ element_mul(a22, a2, a2);
+ element_t v0;
+ element_init(v0, base);
+ element_sub(v0, a0, a2); // v0 == a0-a2
+ element_t delta;
+ element_init(delta, base);
+ element_mul(delta, v0, a02); // delta = (a0-a2)*(a0^2), free
+ element_sub(v0, a1, a0); // v0 == a1-a0
+ element_t c0;
+ element_init(c0, base);
+ element_mul(c0, v0, a12); // c0 == (a1-a0)*(a1^2)
+ element_add(delta, delta, c0); // delta = (a0-a2)*(a0^2) + (a1-a0)*(a1^2)
+ element_sub(v0, a2, v0); // v0 == a2-(a1-a0) = a0-a1+a2
+ element_t c1;
+ element_init(c1, base);
+ element_mul(c1, v0, a22); // c1 == (a0-a1+a2)*(a2^2)
+ element_add(delta, delta, c1); // delta = (a0-a2)*(a0^2) + (a1-a0)*(a1^2) + (a0-a1+a2)*(a2^2)
+ element_invert(delta, delta); // delta = [(a0-a2)*(a0^2) + (a1-a0)*(a1^2) + (a0-a1+a2)*(a2^2)] ^ {-1}
+ element_add(v0, a02, a22); // v0 == a0^2+a2^2
+ element_t c2;
+ element_init(c2, base);
+ element_mul(c2, a0, a2); // c2 == a0*a2
+ element_sub(c0, v0, c2); // c0 == a0^2+a2^2-a0*a2
+ element_add(v0, a1, a2); // v0 == a1+a2
+ element_t c3;
+ element_init(c3, base);
+ element_mul(c3, a1, v0); // c3 == a1*(a1+a2)
+ element_sub(c0, c0, c3); // c0 == a0^2+a2^2-a0*a2-a1*(a1+a2)
+ element_mul(c0, c0, delta); // c0 *= delta
+ element_mul(c1, a0, a1); // c1 == a0*a1
+ element_sub(c1, a22, c1); // c1 == a2^2-a0*a1
+ element_mul(c1, c1, delta); // c1 *= delta
+ element_sub(c2, a12, c2); // c2 == a1^2-a0*a2
+ element_sub(c2, c2, a22); // c2 == a1^2-a0*a2-a2^2
+ element_mul(c2, c2, delta); // c2 *= delta
+ element_set(e0, c0);
+ element_set(e1, c1);
+ element_set(e2, c2);
+ element_clear(a02);
+ element_clear(a12);
+ element_clear(a22);
+ element_clear(v0);
+ element_clear(delta);
+ element_clear(c0);
+ element_clear(c1);
+ element_clear(c2);
+ element_clear(c3);
+}
+
+void field_clear_gf33m(field_t f) {
+ UNUSED_VAR(f);
+}
+
+/* initialize the finite field as $base_field[x]/(x^3 - x - 1)$, whose base field is $b$ */
+void field_init_gf33m(field_t f, field_t b) {
+ field_init(f);
+ f->data = b;
+ f->field_clear = field_clear_gf33m;
+ f->init = gf33m_init;
+ f->clear = gf33m_clear;
+ f->set = gf33m_assign;
+ f->set0 = gf33m_set0;
+ f->set1 = gf33m_set1;
+ f->random = gf33m_random;
+ f->cmp = gf33m_cmp;
+ f->add = gf33m_add;
+ f->sub = gf33m_sub;
+ f->mul = gf33m_mult;
+ f->cubic = gf33m_cubic;
+ f->invert = gf33m_invert;
+ f->item_count = gf33m_item_count;
+ f->item = gf33m_item;
+ f->out_str = gf33m_out_str;
+ mpz_pow_ui(f->order, b->order, 3);
+ f->name = "GF(3^{3*m})";
+}
+