{-# LANGUAGE FlexibleInstances #-}

module Data.Matrix.Class where

import Prelude ()
import Algebra.RingUtils
import Control.Applicative hiding ((<|>))


fingerprint m = [[ if isZero (at i j m) then ' ' else 'X' | i <- [0..x-1] ] | j <- [0..y-1]]
  where x = countColumns m
        y = countRows m

(f *** g) (x,y) = (f x,g y)

data Dimension
    = XD
    | YD
      deriving (Eq,Show)

quad a b c d = (a <|> b) <-> (c <|> d)


nextDim XD = YD
nextDim YD = XD

type Extent = (Int,Int)

ext XD (x,y) = x
ext YD (x,y) = y

glueExt XD (x1,y1) (x2,y2)  = (x1+x2,y1)
glueExt YD (x1,y1) (x2,y2)  = (x1,y1+y2)

splitExt XD k (x,y) = ((k,y),(x-k,y))
splitExt YD k (x,y) = ((x,k),(x,y-k))

class Matrix m where
  at :: AbelianGroupZ a => Int -> Int -> m a -> a
  extent :: m a -> Extent
  -- | Sigleton matrix
  singleton :: AbelianGroupZ a => a -> m a
  glue :: AbelianGroup a => Dimension -> m a -> m a -> m a
  split :: AbelianGroupZ a => Dimension -> Int -> m a -> (m a, m a)
  zeroMatrix :: AbelianGroup a => Int -> Int -> m a

instance Matrix m => Matrix (O Pair m) where
  at i j (O (x :/: y)) = at i j x + at i j y
  extent (O (x :/: y)) = extent x -- union with y
  glue d (O p) (O q) = O $ glue d <$> p <*> q
  split d k (O (x :/: y)) = (O $ ax :/: ay, O $ bx :/: by)
     where (ax,bx) = split d k x
           (ay,by) = split d k y
  zeroMatrix x y = O $ pure (zeroMatrix x y)
  singleton x = O $ pure (singleton x) -- Attention: on both sides always!


(<|>) :: (AbelianGroup a, Matrix m) => m a -> m a -> m a
(<|>) = glue XD

(<->) :: (AbelianGroup a, Matrix m)  => m a -> m a -> m a
(<->) = glue YD

countColumns, countRows :: Matrix m => m a -> Int
countColumns = ext XD . extent
countRows = ext YD . extent

chopLastColumn, chopFirstRow, chopFirstColumn, chopLastRow, lastColumn, firstRow :: (AbelianGroupZ a, Matrix m) => m a -> m a
chopFirstRow = snd . split YD 1
chopFirstColumn = snd . split XD 1
chopLastColumn x = fst . split XD (countColumns x - 1) $ x
firstRow = fst . split YD 1
lastColumn x = snd . split XD (countColumns x - 1) $ x

chopLastRow x = fst . split YD (countRows x - 1) $ x