-- | A data structure representing an /m/×/n/ matrix and some operations on it.
--
-- The SNF transformation is similar to Gaussian elimination, but using integers,
-- which form a principal ideal domain. There is a handful of operations that we implement
-- as operations on rows, and then using efficient transposition, these operations
-- can be applied to columns.
module Algorithms.CL.SNFMatrix where
import Data.Array
import Control.Exception
-- | A data type to hold an /m/×/n/ integer matrix (/a/[sub /ij/]).
-- The fields are: /m/, the number of rows; /n/, the number of
-- columns; /a/, the matrix data, and /isTranspose/, a flag to
-- indicate whether the matrix should be transposed.
data SNFMatrix a = SNFMatrix Int Int (Array (Int, Int) a) Bool deriving (Show)
-- | Transpose a matrix.
transpose :: SNFMatrix a -> SNFMatrix a
transpose (SNFMatrix rows cols mtx trans) = SNFMatrix rows cols mtx (not trans)
-- | Get the number of rows in the matrix.
rows :: SNFMatrix a -> Int
rows (SNFMatrix rows cols mtx trans) = if trans then cols else rows
-- | Get the number of columns in the matrix.
cols :: SNFMatrix a -> Int
cols (SNFMatrix rows cols mtx trans) = if trans then rows else cols
-- | Get a list of row indices for a given matrix.
row_list :: SNFMatrix a -> [Int]
row_list m = [0..((rows m)-1)]
-- | Get a list of column indices for a given matrix.
col_list :: SNFMatrix a -> [Int]
col_list m = [0..((cols m)-1)]
-- | Get an index tuple into the matrix given row and column.
idx :: SNFMatrix a -> Int -> Int -> (Int, Int)
idx (SNFMatrix rows cols mtx trans) i j = if trans then (j,i) else (i,j)
-- | Get a matrix element.
mtx_elem :: (Show a) => SNFMatrix a -> Int -> Int -> a
mtx_elem m@(SNFMatrix rows cols mtx trans) i j =
if (ri >= rows || rj >= cols)
then throw (IndexOutOfBounds ("Bad index i=" ++ show i ++ ",j=" ++ show j ++ ",m=" ++ show m))
else mtx ! (ri,rj)
where (ri, rj) = idx m i j
-- | Multiply a row of the matrix by a multiplier.
mulrow :: (Show a, Num a) => SNFMatrix a -> Int -> a -> SNFMatrix a
mulrow m@(SNFMatrix rows cols mtx trans) row mul =
SNFMatrix rows cols mtx' trans
where
new_elem i j = (mtx_elem m i j) * mul
mtx' = mtx // [ ((idx m row j), new_elem row j) | j <- col_list m ]
-- | Swap two rows of a matrix.
swaprows :: (Show a) => SNFMatrix a -> Int -> Int -> SNFMatrix a
swaprows m@(SNFMatrix rows cols mtx trans) row1 row2 =
SNFMatrix rows cols mtx'' trans
where
mtx' = mtx // [ ((idx m row1 j), mtx_elem m row2 j) | j <- col_list m ]
mtx'' = mtx' // [ ((idx m row2 j), mtx_elem m row1 j) | j <- col_list m ]
-- | Add a multiple of a row to another row.
addmulrow :: (Show a, Num a) => SNFMatrix a -> Int -> Int -> a -> SNFMatrix a
addmulrow m@(SNFMatrix rows cols mtx trans) rowtgt rowsrc mul =
SNFMatrix rows cols mtx' trans
where
new_elem j = (mtx_elem m rowsrc j) * mul + (mtx_elem m rowtgt j)
mtx' = mtx // [ ((idx m rowtgt j), new_elem j ) | j <- col_list m ]
-- | Print an 'SNFMatrix' for debugging.
printSNF :: (Show a) => SNFMatrix a -> [String]
printSNF m@(SNFMatrix rows cols mtx trans) =
["mtx <" ++ show rows ++ "," ++ show cols ++ ">" ] ++
[ "{" ++ show mtx ++ "}" ] ++
[ print_row m i | i <- [ 0 .. (rows-1) ] ]
where
print_row m i = concat [ " " ++ show (mtx ! (i,j)) | j <- [0 .. (cols-1)] ]
-- | Construct an 'SNFMatrix' from a list.
matrixFromList :: [[a]] -> SNFMatrix a
matrixFromList list@(x:xs) = SNFMatrix rows cols mtx trans
where
rows = length list
cols = length x -- Assume all inside-lists are of the same length
trans= False
mtx = array ((0, 0),(rows-1, cols-1)) $ read_data 0 list
read_data i [] = []
read_data i (line:lines) = (read_line i 0 line) ++ read_data (i+1) lines
read_line i j [] = []
read_line i j (x:xs) = ((i,j), x) : (read_line i (j+1) xs)
matrixFromList [] = error "matrixFromList: empty list"