# Array creation¶

## Explicit construction¶

Galois field arrays can be constructed either explicitly or through numpy view casting. The method of array creation is the same for all Galois fields, but $$\mathrm{GF}(7)$$ is used as an example here.

In [1]: GF7 = galois.GF(7)

# Represents an existing numpy array
In [2]: x_np = np.random.randint(0, 7, 10, dtype=int); x_np
Out[2]: array([3, 0, 2, 2, 0, 1, 3, 0, 6, 5])

# Create a Galois field array through explicit construction (x_np is copied)
In [3]: x = GF7(x_np); x
Out[3]: GF([3, 0, 2, 2, 0, 1, 3, 0, 6, 5], order=7)


## View casting¶

# View cast an existing array to a Galois field array (no copy operation)
In [4]: y = x_np.view(GF7); y
Out[4]: GF([3, 0, 2, 2, 0, 1, 3, 0, 6, 5], order=7)


Warning

View casting creates a pointer to the original data and simply interprets it as a new numpy.ndarray subclass, namely the Galois field classes. So, if the original array is modified so will the Galois field array.

In [5]: x_np
Out[5]: array([3, 0, 2, 2, 0, 1, 3, 0, 6, 5])

# Add 1 (mod 7) to the first element of x_np
In [6]: x_np[0] = (x_np[0] + 1) % 7; x_np
Out[6]: array([4, 0, 2, 2, 0, 1, 3, 0, 6, 5])

# Notice x is unchanged due to the copy during the explicit construction
In [7]: x
Out[7]: GF([3, 0, 2, 2, 0, 1, 3, 0, 6, 5], order=7)

# Notice y is changed due to view casting
In [8]: y
Out[8]: GF([4, 0, 2, 2, 0, 1, 3, 0, 6, 5], order=7)


## Alternate constructors¶

There are alternate constructors for convenience: FieldArray.Zeros(), FieldArray.Ones(), FieldArray.Range(), FieldArray.Random(), and FieldArray.Elements().

In [9]: GF256.Random((2,5))
Out[9]:
GF([[ 18, 119,   2, 204, 223],
[255, 149, 183, 234, 227]], order=2^8)

In [10]: GF256.Range(10,20)
Out[10]: GF([10, 11, 12, 13, 14, 15, 16, 17, 18, 19], order=2^8)

In [11]: GF256.Elements()
Out[11]:
GF([  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,
14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,
28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,
42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,
56,  57,  58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,
84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,
98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153,
154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237,
238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251,
252, 253, 254, 255], order=2^8)


## Array dtypes¶

Galois field arrays support all signed and unsigned integer dtypes, presuming the data type can store values in $$[0, p^m)$$. The default dtype is the smallest valid unsigned dtype.

In [12]: GF = galois.GF(7)

In [13]: a = GF.Random(10); a
Out[13]: GF([1, 2, 4, 5, 0, 6, 5, 2, 3, 5], order=7)

In [14]: a.dtype
Out[14]: dtype('uint8')

# Type cast an existing Galois field array to a different dtype
In [15]: a = a.astype(np.int16); a
Out[15]: GF([1, 2, 4, 5, 0, 6, 5, 2, 3, 5], order=7)

In [16]: a.dtype
Out[16]: dtype('int16')


A specific dtype can be chosen by providing the dtype keyword argument during array creation.

# Explicitly create a Galois field array with a specific dtype
In [17]: b = GF.Random(10, dtype=np.int16); b
Out[17]: GF([0, 1, 6, 4, 2, 2, 3, 3, 0, 6], order=7)

In [18]: b.dtype
Out[18]: dtype('int16')


## Field element display modes¶

The default representation of a finite field element is the integer representation. That is, for $$\mathrm{GF}(p^m)$$ the $$p^m$$ elements are represented as $$\{0,1,\dots,p^m-1\}$$. For extension fields, the field elements can alternatively be represented as polynomials in $$\mathrm{GF}(p)[x]$$ with degree less than $$m$$. For prime fields, the integer and polynomial representations are equivalent because in the polynomial representation each element is a degree-$$0$$ polynomial over $$\mathrm{GF}(p)$$.

For example, in $$\mathrm{GF}(2^3)$$ the integer representation of the $$8$$ field elements is $$\{0, 1, 2, 3, 4, 5, 6, 7\}$$ and the polynomial representation is $$\{0,\ 1,\ x,\ x+1,\ x^2,\ x^2+1,\ x^2+x,\ x^2+x+1\}$$.

In [19]: GF = galois.GF(2**3)

In [20]: a = GF.Random(10)

# The default mode represents the field elements as integers
In [21]: a
Out[21]: GF([4, 1, 3, 1, 0, 2, 6, 3, 7, 5], order=2^3)

# The display mode can be set to "poly" mode
In [22]: GF.display("poly"); a
Out[22]:
GF([α^2, 1, α + 1, 1, 0, α, α^2 + α, α + 1, α^2 + α + 1, α^2 + 1],
order=2^3)

# The display mode can be set to "power" mode
In [23]: GF.display("power"); a
Out[23]: GF([α^2, 1, α^3, 1, 0, α, α^4, α^3, α^5, α^6], order=2^3)

# Reset the display mode to the default
In [24]: GF.display(); a
Out[24]: GF([4, 1, 3, 1, 0, 2, 6, 3, 7, 5], order=2^3)


The FieldClass.display() method can be called as a context manager.

# The original display mode
In [25]: print(a)
GF([4, 1, 3, 1, 0, 2, 6, 3, 7, 5], order=2^3)

# The new display context
In [26]: with GF.display("poly"):
....:    print(a)
....:
GF([α^2, 1, α + 1, 1, 0, α, α^2 + α, α + 1, α^2 + α + 1, α^2 + 1],
order=2^3)

In [27]: with GF.display("power"):
....:    print(a)
....:
GF([α^2, 1, α^3, 1, 0, α, α^4, α^3, α^5, α^6], order=2^3)

# Returns to the original display mode
In [28]: print(a)
GF([4, 1, 3, 1, 0, 2, 6, 3, 7, 5], order=2^3)