-- | Array & table functions
module Music.Theory.Array where

import Data.List {- base -}
import qualified Data.Array as A {- array -}

import qualified Music.Theory.List as T {- hmt-base -}

-- * Association List (List Array)

-- | 'T.minmax' of /k/.
larray_bounds :: Ord k => [(k,v)] -> (k,k)
larray_bounds :: forall k v. Ord k => [(k, v)] -> (k, k)
larray_bounds = forall t. Ord t => [t] -> (t, t)
T.minmax forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> a
fst

-- | 'A.array' of association list.
larray :: A.Ix k => [(k,v)] -> A.Array k v
larray :: forall k v. Ix k => [(k, v)] -> Array k v
larray [(k, v)]
a = forall i e. Ix i => (i, i) -> [(i, e)] -> Array i e
A.array (forall k v. Ord k => [(k, v)] -> (k, k)
larray_bounds [(k, v)]
a) [(k, v)]
a

-- * List Table

-- | Plain list representation of a two-dimensional table of /a/ in
-- row-order.  Tables are regular, ie. all rows have equal numbers of
-- columns.
type Table a = [[a]]

-- | Table row count.
tbl_rows :: Table t -> Int
tbl_rows :: forall t. Table t -> Int
tbl_rows = forall (t :: * -> *) a. Foldable t => t a -> Int
length

-- | Table column count, assumes table is regular.
tbl_columns :: Table t -> Int
tbl_columns :: forall t. Table t -> Int
tbl_columns Table t
tbl =
  case Table t
tbl of
    [] -> Int
0
    [t]
r0:Table t
_ -> forall (t :: * -> *) a. Foldable t => t a -> Int
length [t]
r0

-- | Determine is table is regular, ie. all rows have the same number of columns.
--
-- > tbl_is_regular [[0..3],[4..7],[8..11]] == True
tbl_is_regular :: Table t -> Bool
tbl_is_regular :: forall t. Table t -> Bool
tbl_is_regular = (forall a. Eq a => a -> a -> Bool
== Int
1) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Int
length forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Eq a => [a] -> [a]
nub forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall (t :: * -> *) a. Foldable t => t a -> Int
length

-- | Map /f/ at table, padding short rows with /k/.
tbl_make_regular :: (t -> u,u) -> Table t -> Table u
tbl_make_regular :: forall t u. (t -> u, u) -> Table t -> Table u
tbl_make_regular (t -> u
f,u
k) Table t
tbl =
    let z :: Int
z = forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum (forall a b. (a -> b) -> [a] -> [b]
map forall (t :: * -> *) a. Foldable t => t a -> Int
length Table t
tbl)
    in forall a b. (a -> b) -> [a] -> [b]
map (forall a. a -> Int -> [a] -> [a]
T.pad_right u
k Int
z forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map t -> u
f) Table t
tbl

-- | Append a sequence of /nil/ (or default) values to each row of /tbl/
-- so to make it regular (ie. all rows of equal length).
tbl_make_regular_nil :: t -> Table t -> Table t
tbl_make_regular_nil :: forall t. t -> Table t -> Table t
tbl_make_regular_nil t
k = forall t u. (t -> u, u) -> Table t -> Table u
tbl_make_regular (forall a. a -> a
id,t
k)

-- * Matrix Indices

-- | Matrix dimensions are written (rows,columns).
type Dimensions i = (i,i)

-- | Matrix indices are written (row,column) & are here _zero_ indexed.
type Ix i = (i,i)

-- | Translate 'Ix' by row and column delta.
--
-- > ix_translate (1,2) (3,4) == (4,6)
ix_translate :: Num t => (t,t) -> Ix t -> Ix t
ix_translate :: forall t. Num t => (t, t) -> (t, t) -> (t, t)
ix_translate (t
dr,t
dc) (t
r,t
c) = (t
r forall a. Num a => a -> a -> a
+ t
dr,t
c forall a. Num a => a -> a -> a
+ t
dc)

-- | Modulo 'Ix' by 'Dimensions'.
--
-- > ix_modulo (4,4) (3,7) == (3,3)
ix_modulo :: Integral t => Dimensions t -> Ix t -> Ix t
ix_modulo :: forall t.
Integral t =>
Dimensions t -> Dimensions t -> Dimensions t
ix_modulo (t
nr,t
nc) (t
r,t
c) = (t
r forall a. Integral a => a -> a -> a
`mod` t
nr,t
c forall a. Integral a => a -> a -> a
`mod` t
nc)

-- | Given number of columns and row index, list row indices.
--
-- > row_indices 3 1 == [(1,0),(1,1),(1,2)]
row_indices :: (Enum t, Num t) => t -> t -> [Ix t]
row_indices :: forall t. (Enum t, Num t) => t -> t -> [Ix t]
row_indices t
nc t
r = forall a b. (a -> b) -> [a] -> [b]
map (\t
c -> (t
r,t
c)) [t
0 .. t
nc forall a. Num a => a -> a -> a
- t
1]

-- | Given number of rows and column index, list column indices.
--
-- > column_indices_at 3 1 == [(0,1),(1,1),(2,1)]
column_indices_at :: (Enum t, Num t) => t -> t -> [Ix t]
column_indices_at :: forall t. (Enum t, Num t) => t -> t -> [Ix t]
column_indices_at t
nr t
c = forall a b. (a -> b) -> [a] -> [b]
map (\t
r -> (t
r,t
c)) [t
0 .. t
nr forall a. Num a => a -> a -> a
- t
1]

-- | All zero-indexed matrix indices, in row order.  This is the order
-- given by 'sort'.
--
-- > matrix_indices (2,3) == [(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)]
-- > sort (matrix_indices (2,3)) == matrix_indices (2,3)
matrix_indices :: (Enum t, Num t) => Dimensions t -> [Ix t]
matrix_indices :: forall t. (Enum t, Num t) => Dimensions t -> [Dimensions t]
matrix_indices (t
nr,t
nc) = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (forall t. (Enum t, Num t) => t -> t -> [Ix t]
row_indices t
nc) [t
0 .. t
nr forall a. Num a => a -> a -> a
- t
1 ]

-- | Corner indices of given 'Dimensions', in row order.
--
-- > matrix_corner_indices (2,3) == [(0,0),(0,2),(1,0),(1,2)]
matrix_corner_indices :: Num t => Dimensions t -> [Ix t]
matrix_corner_indices :: forall t. Num t => Dimensions t -> [Dimensions t]
matrix_corner_indices (t
nr,t
nc) = [(t
0,t
0),(t
0,t
nc forall a. Num a => a -> a -> a
- t
1),(t
nr forall a. Num a => a -> a -> a
- t
1,t
0),(t
nr forall a. Num a => a -> a -> a
- t
1,t
nc forall a. Num a => a -> a -> a
- t
1)]

-- | Parallelogram corner indices, given as rectangular 'Dimensions' with an
-- offset for the lower indices.
--
-- > parallelogram_corner_indices ((2,3),2) == [(0,0),(0,2),(1,2),(1,4)]
parallelogram_corner_indices :: Num t => (Dimensions t,t) -> [Ix t]
parallelogram_corner_indices :: forall t. Num t => (Dimensions t, t) -> [Dimensions t]
parallelogram_corner_indices ((t
nr,t
nc),t
o) = [(t
0,t
0),(t
0,t
nc forall a. Num a => a -> a -> a
- t
1),(t
nr forall a. Num a => a -> a -> a
- t
1,t
o),(t
nr forall a. Num a => a -> a -> a
- t
1,t
nc forall a. Num a => a -> a -> a
+ t
o forall a. Num a => a -> a -> a
- t
1)]

-- | Apply 'ix_modulo' and 'ix_translate' for all 'matrix_indices',
-- ie. all translations of a 'shape' in row order.  The resulting 'Ix'
-- sets are not sorted and may have duplicates.
--
-- > concat (all_ix_translations (2,3) [(0,0)]) == matrix_indices (2,3)
all_ix_translations :: Integral t => Dimensions t -> [Ix t] -> [[Ix t]]
all_ix_translations :: forall t.
Integral t =>
Dimensions t -> [Dimensions t] -> [[Dimensions t]]
all_ix_translations Dimensions t
dm [Dimensions t]
ix =
    let f :: Dimensions t -> Dimensions t -> Dimensions t
f Dimensions t
z = forall t.
Integral t =>
Dimensions t -> Dimensions t -> Dimensions t
ix_modulo Dimensions t
dm forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t. Num t => (t, t) -> (t, t) -> (t, t)
ix_translate Dimensions t
z
    in forall a b. (a -> b) -> [a] -> [b]
map (\Dimensions t
dx -> forall a b. (a -> b) -> [a] -> [b]
map (Dimensions t -> Dimensions t -> Dimensions t
f Dimensions t
dx) [Dimensions t]
ix) (forall t. (Enum t, Num t) => Dimensions t -> [Dimensions t]
matrix_indices Dimensions t
dm)

-- | Sort sets into row order and remove duplicates.
all_ix_translations_uniq :: Integral t => Dimensions t -> [Ix t] -> [[Ix t]]
all_ix_translations_uniq :: forall t.
Integral t =>
Dimensions t -> [Dimensions t] -> [[Dimensions t]]
all_ix_translations_uniq Ix t
dm = forall a. Eq a => [a] -> [a]
nub forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map forall a. Ord a => [a] -> [a]
sort forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t.
Integral t =>
Dimensions t -> [Dimensions t] -> [[Dimensions t]]
all_ix_translations Ix t
dm