matrax v0.3.4 Matrax View Source
A matrix library in pure Elixir based on atomics
.
Key features:
- concurrent accessibility: atomics are mutable and can be accessed from multiple processes
- access path only transofrmations: transformations like transpose change only the access path so the same matrix can be worked on in multiple states by different processes at the same time
- fast accessibility: operations like
get/2
andput/3
are very fast and based only on pure Elixir
Examples
iex> matrax = Matrax.new(100, 100) # 100 x 100 matrix
iex> matrax |> Matrax.put({0, 0}, 10) # add 10 to position {0, 0}
iex> matrax |> Matrax.get({0, 0})
10
iex> matrax |> Matrax.add({0, 0}, 80)
iex> matrax |> Matrax.get({0, 0})
90
Enumerable protocol
Matrax
implements the Enumerable protocol, so all Enum functions can be used:
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.put({0, 0}, 8)
iex> matrax |> Enum.max()
8
iex> matrax |> Enum.member?(7)
false
Link to this section Summary
Functions
Adds a list of matrices to matrax
.
Adds incr
to atomic at position
.
Atomic addition and return of the result.
Applies the given fun
function to all elements of matrax
.
Returns position tuple of biggest value.
Returns position tuple of smallest value.
Clears all changes made to %Matrax{}
struct by
setting the :changes
key to []
and reverting its modifications
to :rows
& :columns
.
Clears last change made to %Matrax{}
struct by removing
the head of :changes
key and reverting its modifications
to :rows
& :columns
.
Only modifies the struct, it doesn't move or mutate data.
Converts given column index of %Matrax{}
to list.
Atomically compares the value at position
with expected
,
and if those are equal, sets value at position
to desired
.
Concatenates a list of %Matrax{}
matrices.
Returns a %Matrax{}
struct with a new atomics reference
and positional values identical to the given matrax
.
Returns count of values (rows * columns).
Only modifies the struct, it doesn't move or mutate data.
Drops column of matrix at given column_index
.
Drops row of matrix at given row_index
.
Atomically replaces value at position
with value
and
returns the value it had before.
Returns position of the first occurence of the given value
or nil
if nothing was found.
Flip columns of matrix in the left-right direction (vertical axis).
Flip rows of matrix in the up-down direction (horizontal axis).
Returns value at position
from the given matrax.
Create identity square matrix of given size
.
Returns a position tuple for the given atomics index
.
Returns largest integer in matrax
.
Checks if value
exists within matrax
.
Returns smallest integer in matrax
.
Converts an integer list_of_lists
to a new %Matrax{}
struct.
Converts a list_of_lists
to a new %Matrax{}
struct.
Returns a new %Matrax{}
struct with the given rows
and columns
size.
Returns atomics index corresponding to the position
tuple in the given %Matrax{}
struct.
Puts value
into matrax
at position
.
Reshapes matrax
to the given rows
& cols
.
Only modifies the struct, it doesn't move or mutate data.
Converts given row index of %Matrax{}
to list.
Set column of a matrix at column_index
to the values from the given 1-column matrix.
Set row of a matrix at row_index
to the values from the given 1-row matrix.
Subtracts a list of matrices from matrax
.
Subtracts decr
from atomic at position
.
Atomic subtraction and return of the result.
Only modifies the struct, it doesn't move or mutate data.
Returns sum of integers in matrax
.
Converts %Matrax{}
to a flat list.
Converts %Matrax{}
to list of lists.
Trace of matrix (sum of all diagonal elements).
Only modifies the struct, it doesn't move or mutate data.
Link to this section Types
position()
View Sourceposition() :: {row :: non_neg_integer(), col :: non_neg_integer()}
t()
View Sourcet() :: %Matrax{ atomics: reference(), changes: list(), columns: pos_integer(), max: pos_integer(), min: integer(), rows: pos_integer(), signed: boolean() }
Link to this section Functions
Adds a list of matrices to matrax
.
Size (rows, columns) of matrices must match.
Returns :ok
.
Examples
iex> matrax = Matrax.new(5, 5)
iex> matrax7 = Matrax.new(5, 5, seed_fun: fn _ -> 7 end)
iex> matrax |> Matrax.get({0, 0})
0
iex> matrax |> Matrax.add([matrax7, matrax7])
iex> matrax |> Matrax.get({0, 0})
14
iex> matrax |> Matrax.add([matrax7])
iex> matrax |> Matrax.get({0, 0})
21
Adds incr
to atomic at position
.
Returns :ok
.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.add({0, 0}, 2)
:ok
iex> matrax |> Matrax.add({0, 0}, 2)
:ok
iex> matrax |> Matrax.get({0, 0})
4
Atomic addition and return of the result.
Adds incr
to atomic at position
and returns result.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.add_get({0, 0}, 2)
2
iex> matrax |> Matrax.add_get({0, 0}, 2)
4
Applies the given fun
function to all elements of matrax
.
If arity of fun
is 1 it receives the integer as single argument.
If arity of fun
is 2 it receives the integer as first and
position tuple as the second argument.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.apply(fn int -> int + 2 end)
iex> matrax |> Matrax.get({0, 0})
2
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.apply(fn _int, {row, col} -> row * col end)
iex> matrax |> Matrax.get({9, 9})
81
Returns position tuple of biggest value.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.argmax()
{4, 4}
iex> matrax = Matrax.new(5, 5) # all zeros
iex> matrax |> Matrax.argmax()
{0, 0}
Returns position tuple of smallest value.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.argmin()
{0, 0}
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> -(row * col) end)
iex> matrax |> Matrax.argmin()
{4, 4}
Clears all changes made to %Matrax{}
struct by
setting the :changes
key to []
and reverting its modifications
to :rows
& :columns
.
Clears access path only modifications like transpose/1
but not
modifications to integer values in the :atomics
.
Examples
iex> matrax = Matrax.identity(3)
iex> matrax |> Matrax.to_list_of_lists()
[
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
]
iex> matrax = matrax |> Matrax.diagonal()
iex> matrax |> Matrax.apply(fn _ -> 8 end)
iex> matrax |> Matrax.to_list_of_lists()
[[8, 8, 8]]
iex> matrax = matrax |> Matrax.column(0)
iex> matrax |> Matrax.to_list_of_lists()
[[8]]
iex> matrax = matrax |> Matrax.clear_changes()
iex> matrax |> Matrax.to_list_of_lists()
[
[8, 0, 0],
[0, 8, 0],
[0, 0, 8]
]
Clears last change made to %Matrax{}
struct by removing
the head of :changes
key and reverting its modifications
to :rows
& :columns
.
Clears access path only modifications like transpose/1
but not
modifications to integer values in the :atomics
.
Examples
iex> matrax = Matrax.identity(3)
iex> matrax |> Matrax.to_list_of_lists()
[
[1, 0, 0],
[0, 1, 0],
[0, 0, 1]
]
iex> matrax = matrax |> Matrax.diagonal()
iex> matrax |> Matrax.apply(fn _ -> 8 end)
iex> matrax |> Matrax.to_list_of_lists()
[[8, 8, 8]]
iex> matrax = matrax |> Matrax.clear_last_change()
iex> matrax |> Matrax.to_list_of_lists()
[
[8, 0, 0],
[0, 8, 0],
[0, 0, 8]
]
Only modifies the struct, it doesn't move or mutate data.
Reduces matrix to only one column at given column
index.
After column/2
the access path to positions will be
modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {_row, col} -> col end)
iex> matrax |> Matrax.column(4) |> Matrax.to_list_of_lists
[[4], [4], [4], [4], [4]]
column_to_list(matrax, col)
View Sourcecolumn_to_list(t(), non_neg_integer()) :: [integer()]
Converts given column index of %Matrax{}
to list.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.column_to_list(2)
[0, 2, 4, 6, 8]
Atomically compares the value at position
with expected
,
and if those are equal, sets value at position
to desired
.
Returns :ok if desired
was written.
Returns the actual value at position
if it does not equal to desired
.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.compare_exchange({0, 0}, 0, -10)
:ok
iex> matrax |> Matrax.compare_exchange({0, 0}, 3, 10)
-10
Concatenates a list of %Matrax{}
matrices.
Returns a new %Matrax{}
struct with a new atomics reference containing all values
of matrices from list
.
Options
:signed
- (boolean) to have signed or unsigned 64bit integers in the new matrix. Defaults totrue
.
Examples
iex> matrax = Matrax.new(3, 3, seed_fun: fn _, {_row, col} -> col end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 1, 2],
[0, 1, 2],
[0, 1, 2]
]
iex> Matrax.concat([matrax, matrax], :rows) |> Matrax.to_list_of_lists()
[
[0, 1, 2],
[0, 1, 2],
[0, 1, 2],
[0, 1, 2],
[0, 1, 2],
[0, 1, 2]
]
iex> Matrax.concat([matrax, matrax], :columns) |> Matrax.to_list_of_lists()
[
[0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2]
]
Returns a %Matrax{}
struct with a new atomics reference
and positional values identical to the given matrax
.
The returned copy is always changes: []
so this
can be used to finish the access-path only changes
by the transpose/1
, submatrix/3
, reshape/3
functions.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.put({0, 0}, -9)
iex> matrax2 = Matrax.copy(matrax)
iex> Matrax.get(matrax2, {0, 0})
-9
Returns count of values (rows * columns).
Examples
iex> matrax = Matrax.new(5, 5)
iex> Matrax.count(matrax)
25
Only modifies the struct, it doesn't move or mutate data.
After diagonal/1
the access path to positions will be
modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.identity(5)
iex> matrax |> Matrax.diagonal() |> Matrax.to_list_of_lists
[[1, 1, 1, 1, 1]]
drop_column(matrax, column_index)
View Sourcedrop_column(t(), non_neg_integer()) :: t()
Drops column of matrix at given column_index
.
Only modifies the struct, it doesn't move or mutate data.
After drop_column/2
the access path to positions
will be modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(4, 5, seed_fun: fn _, {_row, col} -> col end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4],
]
iex> matrax |> Matrax.drop_column(1) |> Matrax.to_list_of_lists()
[
[0, 2, 3, 4],
[0, 2, 3, 4],
[0, 2, 3, 4],
[0, 2, 3, 4],
]
drop_row(matrax, row_index)
View Sourcedrop_row(t(), non_neg_integer()) :: t()
Drops row of matrix at given row_index
.
Only modifies the struct, it doesn't move or mutate data.
After drop_row/2
the access path to positions
will be modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(5, 4, seed_fun: fn _, {row, _col} -> row end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
]
iex> matrax |> Matrax.drop_row(1) |> Matrax.to_list_of_lists()
[
[0, 0, 0, 0],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
]
Atomically replaces value at position
with value
and
returns the value it had before.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.exchange({0, 0}, -10)
0
iex> matrax |> Matrax.exchange({0, 0}, -15)
-10
Returns position of the first occurence of the given value
or nil
if nothing was found.
Examples
iex> Matrax.new(5, 5) |> Matrax.find(0)
{0, 0}
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.find(16)
{4, 4}
iex> matrax |> Matrax.find(42)
nil
Flip columns of matrix in the left-right direction (vertical axis).
After flip_lr/1
the access path to positions will be
modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(3, 4, seed_fun: fn _, {_row, col} -> col end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3]
]
iex> matrax |> Matrax.flip_lr() |> Matrax.to_list_of_lists()
[
[3, 2, 1, 0],
[3, 2, 1, 0],
[3, 2, 1, 0]
]
Flip rows of matrix in the up-down direction (horizontal axis).
After flip_ud/1
the access path to positions will be
modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(3, 4, seed_fun: fn _, {row, _col} -> row end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 0, 0, 0],
[1, 1, 1, 1],
[2, 2, 2, 2]
]
iex> matrax |> Matrax.flip_ud() |> Matrax.to_list_of_lists()
[
[2, 2, 2, 2],
[1, 1, 1, 1],
[0, 0, 0, 0]
]
Returns value at position
from the given matrax.
Examples
iex> matrax = Matrax.new(10, 10, seed_fun: fn _ -> 3 end)
iex> matrax |> Matrax.get({0, 5})
3
Create identity square matrix of given size
.
Examples
iex> Matrax.identity(5) |> Matrax.to_list_of_lists
[
[1, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 0, 1]
]
index_to_position(matrax, index)
View Sourceindex_to_position(t(), pos_integer()) :: position()
Returns a position tuple for the given atomics index
.
Indices of atomix are 1 based.
Examples
iex> matrax = Matrax.new(10, 10)
iex> Matrax.index_to_position(matrax, 1)
{0, 0}
iex> Matrax.index_to_position(matrax, 10)
{0, 9}
Returns largest integer in matrax
.
Examples
iex> matrax = Matrax.new(10, 10, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.max()
81
iex> Matrax.new(5, 5) |> Matrax.max()
0
Checks if value
exists within matrax
.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.member?(6)
true
iex> matrax |> Matrax.member?(100)
false
Returns smallest integer in matrax
.
Examples
iex> matrax = Matrax.new(10, 10, seed_fun: fn _ -> 7 end)
iex> matrax |> Matrax.min()
7
Converts an integer list_of_lists
to a new %Matrax{}
struct.
Same as new/2
without options.
Examples
iex> matrax = %Matrax{rows: 2, columns: 3} = Matrax.new([[1,2,3], [4, 5, 6]])
iex> matrax |> Matrax.to_list_of_lists
[[1,2,3], [4, 5, 6]]
Converts a list_of_lists
to a new %Matrax{}
struct.
Options
:signed
- (boolean) to have signed or unsigned 64bit integers. Defaults totrue
.
Examples
iex> matrax = %Matrax{rows: 2, columns: 3} = Matrax.new([[1,2,3], [4, 5, 6]], signed: false)
iex> matrax |> Matrax.to_list_of_lists
[[1,2,3], [4, 5, 6]]
iex> matrax |> Matrax.count
6
new(rows, columns, options \\ [])
View Sourcenew(pos_integer(), pos_integer(), list()) :: t()
Returns a new %Matrax{}
struct with the given rows
and columns
size.
Options
:seed_fun
- (function) a function to seed all positions. Seeapply/2
for further information.:signed
- (boolean) to have signed or unsigned 64bit integers. Defaults totrue
.
Examples
Matrax.new(10, 5) # 10 x 5 matrix
Matrax.new(10, 5, signed: false) # unsigned integers
Matrax.new(10, 5, seed_fun: fn _, {row, col} -> row * col end) # seed values
position_to_index(matrax, position)
View Sourceposition_to_index(t(), position()) :: pos_integer()
Returns atomics index corresponding to the position
tuple in the given %Matrax{}
struct.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.position_to_index({1, 1})
12
iex> matrax |> Matrax.position_to_index({0, 4})
5
Puts value
into matrax
at position
.
Returns :ok
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.put({1, 3}, 5)
:ok
reshape(matrax, desired_rows, desired_columns)
View Sourcereshape(t(), pos_integer(), pos_integer()) :: t()
Reshapes matrax
to the given rows
& cols
.
After reshape/3
the access path to positions will be
modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(4, 3, seed_fun: fn _, {_row, col} -> col end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 1, 2],
[0, 1, 2],
[0, 1, 2],
[0, 1, 2]
]
iex> matrax |> Matrax.reshape(2, 6) |> Matrax.to_list_of_lists()
[
[0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2]
]
Only modifies the struct, it doesn't move or mutate data.
Reduces matrix to only one row at given row
index.
After row/2
the access path to positions will be
modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, _col} -> row end)
iex> matrax |> Matrax.row(4) |> Matrax.to_list_of_lists
[[4, 4, 4, 4, 4]]
row_to_list(matrax, row)
View Sourcerow_to_list(t(), non_neg_integer()) :: [integer()]
Converts given row index of %Matrax{}
to list.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.row_to_list(2)
[0, 2, 4, 6, 8]
set_column(matrax, column_index, column_matrax)
View Sourceset_column(t(), non_neg_integer(), t()) :: t()
Set column of a matrix at column_index
to the values from the given 1-column matrix.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _ -> 1 end)
iex> column_matrax = Matrax.new(5, 1, seed_fun: fn _ -> 3 end)
iex> Matrax.set_column(matrax, 2, column_matrax) |> Matrax.to_list_of_lists
[
[1, 1, 3, 1, 1],
[1, 1, 3, 1, 1],
[1, 1, 3, 1, 1],
[1, 1, 3, 1, 1],
[1, 1, 3, 1, 1],
]
set_row(matrax, row_index, row_matrax)
View Sourceset_row(t(), non_neg_integer(), t()) :: t()
Set row of a matrix at row_index
to the values from the given 1-row matrix.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _ -> 1 end)
iex> row_matrax = Matrax.new(1, 5, seed_fun: fn _ -> 3 end)
iex> Matrax.set_row(matrax, 2, row_matrax) |> Matrax.to_list_of_lists
[
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[3, 3, 3, 3, 3],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
]
Subtracts a list of matrices from matrax
.
Size (rows, columns) of matrices must match.
Returns :ok
.
Examples
iex> matrax = Matrax.new(5, 5)
iex> matrax7 = Matrax.new(5, 5, seed_fun: fn _ -> 7 end)
iex> matrax |> Matrax.get({0, 0})
0
iex> matrax |> Matrax.sub([matrax7, matrax7])
iex> matrax |> Matrax.get({0, 0})
-14
iex> matrax |> Matrax.sub([matrax7])
iex> matrax |> Matrax.get({0, 0})
-21
Subtracts decr
from atomic at position
.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.sub({0, 0}, 1)
:ok
iex> matrax |> Matrax.sub({0, 0}, 1)
:ok
iex> matrax |> Matrax.get({0, 0})
-2
Atomic subtraction and return of the result.
Subtracts decr
from atomic at position
and returns result.
Examples
iex> matrax = Matrax.new(10, 10)
iex> matrax |> Matrax.sub_get({0, 0}, 2)
-2
iex> matrax |> Matrax.sub_get({0, 0}, 2)
-4
Only modifies the struct, it doesn't move or mutate data.
Ranges are inclusive.
After submatrix/3
the access path to positions will be
modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(7, 4, seed_fun: fn _, {row, col} -> row + col end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 1, 2, 3],
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6],
[4, 5, 6, 7],
[5, 6, 7, 8],
[6, 7, 8, 9]
]
iex> matrax |> Matrax.submatrix(5..6, 1..3) |> Matrax.to_list_of_lists()
[
[6, 7, 8],
[7, 8, 9]
]
Returns sum of integers in matrax
.
Examples
iex> matrax = Matrax.new(10, 10, seed_fun: fn _, {row, col} -> row * col end)
iex> matrax |> Matrax.sum()
2025
iex> Matrax.new(5, 5, seed_fun: fn _ -> 1 end) |> Matrax.sum()
25
Converts %Matrax{}
to a flat list.
Examples
iex> matrax = Matrax.new(3, 3, seed_fun: fn _, {row, col} -> row * col end)
iex> Matrax.to_list(matrax)
[0, 0, 0, 0, 1, 2, 0, 2, 4]
Converts %Matrax{}
to list of lists.
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _, {row, col} -> row * col end)
iex> Matrax.to_list_of_lists(matrax)
[
[0, 0, 0, 0, 0],
[0, 1, 2, 3, 4],
[0, 2, 4, 6, 8],
[0, 3, 6, 9, 12],
[0, 4, 8, 12, 16]
]
Trace of matrix (sum of all diagonal elements).
Examples
iex> matrax = Matrax.new(5, 5, seed_fun: fn _ -> 1 end)
iex> matrax |> Matrax.trace()
5
Only modifies the struct, it doesn't move or mutate data.
After transpose/1
the access path to positions
will be modified during execution.
If you want to get a new :atomics
with mofified data
use the copy/1
function which applies the :changes
.
Examples
iex> matrax = Matrax.new(7, 4, seed_fun: fn _, {row, col} -> row + col end)
iex> matrax |> Matrax.to_list_of_lists()
[
[0, 1, 2, 3],
[1, 2, 3, 4],
[2, 3, 4, 5],
[3, 4, 5, 6],
[4, 5, 6, 7],
[5, 6, 7, 8],
[6, 7, 8, 9]
]
iex> matrax |> Matrax.transpose() |> Matrax.to_list_of_lists()
[
[0, 1, 2, 3, 4, 5, 6],
[1, 2, 3, 4, 5, 6, 7],
[2, 3, 4, 5, 6, 7, 8],
[3, 4, 5, 6, 7, 8, 9]
]