The goal of decoding
is to provide decoding algorithms for error
correcting codes. We tried to write algorithms in a generic way so
that a lot of alphabets can be used (e.g. finite fields, finite local
rings). It is up to the user of the library to give some C code
that provides needed functions to manipulate the elements of the
chosen alphabet. The word "alphabet" is badly chosen because decoding
requires an alphabet to be a (not nessarily commutative) ring with
identity. We will use the words "alphabet" and "ring" interchangeably.
The current page describes how to provide your own alphabet to
decoding
. A few alphabets, such as prime fields, are provided by
decoding
but are far from being optimized. For exemple, if you want
optimized finite fields in caracteristic 2, you can use MPFQ.
For a list of alphabets proposed by decoding
see
decoding-alphabets
(3).
It is strongly recommanded to have a separated file for each alphabet
you want to use. Before doing anything, you must include
decoding/ring_reset.h
.
#include <decoding/ring_reset.h>
This file undefs some macros that could have been defined by
another alphabet. For details see decoding-ring-defines
(3).
Then you must give a name to the ring you are implementing with
the R
macro. In fact the user can provide a name via the
RING_NAME
function.
#ifdef RING_NAME
#define R RING_NAME
#else
#define R GF5
#endif
This name will correspond to the C type to declare elements of GF(5).
All functions that manipulate elements of GF(5), such as polynomial
multiplication, will see their names prefixed with GF5
.
For test purpose you can define how to create the ring in MAGMA
with the R_magma_decl
string. It is not mandatory.
const char *R_magma_decl = __S(R) " := GF(5)";
Then we tell decoding
that the ring is of type Z/nZ
with
n
that can hold within a machine word with RING_MODN_WORD
.
We also say that the characteristic holds within a machine integer
with CHARACTERISTIC_WORD
and give its type.
and its value in the variable R_characteristic
.
#define CHARACTERISTIC_WORD
typedef int R_characteristic_type;
const int R_characteristic = 5;
Now you declare the C type to represent elements of your ring.
For example, with GF(5), one only needs an int
to represents
its elements. We typedef three different types: a pointer to
the int
type, an array of one int
and the int
type itself.
The typedefs must be respectively R_ptr
, R
and R_content
.
typedef int *R_ptr;
typedef int R[1];
typedef int R_content;
Note that R
and R_ptr
designate alsmost the same type and
the second typedef is syntactic sugar.
The following functions must be provided. They follow the semantic
of the GMP mpz_
functions. This document is generated from the
comments of the include/decoding/rings/GF5.c
, you can look at it
to have a working example of a ring whose elements hold in a C int
.
void R_ring_init(void);
This function is mandatory before doing anything with the ring. It performs some variables initialization.
void R_ring_clear(void);
When the ring is no more needed, you may call this function
to free the memory occupied by the ring, usually what was
allocated by R_ring_init
.
void R_clear(R a);
Frees the memory occupied by a
.
If you use a
after a call to R_clear
the behavior is undefined.
void R_clears(R a);
Free the memory occupied by a NULL-terminated list of R
variables.
If you use one of the variable after a call to R_clears
the behavior
is undefined.
void R_init(R a);
Initialize a
and set its value to 0
. This is the
place to allocate some memory needed for a
.
void R_inits(R a,...);
Initialize a NULL-terminated list of R
variables, and set their
values to 0
. This is the place to allocate some memory needed for
the variables.
void R_copy(R r,R a);
Copy the value of a
into r
. This must be a deep copy.
void R_swap(R r,R a);
Swap the values of r
and a
.
void R_init_copy(R r,R a);
Initialize r
and do a deep copy of a
into r
.
void R_print(FILE *out,R a);
Print (prettyprint) a
into the output stream out
.
int R_cmp(R a,R b);
Compare a
and b
and returns 0
if and only if the value of
a
is mathematically equal to the value of b
.
int R_iszero(R a);
Compare a
and 0
. Returns 0
if and only if a
is nonzero.
void R_zero(R r);
Set r
to 0
.
void R_random(R r);
Set r
to a random element of the ring.
void R_add(R r,R a,R b);
Set r
to a + b
.
Note that r
, a
and b
can point to the same memory.
void R_sub(R r,R a,R b);
Set r
to a - b
.
Note that r
, a
and b
can point to the same memory.
void R_sub_sub(R r,R a,R b);
Set r
to r - a - b
.
Note that r
, a
and b
can point to the same memory.
void R_add_mul(R r,R a,R b);
Set r
to r + (a * b)
.
Note that r
, a
and b
can point to the same memory.
void R_sub_mul(R r,R a,R b);
Set r
to r - (a * b)
.
Note that r
, a
and b
can point to the same memory.
void R_neg(R r,R a);
Set r
to -a
.
Note that r
and a
can point to the same memory.
void R_mul(R r,R a,R b);
Set r
to a * b
.
Note that r
, a
and b
can point to the same memory.
void R_square(R r,R a);
Set r
to a * a
.
Note that r
and a
can point to the same memory.
int R_inv(R r,R a);
Set r
to a^-1
. Return 0
if and only if a
has an inverse.
If a
has no inverse then the value of r
is undefined.
Note that r
and a
can point to the same memory.
int R_div(R r,R a,R b);
Set r
to a / b)
. Return 0
if and only if b
has an inverse.
If b
has no inverse then the value of r
is undefined.
Note that r
, a
and b
can point to the same memory.
void R_castint(R r,int a);
Set r
to the value of 1 + 1 + ... + 1
(a
times) where 1
is the unity element of the ring.
void R_cast_integer(R r,R_characteristic_type a);
Set r
to the value of 1 + 1 + ... + 1
(a
times) where 1
is the unity element of the ring. It can happen that the characteristic
of the ring does not hold within a machine integer. Therefore
the R_castint
function cannot be used, hence the existence of
R_cast_integer
.
void R_next(R r,R a);
Set r
to the element of the ring following a
.
Suppose that your ring has cardinality n
.
You must order the elements of your ring so that they can all
be reached with exactly n
calls to R_next
.
This function is useful to construct the support of Reed-Solomon
codes.
You can add auxiliary variables and functions. Let say you want
to add myfunc
. It is recommended to prefix the function name
with R_
. This is how to proceed:
#define R_myfunc __C(R,_myfunc)
void R_myfunc(int a,R b);
[ ... ]
void R_myfunc(int a,R b) {
...
}
[ ... ]
#undef R_myfunc
The [ ... ]
usually contains the implementation of the ring,
at least all the functions descrobed above.
You can use the new function with the name R_myfunc
before the
#undef R_myfunc
. The __C
macros concatenates R
(GF5
for
example) and _myfunc
. Thus name of the new function in other source
files or after the #undef R_myfunc
is GF5_myfunc
. The same holds
for variables:
#define R_myvar __C(R,_myvar)
some_type R_myvar;
[ ... ]
#undef R_myvar
Finally you must include decoding/lowlevels_decl.h
at the end of
the source file.
#include <decoding/lowlevels_decl.h>
Written by Guillaume Quintin (coincoin169g@gmail.com).
decoding-alphabets
(3)
decoding-ring-defines
(3)
MPFQ(http://mpfq.gforge.inria.fr/),
GMP(http://gmplib.org/),
MAGMA(http://magma.maths.usyd.edu.au/magma/)