I was experimenting with the second option so far and have a follow up question.

In prime extension fields numbers are represented as polynomials and all calculations are done using these polynomials.

In GF(2^2):

```
0 = 0 -> (0*x + 0) -> [0, 0]
1 = 1 -> (0*x + 1) -> [0, 1]
2 = x -> (1*x + 0) -> [1, 0]
3 = x + 1 -> (1*x + 1) -> [1, 1]
```

Therefore `2 * 2`

is

```
2 * 2
= (1*x + 0) * (1*x + 0) -> [1, 0] * [1, 0]
= x * x
= x^2 -> [1, 0, 0]
```

How could I achieve this behaviour with Nx functions?

There is element-wise multiplication, but I need that â€śoverflowâ€ť into the next degree.

I found the operation `Nx. outer(t1, t2)`

, which can give me a matrix of `element * row`

results like so:

```
# [1,0] * [1,0]
Nx.outer(Nx.tensor([1,0]), Nx.tensor([1,0]))
#Nx.Tensor<
s64[2][2]
[
[1, 0], # 1 * [1,0]
[0, 0] # 0 * [1,0]
]
>
```

If I can pad and â€śshiftâ€ť the first row to the left I can sum the tensor over the y axis:

```
# 2 * 2 = x * x = x^2 = [1,0,0]
Nx.outer(Nx.tensor([1,0]), Nx.tensor([1,0]))
# [1, 0],
# [0, 0]
|> Nx.pad(0, [{0,0,0}, {1,0,0}]) # insert single zero into all rows
# [0, 1, 0],
# [0, 0, 0]
|> shift([1,0]) # <-- made up: cycle first row by one to the left, second not at all
# [1, 0, 0],
# [0, 0, 0]
|> Nx.sum(axis: 1)
# [1, 0, 0]
```

another example:

```
# 3 * 2 = (x + 1) * x = x^2 + x = [1,1,0]
Nx.outer(Nx.tensor([1,1]), Nx.tensor([1,0]))
# [1, 0],
# [1, 0]
|> Nx.pad(0, [{0,0,0}, {1,0,0}]) # insert single zero into all rows
# [0, 1, 0],
# [0, 1, 0]
|> shift([1,0]) # <-- made up: cycle first row by one to the left, second not at all
# [1, 0, 0],
# [0, 1, 0]
|> Nx.sum(axis: 1)
# [1, 1, 0]
```

Is there a function that behaves like `shift`

?