This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

# Matrix

A matrix implemention for Elm. Internally it uses a flat array for speed reasons.

# The matrix type

type alias Matrix a = { size : ( Int, Int ) , data : Array a }

Matrix a has a given size, and data contained within

# Creating a matrix

repeat : Int -> Int -> a -> Matrix a

Create a matrix of a given size `x y` with a default value of `v`

fromList : List (List a) -> Maybe (Matrix a)

Create a matrix from a list of lists. If the lists within the list are not consistently sized, return `Nothing` Otherwise return a matrix with the size as the size of the outer and nested lists. The outer list represents the y axis and inner lists represent the x axis. Eg: [ [ {x=0, y=0}, {x=1, y=0}, {x=2, y=0} ] , [ {x=0, y=1}, {x=1, y=1}, {x=2, y=1} ] , [ {x=0, y=2}, {x=1, y=2}, {x=2, y=2} ] ]

empty : Matrix a

Create an empty matrix

# Get matrix dimensions

height : Matrix a -> Int

Height of a given matrix

width : Matrix a -> Int

Width of a given matrix

# Dealing with individual elements

get : Int -> Int -> Matrix a -> Maybe a

Get a value from a given `x y` and return `Just v` if it exists Otherwise `Nothing`

set : Int -> Int -> a -> Matrix a -> Matrix a

Set a value at a given `i, j` in the matrix and return the new matrix If the `i, j` is out of bounds then return the unmodified matrix

update : Int -> Int -> (a -> a) -> Matrix a -> Matrix a

Update an element at `x, y` with the given update function If out of bounds, return the matrix unchanged

# Appending to an Matrix

concatVertical : Matrix a -> Matrix a -> Maybe (Matrix a)

Append a matrix to another matrix vertically and return the result. Return Nothing if the widths don't match

concatHorizontal : Matrix a -> Matrix a -> Maybe (Matrix a)

Append a matrix to another matrix horizontally and return the result. Return Nothing if the heights don't match

# Get rows/columns

getRow : Int -> Matrix a -> Maybe (Array a)

Get a row at a given j

getColumn : Int -> Matrix a -> Maybe (Array a)

Get a column at a given i

# Applying functions

filter : (a -> Bool) -> Matrix a -> Array a

Keep only elements that return `True` when passed to the given function f

map : (a -> b) -> Matrix a -> Matrix b

Apply a function of every element in the matrix

map2 : (a -> b -> c) -> Matrix a -> Matrix b -> Maybe (Matrix c)

Apply a function to two matricies at once

indexedMap : (Int -> Int -> a -> b) -> Matrix a -> Matrix b

Apply a function, taking the `x, y` of every element in the matrix

toIndexedArray : Matrix a -> Array ( ( Int, Int ), a )

Convert a matrix to an indexed array

``````module Matrix
exposing
( Matrix
, concatHorizontal
, concatVertical
, empty
, filter
, fromList
, get
, getColumn
, getRow
, height
, indexedMap
, map
, map2
, repeat
, set
, toIndexedArray
, update
, width
)

{-| A matrix implemention for Elm.
Internally it uses a flat array for speed reasons.

# The matrix type

@docs Matrix

# Creating a matrix

@docs repeat, fromList, empty

# Get matrix dimensions

@docs height, width

# Dealing with individual elements

@docs get, set, update

# Appending to an Matrix

@docs concatVertical, concatHorizontal

# Get rows/columns

@docs getRow, getColumn

# Applying functions

@docs filter, map, map2, indexedMap, toIndexedArray

-}

import Array.Hamt as Array exposing (Array)
import List

{-| Matrix a has a given size, and data contained within
-}
type alias Matrix a =
{ size : ( Int, Int )
, data : Array a
}

{-| Create an empty matrix
-}
empty : Matrix a
empty =
{ size = ( 0, 0 ), data = Array.empty }

{-| Width of a given matrix
-}
width : Matrix a -> Int
width matrix =
Tuple.first matrix.size

{-| Height of a given matrix
-}
height : Matrix a -> Int
height matrix =
Tuple.second matrix.size

{-| Create a matrix of a given size `x y` with a default value of `v`
-}
repeat : Int -> Int -> a -> Matrix a
repeat x y v =
{ size = ( x, y )
, data = Array.repeat (x * y) v
}

{-| Create a matrix from a list of lists.
If the lists within the list are not consistently sized, return `Nothing`
Otherwise return a matrix with the size as the size of the outer and nested lists.
The outer list represents the y axis and inner lists represent the x axis.
Eg:
[ [ {x=0, y=0}, {x=1, y=0}, {x=2, y=0} ]
, [ {x=0, y=1}, {x=1, y=1}, {x=2, y=1} ]
, [ {x=0, y=2}, {x=1, y=2}, {x=2, y=2} ]
]
-}
fromList : List (List a) -> Maybe (Matrix a)
fromList list =
let
-- the number of elements in the top level list is taken as height
height =
List.length list

-- the number of elements in the first element is taken as the width
width =
List.length <|
Just x ->
x

Nothing ->
[]

-- ensure that all "rows" are the same size
allSame =
List.isEmpty <| List.filter (\x -> List.length x /= width) list
in
if not allSame then
Nothing
else
Just { size = ( width, height ), data = Array.fromList <| List.concat list }

{-| Get a value from a given `x y` and return `Just v` if it exists
Otherwise `Nothing`
-}
get : Int -> Int -> Matrix a -> Maybe a
get i j matrix =
let
pos =
(j * width matrix) + i
in
if (i < width matrix && i > -1) && (j < height matrix && j > -1) then
Array.get pos matrix.data
else
Nothing

{-| Get a row at a given j
-}
getRow : Int -> Matrix a -> Maybe (Array a)
getRow j matrix =
let
start =
j * width matrix

end =
start + width matrix
in
if end > (width matrix * height matrix) then
Nothing
else
Just <| Array.slice start end matrix.data

{-| Get a column at a given i
-}
getColumn : Int -> Matrix a -> Maybe (Array a)
getColumn i matrix =
let
width =
Tuple.first matrix.size

height =
Tuple.second matrix.size

indices =
List.map (\x -> x * width + i) (List.range 0 (height - 1))
in
if i >= width then
Nothing
else
Just <|
Array.fromList <|
List.foldl
(\index ls ->
case Array.get index matrix.data of
Just v ->
ls ++ [ v ]

Nothing ->
ls
)
[]
indices

{-| Append a matrix to another matrix horizontally and return the result. Return Nothing if the heights don't match
-}
concatHorizontal : Matrix a -> Matrix a -> Maybe (Matrix a)
concatHorizontal a b =
let
finalWidth =
Tuple.first a.size + Tuple.first b.size

insert i xs array =
Array.append
(Array.append (Array.slice 0 i array) xs)
(Array.slice i (Array.length array) array)
in
if Tuple.second a.size /= Tuple.second b.size then
Nothing
else
Just <|
{ a
| size = ( finalWidth, Tuple.second a.size )
, data =
List.foldl
(\( i, xs ) acc -> insert (i * finalWidth) xs acc)
b.data
<|
List.foldl
(\i ls ->
case getRow i a of
Just v ->
ls ++ [ ( i, v ) ]

Nothing ->
ls
)
[]
(List.range 0 (Tuple.second a.size - 1))
}

{-| Append a matrix to another matrix vertically and return the result. Return Nothing if the widths don't match
-}
concatVertical : Matrix a -> Matrix a -> Maybe (Matrix a)
concatVertical a b =
if Tuple.first a.size /= Tuple.first b.size then
Nothing
else
Just <| { a | size = ( Tuple.first a.size, Tuple.second a.size + Tuple.second b.size ), data = Array.append a.data b.data }

{-| Set a value at a given `i, j` in the matrix and return the new matrix
If the `i, j` is out of bounds then return the unmodified matrix
-}
set : Int -> Int -> a -> Matrix a -> Matrix a
set i j v matrix =
let
pos =
(j * Tuple.first matrix.size) + i
in
if (i < width matrix && i > -1) && (j < height matrix && j > -1) then
{ matrix | data = Array.set pos v matrix.data }
else
matrix

{-| Update an element at `x, y` with the given update function
If out of bounds, return the matrix unchanged
-}
update : Int -> Int -> (a -> a) -> Matrix a -> Matrix a
update x y f matrix =
case get x y matrix of
Nothing ->
matrix

Just v ->
set x y (f v) matrix

{-| Apply a function of every element in the matrix
-}
map : (a -> b) -> Matrix a -> Matrix b
map f matrix =
{ matrix | data = Array.map f matrix.data }

{-| Apply a function to two matricies at once
-}
map2 : (a -> b -> c) -> Matrix a -> Matrix b -> Maybe (Matrix c)
map2 f a b =
if a.size == b.size then
Just { a | data = Array.fromList <| List.map2 f (Array.toList a.data) (Array.toList b.data) }
else
Nothing

{-| Apply a function, taking the `x, y` of every element in the matrix
-}
indexedMap : (Int -> Int -> a -> b) -> Matrix a -> Matrix b
indexedMap f matrix =
let
f_ i v =
let
x =
i % width matrix

y =
i // width matrix
in
f x y v
in
{ matrix | data = Array.fromList <| List.indexedMap f_ <| Array.toList matrix.data }

{-| Keep only elements that return `True` when passed to the given function f
-}
filter : (a -> Bool) -> Matrix a -> Array a
filter f matrix =
Array.filter f matrix.data

{-| Convert a matrix to an indexed array
-}
toIndexedArray : Matrix a -> Array ( ( Int, Int ), a )
toIndexedArray matrix =
(indexedMap (\x y v -> ( ( x, y ), v )) matrix).data
```
```