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.
Évariste Galois
Ask Jacobi or Gauss publicly to give their opinion, not as to the truth, but as to the importance of these theorems. Later there will be, I hope, some people who will find it to their advantage to decipher all this mess.
- May 29, 1832 (two days before his death)
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]: print(GF7.properties)
Galois Field:
name: GF(7)
characteristic: 7
degree: 1
order: 7
irreducible_poly: x + 4
is_primitive_poly: True
primitive_element: 3
In [3]: GF7 = galois.GF(7, repr="power")
In [4]: print(GF7.properties)
Galois Field:
name: GF(7)
characteristic: 7
degree: 1
order: 7
irreducible_poly: x + 4
is_primitive_poly: True
primitive_element: 3
Info
In this tutorial, we suggest using the integer representation 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 [5]: GF7.elements
Out[5]: GF([0, 1, 2, 3, 4, 5, 6], order=7)
In [6]: GF7.elements
Out[6]: 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 [7]: a_int = 3
In [8]: b_int = 5
In [9]: p = GF7.characteristic; p
Out[9]: 7
Here are
In [10]: a = GF7(3); a
Out[10]: GF(3, order=7)
In [11]: b = GF7(5); b
Out[11]: GF(5, order=7)
In [12]: a = GF7(3); a
Out[12]: GF(α, order=7)
In [13]: b = GF7(5); b
Out[13]: GF(α^5, order=7)
Addition¶
We can see that
In [14]: (a_int + b_int) % p
Out[14]: 1
In [15]: a + b
Out[15]: GF(1, order=7)
In [16]: (a_int + b_int) % p
Out[16]: 1
In [17]: a + b
Out[17]: 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 [18]: 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 [19]: 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 [20]: (a_int - b_int) % p
Out[20]: 5
In [21]: a - b
Out[21]: GF(5, order=7)
In [22]: (a_int - b_int) % p
Out[22]: 5
In [23]: a - b
Out[23]: GF(α^5, order=7)
Here is the subtraction table for completeness.
In [24]: 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 [25]: 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 [26]: (a_int * b_int) % p
Out[26]: 1
In [27]: a * b
Out[27]: GF(1, order=7)
In [28]: (a_int * b_int) % p
Out[28]: 1
In [29]: a * b
Out[29]: GF(1, order=7)
Here is the multiplication table for completeness.
In [30]: 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 [31]: 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 [32]: galois.egcd(b_int, p)
Out[32]: (1, 3, -2)
The galois
library uses the Extended Euclidean Algorithm to compute multiplicative inverses (and division) in prime fields.
The inverse of 5 in
In [33]: b ** -1
Out[33]: GF(3, order=7)
In [34]: np.reciprocal(b)
Out[34]: GF(3, order=7)
In [35]: b ** -1
Out[35]: GF(α, order=7)
In [36]: np.reciprocal(b)
Out[36]: GF(α, order=7)
Division¶
Now let’s return to division in finite fields. As mentioned earlier,
To compute
In [37]: _, b_inv_int, _ = galois.egcd(b_int, p)
In [38]: (a_int * b_inv_int) % p
Out[38]: 2
In [39]: a * b**-1
Out[39]: GF(2, order=7)
In [40]: a / b
Out[40]: GF(2, order=7)
In [41]: _, b_inv_int, _ = galois.egcd(b_int, p)
In [42]: (a_int * b_inv_int) % p
Out[42]: 2
In [43]: a * b**-1
Out[43]: GF(α^2, order=7)
In [44]: a / b
Out[44]: GF(α^2, order=7)
Here is the division table for completeness. Notice that division is not defined for
In [45]: 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 [46]: 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 [47]: galois.primitive_root(7)
Out[47]: 3
A primitive element¶
In galois
, a primitive element of a finite field is provided by the primitive_element
class property.
In [48]: print(GF7.properties)
Galois Field:
name: GF(7)
characteristic: 7
degree: 1
order: 7
irreducible_poly: x + 4
is_primitive_poly: True
primitive_element: 3
In [49]: g = GF7.primitive_element; g
Out[49]: GF(3, order=7)
In [50]: print(GF7.properties)
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(α, 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 [52]: g.multiplicative_order()
Out[52]: 6
In [53]: 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 [54]: list(galois.primitive_roots(7))
Out[54]: [3, 5]
In [55]: GF7.primitive_elements
Out[55]: GF([3, 5], order=7)
In [56]: g = GF7(5); g
Out[56]: GF(5, order=7)
In [57]: list(galois.primitive_roots(7))
Out[57]: [3, 5]
In [58]: GF7.primitive_elements
Out[58]: GF([ α, α^5], order=7)
In [59]: g = GF7(5); g
Out[59]: GF(α^5, order=7)
This means that 3 and 5 generate the multiplicative group
Here is the representation table using a different generator
In [60]: g.multiplicative_order()
Out[60]: 6
In [61]: 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 [62]: e = GF7(2); e
Out[62]: GF(2, order=7)
In [63]: e = GF7(2); e
Out[63]: GF(α^2, order=7)
It has
In [64]: e.multiplicative_order()
Out[64]: 3
In [65]: 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