Algebraic structures are represented in the field_t data type, which mostly contains
pointers to functions written to perform operations such as
addition and multiplication on elements of that particular
group, ring or field:
struct field_s {
...
void (*init)(element_ptr);
void (*clear)(element_ptr);
...
void (*add)(element_ptr, element_ptr, element_ptr);
void (*sub)(element_ptr, element_ptr, element_ptr);
void (*mul)(element_ptr, element_ptr, element_ptr);
...
};
typedef struct field_s *field_ptr;
typedef struct field_s field_t[1];
The name algebraic_structure_t
is arguably more accurate, but far too cumbersome. It may help
if one views groups and rings as handicapped fields.
The last two lines of the above code excerpt show how GMP and PBC define data types: they are arrays of length one so that when a variable is declared, space is automatically allocated for it. Yet when used as a argument to a function, a pointer is passed, thus there is no need to explicitly allocate and deallocate memory, nor reference and dereference variables.
Each element_t contains a field
named field to such a field_t variable. The only other field is
data, which stores any data needed
for the implementation of the particular algebraic structure
the element resides in.
struct element_s {
struct field_s *field;
void *data;
};
When an element_t variable is
initialized, field is set
appropriately, and then the initialization specific to that
field is called to complete the initialization. Here, a line of
code is worth a thousand words:
void element_init(element_t e, field_ptr f) {
e->field = f;
f->init(e);
}
Thus during a call to one of the element_ functions, the field pointer is followed then the appropriate
routine is executed. For example, modular addition results when
the input element is an element of a finite field, while
polynomial addition is performed for elements of a polynomial
ring and so on.
void element_add(element_t n, element_t a, element_t b) {
n->field->add(n, a, b);
}