FAAST
0.2.1
|
FAAST offers an intuitive object-oriented interface to handle finite fields and their extensions. The three main types you will use are Field, FieldElement and FieldPolynomial, which are described in the section Finite Field Arithmetics.
FAAST adds some genericity on top of the traditional NTL
architecture, one of its most prominent features is the mechanism of Infrastructures which uses C++ templates to let you choose statically (at compile-time) which set of NTL
algorithms use.
All in all, the goal of FAAST is to represent Lattices of fields and give algorithms to work into them. For the moment FAAST is limited to some special types of lattices arising from Artin-Schreier towers. See Lattices of fields for more details.
This module contains three types, namely FAAST::Field, FAAST::FieldElement and FAAST::FieldPolynomial, which represent, respectively, finite fields, elements of finite fields and polynomials with coefficient over finite fields.With some notable exception (see FieldElement() and FieldPolynomial()), each FAAST::FieldElement and FAAST::FieldPolynomial belongs to a FAAST::Field object called its parent field. Elements (and polynomials) having the same parent field can be combined (added, multiplied, etc.) freely, while limitations apply for combining elements belonging to two different fields; see FAAST::FieldElement and FAAST::FieldPolynomial.Elements (and polynomials) can be moved from one field to another when a morphism is known; see Lattices of fields.
See the module Finite Field Arithmetics for the list of methods and functions.
NTL
provides three different ways of representing modular integers:
zz_p
is a class representing modular integers with word-sized modulus,ZZ_p
is a class representing modular integers with arbitrary sized modulus,GF2
is a class representing integers modulo 2.ZZ_p_X
, GF2_E
, etc.) representing polynomials with modular coefficients, modular polynomials with modular coefficients, etc. The actual algorithms implementing arithmetics for such types vary and affect remarkably the performances of FAAST. See the NTL
manual for more details.Infrastructures are collections (struct
's) of types providing genericity over NTL
types. They provide an unique set of names to abstract from the implementation details of the three NTL families ZZ_p*
, zz_p*
and GF2*
. You must provide an Infrastructure as template parameter to the types and most of the functions of FAAST. This parameter tells FAAST which of the three NTL
families it should use to perform modular arithmetics.Here's an example of how to use the infrastructure FAAST::zz_p_Algebra. First some typedef
's to save typing: long
), then you should opt for FAAST::ZZ_p_Algebra; notice however that FAAST does not let you build Artin-Schreier extensions in characteristics greater than the greatest long
, so you will probably miss its most exciting features.Finally, if you only work in characteristic 2 and care about performance, you should consider compiling NTL
with the gf2x
library and using FAAST::GF2_Algebra as Infrastructure. If you don't use the gf2x
library, then FAAST::GF2_Algebra will only be interesting for moderate field cardinalities, but it will give the pace up to FAAST::zz_p_Algebra for huge fields.
See the module Infrastructures for more details.
The most exciting feature of FAAST is its ability to deal with field extension and keep track of morphisms. This ability is limited to Artin-Schreier towers as described in [DFS '09] and [Couveignes '00] and gives rise to lattices of a special form; we call them stem lattices.
For the rest of this section p will be the characteristic of our fields. Remind that an Artin-Schreier extension is a field extension of degree p generated by an irreducible polynomial of the form
The simplest lattice is the lattice formed by a single field. Finite fields of arbitrary cardinality can be built through the static instantiators Field::createField(). Suppose you build the field with p elements Fp then the resulting lattice looks like
When you build non-prime fields, the prime subfield is automatically constructed. Suppose you build a field named K0, then the resulting lattice looks like
Now, to add fields to a lattice there is two methods, namely Field::ArtinSchreierExtension() const and Field::ArtinSchreierExtension(const FieldElement&) const. Both create an Artin-Schreier extension, but they differ in the way the extension is created. The former method chooses a default irreducible Artin-Schreier polynomial such that the generated Artin-Schreier extension is primitive in the sense of [DFS '09, Section 3]. The resulting lattice is then
The latter method constructs the splitting fields of the polynomial where
e
is the FieldElement given as a parameter. Assuming that the polynomial is irreducible, it constructs a primitive extension using Field::ArtinSchreierExtension() const and then uses the isomorphism algorithm of [Couveignes '00] to establish an isomorphism between the primitive field and the splitting field. So for example a call to
would generate a lattice
where K1 is the primitive extension and U1 is the field generated by .
All the objects of type Field are persistent: once they are created they are never destroyed. So, even if you don't hold a reference to K1, another call to
will not create a new primitive field, it will use K1 instead. The resulting lattice would be
By repeatedly mixing calls to the two methods, one ends up with lattices of the form
Now the reason why we call this a stem lattice should be clear. In the middle there's the primitive tower (K0, K1, ..., Kn), in the sense of [DFS '09, Section 3], which we call the stem. The stem is organized in levels, each level being an extension of degree p over the lower one (with the only possible exception of the extension K0/Fp). At each level, a set of fields all isomorphic to that level forms the spurs.
The methods Field::subField(), Field::overField(), Field::baseField(), Field::primeField() and Field::stemField() let you navigate field lattices.
Coercion operators (see FieldElement and FieldPolynomial) let you move elements from one field to the other, provided that the element belongs to both fields. So for example you can move elements from K0 or M1 to L1, but it will not always be possible to move an element from K2 to K1. For example, coercion from K0 to K1 is as easy as
In case a coercion is not possible, an IllegalCoercionException is issued.
Another way of moving around in field lattices is to use the vector space morphisms between the levels of the lattice. The functions pushDown() and liftUp() let you express the morphism between two adjacent levels of the stem, while Field::toBivariate() and Field::toUnivariate() let you express the morphisms between to arbitrary spurs in two adjacent levels. Notice that there is no need to know what the order of the calls to Field::ArtinSchreierExtension() was in order to apply these methods: any field of a level can be considered as an Artin-Schreier extension over any other field in the lower level.
See the Exceptions module for a list of all the exceptions thrown by methods and functions of this library.