Intro to Prime Fields¶
A Galois field is a finite field named in honor of Évariste Galois, one of the fathers of group theory. A field is a set that is closed under addition, subtraction, multiplication, and division. To be closed under an operation means that performing the operation on any two elements of the set will result in another element from the set. A finite field is a field with a finite set of elements.
Galois proved that finite fields exist only when their order (or size of the set) is a prime power
Prime field¶
In this tutorial, we will consider the prime field galois
library, the FieldArray
subclass GF7
is created using the class factory GF()
.
In [1]: GF7 = galois.GF(7)
In [2]: GF7
Out[2]: <class 'numpy.ndarray over GF(7)'>
In [3]: print(GF7)
Galois Field:
name: GF(7)
characteristic: 7
degree: 1
order: 7
irreducible_poly: x + 4
is_primitive_poly: True
primitive_element: 3
In [4]: GF7 = galois.GF(7, display="power")
In [5]: GF7
Out[5]: <class 'numpy.ndarray over GF(7)'>
In [6]: print(GF7)
Galois Field:
name: GF(7)
characteristic: 7
degree: 1
order: 7
irreducible_poly: x + 4
is_primitive_poly: True
primitive_element: 3
Note
In this tutorial, we use the display="int"
default to display the elements. However, sometimes it is useful to view elements
in their power representation
See Element Representation for more details.
Elements¶
The elements of the finite field
The elements of the finite field are retrieved in a 1-D array using the Elements()
classmethod.
In [7]: GF7.Elements()
Out[7]: GF([0, 1, 2, 3, 4, 5, 6], order=7)
In [8]: GF7.Elements()
Out[8]: GF([ 0, 1, α^2, α, α^4, α^5, α^3], order=7)
Arithmetic¶
Addition, subtraction, and multiplication in
In this tutorial, consider two field elements galois
to perform explicit modular
integer arithmetic and then prime field arithmetic.
Here are
In [9]: a_int = 3
In [10]: b_int = 5
In [11]: p = GF7.characteristic; p
Out[11]: 7
Here are
In [12]: a = GF7(3); a
Out[12]: GF(3, order=7)
In [13]: b = GF7(5); b
Out[13]: GF(5, order=7)
In [14]: a = GF7(3); a
Out[14]: GF(α, order=7)
In [15]: b = GF7(5); b
Out[15]: GF(α^5, order=7)
Addition¶
We can see that
In [16]: (a_int + b_int) % p
Out[16]: 1
In [17]: a + b
Out[17]: GF(1, order=7)
In [18]: (a_int + b_int) % p
Out[18]: 1
In [19]: a + b
Out[19]: GF(1, order=7)
The galois
library includes the ability to display the arithmetic tables for any finite field. The table is only readable
for small fields, but nonetheless the capability is provided. Select a few computations at random and convince yourself the
answers are correct.
In [20]: print(GF7.arithmetic_table("+"))
+-------+---+---+---+---+---+---+---+
| x + y | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-------+---+---+---+---+---+---+---+
| 0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-------+---+---+---+---+---+---+---+
| 1 | 1 | 2 | 3 | 4 | 5 | 6 | 0 |
+-------+---+---+---+---+---+---+---+
| 2 | 2 | 3 | 4 | 5 | 6 | 0 | 1 |
+-------+---+---+---+---+---+---+---+
| 3 | 3 | 4 | 5 | 6 | 0 | 1 | 2 |
+-------+---+---+---+---+---+---+---+
| 4 | 4 | 5 | 6 | 0 | 1 | 2 | 3 |
+-------+---+---+---+---+---+---+---+
| 5 | 5 | 6 | 0 | 1 | 2 | 3 | 4 |
+-------+---+---+---+---+---+---+---+
| 6 | 6 | 0 | 1 | 2 | 3 | 4 | 5 |
+-------+---+---+---+---+---+---+---+
In [21]: print(GF7.arithmetic_table("+"))
+-------+-----+-----+-----+-----+-----+-----+-----+
| x + y | 0 | 1 | α | α^2 | α^3 | α^4 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| 0 | 0 | 1 | α | α^2 | α^3 | α^4 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| 1 | 1 | α^2 | α^4 | α | 0 | α^5 | α^3 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α | α | α^4 | α^3 | α^5 | α^2 | 0 | 1 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^2 | α^2 | α | α^5 | α^4 | 1 | α^3 | 0 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^3 | α^3 | 0 | α^2 | 1 | α^5 | α | α^4 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^4 | α^4 | α^5 | 0 | α^3 | α | 1 | α^2 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^5 | α^5 | α^3 | 1 | 0 | α^4 | α^2 | α |
+-------+-----+-----+-----+-----+-----+-----+-----+
Subtraction¶
As with addition, we can see that
In [22]: (a_int - b_int) % p
Out[22]: 5
In [23]: a - b
Out[23]: GF(5, order=7)
In [24]: (a_int - b_int) % p
Out[24]: 5
In [25]: a - b
Out[25]: GF(α^5, order=7)
Here is the subtraction table for completeness.
In [26]: print(GF7.arithmetic_table("-"))
+-------+---+---+---+---+---+---+---+
| x - y | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-------+---+---+---+---+---+---+---+
| 0 | 0 | 6 | 5 | 4 | 3 | 2 | 1 |
+-------+---+---+---+---+---+---+---+
| 1 | 1 | 0 | 6 | 5 | 4 | 3 | 2 |
+-------+---+---+---+---+---+---+---+
| 2 | 2 | 1 | 0 | 6 | 5 | 4 | 3 |
+-------+---+---+---+---+---+---+---+
| 3 | 3 | 2 | 1 | 0 | 6 | 5 | 4 |
+-------+---+---+---+---+---+---+---+
| 4 | 4 | 3 | 2 | 1 | 0 | 6 | 5 |
+-------+---+---+---+---+---+---+---+
| 5 | 5 | 4 | 3 | 2 | 1 | 0 | 6 |
+-------+---+---+---+---+---+---+---+
| 6 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------+---+---+---+---+---+---+---+
In [27]: print(GF7.arithmetic_table("-"))
+-------+-----+-----+-----+-----+-----+-----+-----+
| x - y | 0 | 1 | α | α^2 | α^3 | α^4 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| 0 | 0 | α^3 | α^4 | α^5 | 1 | α | α^2 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| 1 | 1 | 0 | α^5 | α^3 | α^2 | α^4 | α |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α | α | α^2 | 0 | 1 | α^4 | α^3 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^2 | α^2 | 1 | α^3 | 0 | α | α^5 | α^4 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^3 | α^3 | α^5 | α | α^4 | 0 | α^2 | 1 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^4 | α^4 | α | 1 | α^2 | α^5 | 0 | α^3 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^5 | α^5 | α^4 | α^2 | α | α^3 | 1 | 0 |
+-------+-----+-----+-----+-----+-----+-----+-----+
Multiplication¶
Similarly, we can see that
In [28]: (a_int * b_int) % p
Out[28]: 1
In [29]: a * b
Out[29]: GF(1, order=7)
In [30]: (a_int * b_int) % p
Out[30]: 1
In [31]: a * b
Out[31]: GF(1, order=7)
Here is the multiplication table for completeness.
In [32]: print(GF7.arithmetic_table("*"))
+-------+---+---+---+---+---+---+---+
| x * y | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-------+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+-------+---+---+---+---+---+---+---+
| 1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
+-------+---+---+---+---+---+---+---+
| 2 | 0 | 2 | 4 | 6 | 1 | 3 | 5 |
+-------+---+---+---+---+---+---+---+
| 3 | 0 | 3 | 6 | 2 | 5 | 1 | 4 |
+-------+---+---+---+---+---+---+---+
| 4 | 0 | 4 | 1 | 5 | 2 | 6 | 3 |
+-------+---+---+---+---+---+---+---+
| 5 | 0 | 5 | 3 | 1 | 6 | 4 | 2 |
+-------+---+---+---+---+---+---+---+
| 6 | 0 | 6 | 5 | 4 | 3 | 2 | 1 |
+-------+---+---+---+---+---+---+---+
In [33]: print(GF7.arithmetic_table("*"))
+-------+-----+-----+-----+-----+-----+-----+-----+
| x * y | 0 | 1 | α | α^2 | α^3 | α^4 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| 1 | 0 | 1 | α | α^2 | α^3 | α^4 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α | 0 | α | α^2 | α^3 | α^4 | α^5 | 1 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^2 | 0 | α^2 | α^3 | α^4 | α^5 | 1 | α |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^3 | 0 | α^3 | α^4 | α^5 | 1 | α | α^2 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^4 | 0 | α^4 | α^5 | 1 | α | α^2 | α^3 |
+-------+-----+-----+-----+-----+-----+-----+-----+
| α^5 | 0 | α^5 | 1 | α | α^2 | α^3 | α^4 |
+-------+-----+-----+-----+-----+-----+-----+-----+
Multiplicative inverse¶
Division in
Euclid discovered an efficient algorithm to solve the Bézout Identity,
which is used to find the multiplicative inverse. It is now called the Extended Euclidean Algorithm.
Given two integers egcd()
.
If
# Returns (gcd, s, t)
In [34]: galois.egcd(b_int, p)
Out[34]: (1, 3, -2)
The galois
library uses the Extended Euclidean Algorithm to compute multiplicative inverses (and division) in prime fields.
The inverse of
In [35]: b ** -1
Out[35]: GF(3, order=7)
In [36]: np.reciprocal(b)
Out[36]: GF(3, order=7)
In [37]: b ** -1
Out[37]: GF(α, order=7)
In [38]: np.reciprocal(b)
Out[38]: GF(α, order=7)
Division¶
Now let’s return to division in finite fields. As mentioned earlier,
To compute
In [39]: _, b_inv_int, _ = galois.egcd(b_int, p)
In [40]: (a_int * b_inv_int) % p
Out[40]: 2
In [41]: a * b**-1
Out[41]: GF(2, order=7)
In [42]: a / b
Out[42]: GF(2, order=7)
In [43]: _, b_inv_int, _ = galois.egcd(b_int, p)
In [44]: (a_int * b_inv_int) % p
Out[44]: 2
In [45]: a * b**-1
Out[45]: GF(α^2, order=7)
In [46]: a / b
Out[46]: GF(α^2, order=7)
Here is the division table for completeness. Notice that division is not defined for
In [47]: print(GF7.arithmetic_table("/"))
+-------+---+---+---+---+---+---+
| x / y | 1 | 2 | 3 | 4 | 5 | 6 |
+-------+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+-------+---+---+---+---+---+---+
| 1 | 1 | 4 | 5 | 2 | 3 | 6 |
+-------+---+---+---+---+---+---+
| 2 | 2 | 1 | 3 | 4 | 6 | 5 |
+-------+---+---+---+---+---+---+
| 3 | 3 | 5 | 1 | 6 | 2 | 4 |
+-------+---+---+---+---+---+---+
| 4 | 4 | 2 | 6 | 1 | 5 | 3 |
+-------+---+---+---+---+---+---+
| 5 | 5 | 6 | 4 | 3 | 1 | 2 |
+-------+---+---+---+---+---+---+
| 6 | 6 | 3 | 2 | 5 | 4 | 1 |
+-------+---+---+---+---+---+---+
In [48]: print(GF7.arithmetic_table("/"))
+-------+-----+-----+-----+-----+-----+-----+
| x / y | 1 | α | α^2 | α^3 | α^4 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+-------+-----+-----+-----+-----+-----+-----+
| 1 | 1 | α^5 | α^4 | α^3 | α^2 | α |
+-------+-----+-----+-----+-----+-----+-----+
| α | α | 1 | α^5 | α^4 | α^3 | α^2 |
+-------+-----+-----+-----+-----+-----+-----+
| α^2 | α^2 | α | 1 | α^5 | α^4 | α^3 |
+-------+-----+-----+-----+-----+-----+-----+
| α^3 | α^3 | α^2 | α | 1 | α^5 | α^4 |
+-------+-----+-----+-----+-----+-----+-----+
| α^4 | α^4 | α^3 | α^2 | α | 1 | α^5 |
+-------+-----+-----+-----+-----+-----+-----+
| α^5 | α^5 | α^4 | α^3 | α^2 | α | 1 |
+-------+-----+-----+-----+-----+-----+-----+
Primitive elements¶
A property of finite fields is that some elements produce the non-zero elements of the field by their powers.
A primitive element
In prime fields
Primitive roots mod ¶
An integer
Finding primitive roots mod primitive_root()
and primitive_roots()
.
In [49]: galois.primitive_root(7)
Out[49]: 3
A primitive element¶
In galois
, a primitive element of a finite field is provided by the primitive_element
class property.
In [50]: print(GF7)
Galois Field:
name: GF(7)
characteristic: 7
degree: 1
order: 7
irreducible_poly: x + 4
is_primitive_poly: True
primitive_element: 3
In [51]: g = GF7.primitive_element; g
Out[51]: GF(3, order=7)
In [52]: print(GF7)
Galois Field:
name: GF(7)
characteristic: 7
degree: 1
order: 7
irreducible_poly: x + 4
is_primitive_poly: True
primitive_element: 3
In [53]: g = GF7.primitive_element; g
Out[53]: GF(α, order=7)
The galois
package allows you to easily display all powers of an element and their equivalent polynomial, vector, and integer
representations using repr_table()
. Let’s ignore the polynomial and vector representations for now.
They will become useful for extension fields.
Here is the representation table using the default generator
In [54]: g.multiplicative_order()
Out[54]: 6
In [55]: print(GF7.repr_table())
+-------+------------+--------+---------+
| Power | Polynomial | Vector | Integer |
+-------+------------+--------+---------+
| 0 | 0 | [0] | 0 |
+-------+------------+--------+---------+
| 3^0 | 1 | [1] | 1 |
+-------+------------+--------+---------+
| 3^1 | 3 | [3] | 3 |
+-------+------------+--------+---------+
| 3^2 | 2 | [2] | 2 |
+-------+------------+--------+---------+
| 3^3 | 6 | [6] | 6 |
+-------+------------+--------+---------+
| 3^4 | 4 | [4] | 4 |
+-------+------------+--------+---------+
| 3^5 | 5 | [5] | 5 |
+-------+------------+--------+---------+
Other primitive elements¶
There are multiple primitive elements of any finite field. All primitive elements are provided in the
primitive_elements
class property.
In [56]: list(galois.primitive_roots(7))
Out[56]: [3, 5]
In [57]: GF7.primitive_elements
Out[57]: GF([3, 5], order=7)
In [58]: g = GF7(5); g
Out[58]: GF(5, order=7)
In [59]: list(galois.primitive_roots(7))
Out[59]: [3, 5]
In [60]: GF7.primitive_elements
Out[60]: GF([ α, α^5], order=7)
In [61]: g = GF7(5); g
Out[61]: GF(α^5, order=7)
This means that
Here is the representation table using a different generator
In [62]: g.multiplicative_order()
Out[62]: 6
In [63]: print(GF7.repr_table(g))
+-------+------------+--------+---------+
| Power | Polynomial | Vector | Integer |
+-------+------------+--------+---------+
| 0 | 0 | [0] | 0 |
+-------+------------+--------+---------+
| 5^0 | 1 | [1] | 1 |
+-------+------------+--------+---------+
| 5^1 | 5 | [5] | 5 |
+-------+------------+--------+---------+
| 5^2 | 4 | [4] | 4 |
+-------+------------+--------+---------+
| 5^3 | 6 | [6] | 6 |
+-------+------------+--------+---------+
| 5^4 | 2 | [2] | 2 |
+-------+------------+--------+---------+
| 5^5 | 3 | [3] | 3 |
+-------+------------+--------+---------+
Non-primitive elements¶
All other elements of the field cannot generate the multiplicative group. They have multiplicative
orders less than
For example, the element
In [64]: e = GF7(2); e
Out[64]: GF(2, order=7)
In [65]: e = GF7(2); e
Out[65]: GF(α^2, order=7)
It has
In [66]: e.multiplicative_order()
Out[66]: 3
In [67]: print(GF7.repr_table(e))
+-------+------------+--------+---------+
| Power | Polynomial | Vector | Integer |
+-------+------------+--------+---------+
| 0 | 0 | [0] | 0 |
+-------+------------+--------+---------+
| 2^0 | 1 | [1] | 1 |
+-------+------------+--------+---------+
| 2^1 | 2 | [2] | 2 |
+-------+------------+--------+---------+
| 2^2 | 4 | [4] | 4 |
+-------+------------+--------+---------+
| 2^3 | 1 | [1] | 1 |
+-------+------------+--------+---------+
| 2^4 | 2 | [2] | 2 |
+-------+------------+--------+---------+
| 2^5 | 4 | [4] | 4 |
+-------+------------+--------+---------+