gf-complete/gf_poly.c

557 lines
12 KiB
C

#include "gf_complete.h"
#include "gf_method.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define GF_POLY_COEF_MASK8 0xff
#define GF_POLY_COEF_MASK16 0xffff
#define GF_POLY_COEF_MASK32 0xffffffff
#define GF_POLY_COEF_MASK64 0xffffffffffffffff
#define LLUI (long long unsigned int)
struct gf_poly_coef_s;
typedef struct gf_poly_coef_s {
uint64_t coef;
uint64_t power;
struct gf_poly_coef_s *next;
} gf_poly_coef_t;
typedef struct gf_poly_s {
gf_poly_coef_t *leading_coef;
uint64_t num_coefs;
gf_t *coef_gf;
int w;
} gf_poly_t;
static uint64_t gf_add(int w, uint64_t a, uint64_t b)
{
if (w == 8) {
return (a & GF_POLY_COEF_MASK8) ^ (b & GF_POLY_COEF_MASK8);
} else if (w == 16) {
return (a & GF_POLY_COEF_MASK16) ^ (b & GF_POLY_COEF_MASK16);
} else if (w == 32) {
return (a & GF_POLY_COEF_MASK32) ^ (b & GF_POLY_COEF_MASK32);
} else if (w == 64) {
return (a & GF_POLY_COEF_MASK64) ^ (b & GF_POLY_COEF_MASK64);
}
}
static uint64_t gf_mult(int w, gf_t* gf, uint64_t a, uint64_t b)
{
if (w <= 32) {
return gf->multiply.w32(gf, a, b);
} else if (w == 64) {
return gf->multiply.w64(gf, a, b);
}
}
static uint64_t gf_divide(int w, gf_t* gf, uint64_t a, uint64_t b)
{
if (w <= 32) {
return gf->divide.w32(gf, a, b);
} else if (w == 64) {
return gf->divide.w64(gf, a, b);
}
}
static uint64_t gf_inverse(int w, gf_t* gf, uint64_t a)
{
if (w <= 32) {
return gf->inverse.w32(gf, a);
} else if (w == 64) {
return gf->inverse.w64(gf, a);
}
}
gf_poly_t* gf_poly_init(int w, gf_t *gf)
{
gf_poly_t *gf_poly = (gf_poly_t*)malloc(sizeof(gf_poly_t));
if (gf_poly == NULL || gf == NULL) {
return NULL;
}
gf_poly->leading_coef = NULL;
gf_poly->num_coefs = 0;
gf_poly->coef_gf = gf;
gf_poly->w = w;
return gf_poly;
}
void gf_poly_print(gf_poly_t *gf_poly, char *message)
{
gf_poly_coef_t *tmp;
if (gf_poly == NULL) {
fprintf(stderr, "0 * x^0\n");
return;
}
tmp = gf_poly->leading_coef;
while (tmp != NULL) {
printf("%llu * x^%llu", LLUI tmp->coef, LLUI tmp->power);
tmp = tmp->next;
if (tmp) {
printf(" + ");
}
}
if (message != NULL) {
printf(": %s\n", message);
}
}
gf_poly_t* gf_poly_copy(gf_poly_t *poly)
{
gf_poly_t *new_poly = (gf_poly_t*)malloc(sizeof(gf_poly_t));
gf_poly_coef_t *tmp = poly->leading_coef;
if (new_poly == NULL) {
return NULL;
}
new_poly->leading_coef = NULL;
new_poly->num_coefs = 0;
new_poly->coef_gf = poly->coef_gf;
new_poly->w = poly->w;
while (tmp != NULL) {
gf_poly_add_coef(new_poly, tmp->coef, tmp->power);
tmp = tmp->next;
}
return new_poly;
}
void gf_poly_clear(gf_poly_t* a)
{
while (a->leading_coef != NULL) {
gf_poly_coef_t *tmp = a->leading_coef;
a->leading_coef = tmp->next;
free(tmp);
}
}
void gf_poly_free(gf_poly_t **a)
{
gf_poly_clear(*a);
free(*a);
*a = NULL;
}
gf_poly_coef_t* gf_poly_create_node(uint64_t coef, uint64_t power)
{
gf_poly_coef_t* node = (gf_poly_coef_t*)malloc(sizeof(gf_poly_coef_t));
if (node == NULL) {
return NULL;
}
node->coef = coef;
node->power = power;
node->next = NULL;
return node;
}
int gf_poly_remove_node(gf_poly_t *gf_poly, uint64_t power)
{
gf_poly_coef_t* iter = gf_poly->leading_coef;
if (iter->power == power) {
gf_poly->leading_coef = iter->next;
free(iter);
return 0;
}
while (iter->next != NULL) {
if (iter->next->power == power) {
gf_poly_coef_t* tmp = iter->next;
iter->next = iter->next->next;
free(tmp);
return 0;
}
iter = iter->next;
}
return -1;
}
int gf_poly_add_coef(gf_poly_t *gf_poly, uint64_t coef_val, uint64_t power)
{
gf_poly_coef_t* node;
gf_poly_coef_t* iter = gf_poly->leading_coef;
/*
* The new node has the highest power, or there are no terms
*/
if (gf_poly->leading_coef == NULL || gf_poly->leading_coef->power < power) {
node = gf_poly_create_node(coef_val, power);
node->next = gf_poly->leading_coef;
gf_poly->leading_coef = node;
return 0;
}
/*
* The new node is of the same power, add the coefs
*/
if (gf_poly->leading_coef->power == power) {
gf_poly->leading_coef->coef = gf_add(gf_poly->w, gf_poly->leading_coef->coef, coef_val);
if (gf_poly->leading_coef->coef == 0) {
gf_poly_remove_node(gf_poly, power);
}
return 0;
}
while (iter->next != NULL) {
if (iter->next->power == power) {
iter->next->coef = gf_add(gf_poly->w, iter->next->coef, coef_val);
if (iter->next->coef == 0) {
gf_poly_remove_node(gf_poly, power);
}
return 0;
}
if (iter->next->power < power) {
node = gf_poly_create_node(coef_val, power);
node->next = iter->next;
iter->next = node;
return 0;
}
iter = iter->next;
}
/*
* The power passed in is lower than any in the existing poly
*/
node = gf_poly_create_node(coef_val, power);
iter->next = node;
return 0;
}
/*
* Compute a+b and store in a
*/
int gf_poly_add(gf_poly_t* a, gf_poly_t* b)
{
gf_poly_coef_t* iter = b->leading_coef;
while (iter != NULL) {
gf_poly_add_coef(a, iter->coef, iter->power);
iter = iter->next;
}
return 0;
}
/*
* Compute a*b and store in a
*/
int gf_poly_mult(gf_poly_t* a, gf_poly_t* b)
{
gf_poly_coef_t* a_iter = a->leading_coef;
/*
* Remove one node at a time from 'a', starting with
* highest power. Multiply the removed (coef,power)
* by every entry of 'b,' adding each product into 'a.'
*/
while (a_iter != NULL) {
gf_poly_coef_t* tmp = a_iter;
gf_poly_coef_t* b_iter = b->leading_coef;
uint64_t a_power = a_iter->power;
uint64_t a_coef = a_iter->coef;
a_iter = a_iter->next;
gf_poly_remove_node(a, tmp->power);
while (b_iter != NULL) {
uint64_t new_power = b_iter->power + a_power;
uint64_t new_coef = gf_mult(a->w, a->coef_gf, b_iter->coef, a_coef);
gf_poly_add_coef(a, new_coef, new_power);
b_iter = b_iter->next;
}
}
return 0;
}
/*
* Compute a % b and store in a
*/
int gf_poly_reduce(gf_poly_t* a, gf_poly_t* b)
{
gf_poly_t* c = gf_poly_init(a->w, a->coef_gf);
gf_poly_coef_t* a_iter = a->leading_coef;
gf_poly_coef_t* b_iter = b->leading_coef;
/*
* Reduce until the degree of 'a' is less than
* the degree of 'b.' At that point 'a' will
* contain the remainder of a / b.
*/
while (a_iter && (a_iter->power >= b_iter->power)) {
/*
* Get the degree and leading coef of the current
* 'b'.
*/
uint64_t reduce_power = a_iter->power - b_iter->power;
uint64_t reduce_coef = gf_divide(a->w, a->coef_gf, a_iter->coef, b_iter->coef);
/*
* Create a poly that will get rid of leading power
* of 'b' when added: c*x^(n-m)*b(x), where c
* is the leading coef of 'a', n is the deg of 'a'
* and m is the degree of 'b'.
*/
gf_poly_add_coef(c, reduce_coef, reduce_power);
gf_poly_mult(c, b);
/*
* Add the newly created poly, which will reduce
* a(x) by at least one term (leading term).
*/
gf_poly_add(a, c);
gf_poly_clear(c);
/*
* Grab the new leading term of 'a'
*/
a_iter = a->leading_coef;
}
}
/*
* Get the GCD of a and b, return the result
*/
gf_poly_t* gf_poly_gcd(gf_poly_t* a, gf_poly_t* b)
{
gf_poly_t *r1, *r2;
gf_poly_t* tmp_swp;
if (a->leading_coef == NULL || b->leading_coef == NULL) {
return NULL;
}
if (a->leading_coef->power > b->leading_coef->power) {
r1 = a;
r2 = b;
} else {
r1 = b;
r2 = a;
}
while ( 1 ) {
if (r2->leading_coef == NULL) {
break;
}
if (r2->leading_coef->power == 0 && r2->leading_coef->coef <= 1) {
break;
}
gf_poly_reduce(r1, r2);
tmp_swp = r1;
r1 = r2;
r2 = tmp_swp;
}
return r1;
}
/*
* The Ben-Or algorithm for determining irreducibility
*/
int gf_poly_is_irred(gf_poly_t* poly)
{
gf_poly_t *gcd;
gf_poly_t *prod_of_irred;
uint64_t prod_of_irred_power = ((unsigned long long) 1) << poly->w;
int n = poly->leading_coef->power / 2;
int i;
int ret = 0;
gf_poly_t *a = gf_poly_copy(poly);
prod_of_irred = gf_poly_init(a->w, a->coef_gf);
for (i = 1; i <= n; i++) {
gf_poly_add_coef(prod_of_irred, 1, prod_of_irred_power);
gf_poly_add_coef(prod_of_irred, 1, 1);
gf_poly_reduce(prod_of_irred, a);
gcd = gf_poly_gcd(a, prod_of_irred);
/*
* It is irreducible if it is not the product of
* non-trivial factors (non-constant). Therefore,
* the GCD of the poly and prod_of_irred should be
* a constant (0 or 0-degree polynomial).
*/
if (gcd == NULL) {
ret = -1;
break;
} else if (gcd->leading_coef->power != 0) {
ret = -1;
break;
} else if (gcd->leading_coef->power == 0) {
ret = 0;
break;
} else {
ret = -1;
break;
}
// Need if to avoid a overflow error
if ((i + 1) <= n) {
prod_of_irred_power *= prod_of_irred_power;
}
gf_poly_clear(prod_of_irred);
}
gf_poly_free(&a);
return ret;
}
int is_suitible_s(int w, gf_t *gf, uint64_t s)
{
uint64_t num_elems = ((unsigned long long) 1) << w;
uint64_t i = 2;
uint64_t i_inv;
for (; i < num_elems; i++) {
i_inv = gf_inverse(w, gf, i);
if ((i ^ i_inv) == s) {
fprintf(stderr, "Bailed on %llu ^ %llu = %llu\n", LLUI i, LLUI i_inv, LLUI s);
return -1;
}
if (i % 1000000000 == 0) fprintf(stderr, "Processed %llu\n", LLUI i);
}
return 0;
}
static void
usage(char *cmd)
{
fprintf(stderr, "%s w <GF args> S <s value>\n", cmd);
fprintf(stderr, "\t will build a trinomial x^2+S*x+1\n");
fprintf(stderr, "OR\n");
fprintf(stderr, "%s w <GF args> G coef1,power1 <coef2,power2> ... <coefn,powern>\n", cmd);
fprintf(stderr, "\t will build a polynomial coef1^(power1) + ... + coefn^(powern)\n");
fprintf(stderr, "Example: ./gf_poly 8 - - - G 1,2 2,1 1,0\n");
fprintf(stderr, "\t will build a polynomial x^2+2*x+1 with coefs from GF(2^8)\n");
}
/*
* Find irred poly of form x^2+sx+1
* a_n*x^n + a_(n-1)*x^(n-1) + ...
*
* Terms are specified as: a_i,i a_j,j, ... where
* i is the degree of the term and a_i is the coef
*
*/
int main(int argc, char **argv)
{
gf_t gf;
int ret;
int w;
int i;
uint64_t irred_coef_s;
gf_poly_t *irred_poly;
char *term;
bzero(&gf, sizeof(gf_t));
if (argc < 4) {
usage(argv[0]);
return -1;
}
w = atoi(argv[1]);
ret = create_gf_from_argv(&gf, w, argc, argv, 3);
if (ret <= 0) {
fprintf(stderr, "Could not create a GF\n");
return -1;
}
irred_poly = gf_poly_init(w, &gf);
i = ret + 1;
if (strlen(argv[i]) > 1) {
usage(argv[0]);
exit(1);
}
if (argv[i][0] == 'S') {
i++;
irred_coef_s = (uint64_t)strtoull(argv[i], NULL, 10);
/*
* If this is a trinomial of the form x^2+s*x+1, then
* we can do a quick pre-check to see if this may be
* an irreducible polynomial.
*/
if (is_suitible_s(w, &gf, irred_coef_s) < 0) {
fprintf(stderr, "%llu is not a suitable coeffient!\n", LLUI irred_coef_s);
return -1;
} else {
fprintf(stderr, "%llu IS A suitable coeffient!\n", LLUI irred_coef_s);
}
gf_poly_add_coef(irred_poly, 1, 2);
gf_poly_add_coef(irred_poly, irred_coef_s, 1);
gf_poly_add_coef(irred_poly, 1, 0);
} else if (argv[i][0] == 'G') {
term = argv[++i];
while (term != NULL) {
uint64_t coef = strtoull(strtok(term, ","), NULL, 10);
uint64_t power = strtoull(strtok(NULL, ","), NULL, 10);
gf_poly_add_coef(irred_poly, coef, power);
if (i < argc) {
term = argv[++i];
} else {
break;
}
}
} else {
usage(argv[0]);
exit(1);
}
gf_poly_print(irred_poly, " specified via the command line\n");
ret = gf_poly_is_irred(irred_poly);
if (ret < 0) {
gf_poly_print(irred_poly, " IS NOT irreducible\n");
} else {
gf_poly_print(irred_poly, " IS irreducible\n");
}
return 0;
}