{-# LANGUAGE NoMonomorphismRestriction, DeriveFunctor #-}
module Math.Combinatorics.Matroid where
import Math.Core.Utils
import Math.Core.Field hiding (f7)
import Math.Common.ListSet as LS 
import Math.Algebra.LinearAlgebra hiding (rank)
import qualified Math.Combinatorics.Graph as G 
import Math.Combinatorics.FiniteGeometry
import Math.Combinatorics.GraphAuts
import Math.Algebra.Group.PermutationGroup hiding (closure)
import Math.Algebras.VectorSpace hiding (dual)
import Math.Algebras.Structures
import Math.Algebras.Commutative
import Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
implies p q = q || not p
exists = not . null
unique [x] = x
shortlex xs ys = case compare (length xs) (length ys) of
    LT -> LT
    EQ -> compare xs ys
    GT -> GT
isShortlex xs = foldcmpl (\x1 x2 -> shortlex x1 x2 /= GT) xs
toShortlex xs = map snd $ L.sort [(length x, x) | x <- xs]
isClutter ss = and [ (s1 `LS.isSubset` s2) `implies` (s1 == s2) | s1 <- ss, s2 <- ss ]
deletions xs = zipWith (++) (inits xs) (tail $ tails xs)
closedUnderSubsets xss = and [xs' `S.member` xss' | xs <- xss, xs' <- deletions xs]
    where xss' = S.fromList xss
data TrieSet a = TS [(a, TrieSet a)] deriving (Eq,Ord,Functor)
tsshow (TS xts) = "TS [" ++ concatMap (\(x,t) -> "(" ++ show x ++ "," ++ tsshow t ++ ")") xts ++ "]"
instance Show a => Show (TrieSet a) where
    show = show . tstolist
tsempty = TS []
tsinsert (x:xs) (TS ts) =
    case L.lookup x ts of
    Nothing -> let t = tsinsert xs (TS [])
               in TS $ L.insert (x,t) ts
    Just t -> let t' = tsinsert xs t
              in TS $ L.insert (x,t') $ L.delete (x,t) ts
tsinsert [] t = t
tsmember (x:xs) (TS ts) =
    case lookup x ts of
    Nothing -> False
    Just t -> tsmember xs t
tsmember [] (TS []) = True 
tsmember [] _ = False
tssubmember (x:xs) (TS ts) = or [ case compare x y of
                                  LT -> False
                                  EQ -> tssubmember xs t
                                  GT -> tssubmember (x:xs) t
                                | (y,t) <- ts ]
tssubmember [] _ = True
tstolist (TS []) = [[]]
tstolist (TS xts) = concatMap (\(x,t) -> map (x:) (tstolist t)) xts
tsfromlist = foldl' (flip tsinsert) tsempty
data Matroid a = M [a] (TrieSet a) deriving (Eq,Show,Functor)
elements :: Matroid t -> [t]
elements (M es bs) = es
indeps :: (Ord a) => Matroid a -> [[a]]
indeps m = bfs [ ([],es) ]
    where es = elements m
          bfs ( (ls,rs) : nodes ) =
              let ls' = reverse ls in
              if isIndependent m ls'
              then ls' : bfs ( nodes ++ successors (ls,rs) )
              else bfs nodes
          bfs [] = []
          successors (ls,rs) = [ (r:ls, rs') | r:rs' <- L.tails rs ]
isIndependent :: (Ord a) => Matroid a -> [a] -> Bool
isIndependent (M es bs) xs = xs `tssubmember` bs
isDependent :: (Ord a) => Matroid a -> [a] -> Bool
isDependent m = not . isIndependent m
isMatroidIndeps :: (Ord a) => [[a]] -> Bool
isMatroidIndeps is =
    [] `elem` is &&
    closedUnderSubsets is &&
    and [ (l1 < l2) `implies` exists [e | e <- i2 LS.\\ i1, L.insert e i1 `elem` is]
        | i1 <- is, let l1 = length i1, i2 <- is, let l2 = length i2 ]
fromIndeps :: (Ord a) => [a] -> [[a]] -> Matroid a
fromIndeps es is = fromBases es bs
    where bs = dfs [] [([],es)]
          dfs bs ( node@(ls,rs) : nodes ) =
              let succs = successors node
              in if null succs then dfs (ls:bs) nodes else dfs bs (succs ++ nodes)
          dfs ls [] = let r = length $ last ls 
                      in map reverse $ filter (\b -> length b == r) ls
                      
          successors (ls,rs) = [ (r:ls, rs') | r:rs' <- L.tails rs, (r:ls) `S.member` is' ]
          is' = S.fromList $ map reverse is
fromIndeps1 es is = fromBases es bs
    where b = greedy [] es 
          greedy ls (r:rs) = if (r:ls) `S.member` ris'
                             then greedy (r:ls) rs
                             else greedy ls rs
          greedy ls [] = reverse ls
          ris' = S.fromList $ map reverse is
          bs = closure S.empty (S.singleton b) 
          closure interior boundary =
              if S.null boundary
              then S.toList interior
              else let interior' = interior `S.union` boundary
                       boundary' = S.fromList [ b' | b <- S.toList boundary,
                                                     x <- b, y <- es LS.\\ b,
                                                     let b' = L.insert y (L.delete x b),
                                                     b' `S.notMember` interior',
                                                     b' `S.member` is' ]
                   in closure interior' boundary'
          is' = S.fromList is
vectorMatroid :: (Eq k, Fractional k) => [[k]] -> Matroid Int
vectorMatroid = vectorMatroid' . L.transpose
vectorMatroid' :: (Eq k, Fractional k) => [[k]] -> Matroid Int
vectorMatroid' vs = fromBases (map fst vs') bs
    where vs' = zip [1..] vs
          bs = dfs [] [([],[],vs')]
          dfs ls (r@(i,ref,es) : rs) =
              let succs = successors r in
              if null succs then dfs (i:ls) (succs ++ rs) else dfs ls (succs ++ rs)
          dfs ls [] = let r = length $ last ls 
                      in map reverse $ filter (\b -> length b == r) ls
                      
          successors (i,ref,es) = [(i',ref',es') | (j,e):es' <- L.tails es,
                                                   not (inSpanRE ref e),
                                                   let ref' = rowEchelonForm (ref ++ [e]), 
                                                   let i' = j : i ]
cycleMatroid :: (Ord a) => [[a]] -> Matroid Int
cycleMatroid es = fromBases (map fst es') bs
    where es' = zip [1..] es
          bs = dfs [] [([], M.empty, es')]
          dfs ls (r@(i,ref,es) : rs) =
              let succs = successors r
              in if null succs then dfs (i:ls) (succs ++ rs) else dfs ls (succs ++ rs)
          dfs ls [] = let r = length $ last ls 
                      in map reverse $ filter (\b -> length b == r) ls
                      
          successors (i, reps, (j,[u,v]):es' ) =
              if u == v
              then successors (i, reps, es')
              else case (M.lookup u reps, M.lookup v reps) of
                   (Nothing, Nothing) -> (j:i, M.insert u u $ M.insert v u reps, es') : successors (i, reps, es')
                                         
                   (Just u', Nothing) -> (j:i, M.insert v u' reps, es') : successors (i, reps, es')
                                         
                   (Nothing, Just v') -> (j:i, M.insert u v' reps, es') : successors (i, reps, es')
                                         
                   (Just u', Just v') -> if u' == v'
                                         then successors (i,reps,es')
                                              
                                         else (j:i, M.map (\w -> if w == v' then u' else w) reps, es') : successors (i, reps, es')
                                              
          successors (_, _, []) = []
cycleMatroid' es = fmap lookupEdge $ cycleMatroid es
    where table = M.fromList $ zip [1..] es
          lookupEdge = (M.!) table
to1n :: (Ord a) => Matroid a -> Matroid Int
to1n m = fmap to1n' m
    where es = elements m
          table = M.fromList $ zip es [1..]
          to1n' = (M.!) table
incidenceGraphB m = G.G vs' es'
    where es = elements m
          bs = bases m
          vs' = map Left es ++ map Right bs
          es' = L.sort [ [Left e, Right b] | b <- bs, e <- b ]
incidenceGraphC m = G.G vs' es'
    where es = elements m
          cs = L.sort $ circuits m
          vs' = map Left es ++ map Right cs
          es' = L.sort [ [Left e, Right c] | c <- cs, e <- c ]
incidenceGraphH m = G.G vs' es'
    where es = elements m
          hs = L.sort $ hyperplanes m
          vs' = map Left es ++ map Right hs
          es' = L.sort [ [Left e, Right h] | h <- hs, e <- h ]
matroidIsos m1 m2 = incidenceIsos (incidenceGraphH m1) (incidenceGraphH m2)
isMatroidIso :: (Ord a, Ord b) => Matroid a -> Matroid b -> Bool
isMatroidIso m1 m2 = isIncidenceIso (incidenceGraphH m1) (incidenceGraphH m2)
matroidAuts :: (Ord a) => Matroid a -> [Permutation a]
matroidAuts m = incidenceAuts $ incidenceGraphH m
isCircuit :: (Ord a) => Matroid a -> [a] -> Bool
isCircuit m c =
    isDependent m c &&
    all (isIndependent m) (deletions c)
circuits :: (Ord a) => Matroid a -> [[a]]
circuits m = toShortlex $ dfs S.empty [L.insert e b | b <- bs, e <- es LS.\\ b]
    where es = elements m
          bs = bases m
          dfs vs (c:cs) | c `S.member` vs = dfs vs cs
                        | otherwise = let cs' = successors c
                                          vs' = S.insert c vs
                                      in if null cs' then c : dfs vs' cs else dfs vs' (cs' ++ cs)
          dfs _ [] = []
          successors c = [c' | c' <- deletions c, isDependent m c' ]
isMatroidCircuits :: (Ord a) => [[a]] -> Bool
isMatroidCircuits cs =
    [] `notElem` cs &&
    and [(c1 `LS.isSubset` c2) `implies` (c1 == c2) | c1 <- cs, c2 <- cs] &&
    and [ exists [c3 | c3 <- cs, c3 `LS.isSubset` c12']
        | c1 <- cs, c2 <- cs, c1 /= c2,
          e <- c1 `LS.intersect` c2, let c12' = L.delete e (c1 `LS.union` c2)]
fromCircuits :: (Ord a) => [a] -> [[a]] -> Matroid a
fromCircuits es cs = fromBases es bs
    where b = greedy [] es 
          greedy ls (r:rs) = let ls' = ls ++ [r] in
                             if isIndep ls'
                             then greedy ls' rs
                             else greedy ls rs
          greedy ls [] = ls
          bs = closure S.empty (S.singleton b) 
          closure interior boundary =
              if S.null boundary
              then S.toList interior
              else let interior' = interior `S.union` boundary
                       boundary' = S.fromList [ b' | b <- S.toList boundary,
                                                     x <- b, y <- es LS.\\ b,
                                                     let b' = L.insert y (L.delete x b),
                                                     b' `S.notMember` interior',
                                                     isIndep b' ]
                   in closure interior' boundary'
          isIndep xs = not (any (`LS.isSubset` xs) cs)
isLoop :: (Ord a) => Matroid a -> a -> Bool
isLoop m e = isCircuit m [e]
isParallel :: (Ord a) => Matroid a -> a -> a -> Bool
isParallel m f g = isCircuit m [f,g]
isSimple :: (Ord a) => Matroid a -> Bool
isSimple m = all ( (>2) . length ) (circuits m)
isBase :: (Ord a) => Matroid a -> [a] -> Bool
isBase (M es bs) b = b `tsmember` bs
bases :: (Ord a) => Matroid a -> [[a]]
bases (M es bs) = tstolist bs
isMatroidBases :: (Ord a) => [[a]] -> Bool
isMatroidBases bs =
    (not . null) bs &&
    and [ exists [y | y <- b2 LS.\\ b1, L.insert y (L.delete x b1) `elem` bs]
        | b1 <- bs, b2 <- bs, x <- b1 LS.\\ b2 ]
fromBases :: (Ord a) => [a] -> [[a]] -> Matroid a
fromBases es bs = M es (tsfromlist bs)
fundamentalCircuit :: (Ord a) => Matroid a -> [a] -> a -> [a]
fundamentalCircuit m b e = unique [c | c <- circuits m, c `LS.isSubset` be]
    where be = L.insert e b
uniformMatroid m n | m <= n = fromBases es bs
    where es = [1..n]
          bs = combinationsOf m es
u :: Int -> Int -> Matroid Int
u = uniformMatroid
restriction1 m xs = fromBases xs bs'
    where bs = bases m
          is' = toShortlex [b `LS.intersect` xs | b <- bs]
          r = length $ last is'
          bs' = dropWhile ( (< r) . length ) is'
restriction :: (Ord a) => Matroid a -> [a] -> Matroid a
restriction m@(M es bs) xs = M xs bs'
    where (_,bs') = balance $ prune bs
          prune (TS yts) = let (ins, outs) = L.partition (\(y,t) -> y `elem` xs) yts
                               ins' = [(y, prune t) | (y,t) <- ins]
                               outs' = concat [ zts | (y,t) <- outs, let TS zts = prune t ]
                           in TS $ ins' ++ outs'
          balance (TS yts) = let dyt's = [(d',(y,t')) | (y,t) <- yts, let (d',t') = balance t]
                                 d = maximum $ 0 : map fst dyt's
                             in (d+1, TS $ toSet [(y,t') | (d',(y,t')) <- dyt's, d' == d])
rankfun :: (Ord a) => Matroid a -> [a] -> Int
rankfun m xs = (length . head . bases) (restriction m xs)
rank :: (Ord a) => Matroid a -> Int
rank m = length $ head $ bases m
fromRankfun :: (Ord a) => [a] -> ([a] -> Int) -> Matroid a
fromRankfun es rkf = fromBases es bs
    where b = greedy 0 [] es 
          greedy rk ls (r:rs) = let ls' = ls ++ [r] in
                                if rkf ls' == rk+1
                                then greedy (rk+1) ls' rs
                                else greedy rk ls rs
          greedy _ ls [] = ls
          rk = rkf b
          isBasis b' = rkf b' == rk
          bs = closure S.empty (S.singleton b) S.empty 
          closure interior boundary exterior =
              if S.null boundary
              then S.toList interior
              else let interior' = interior `S.union` boundary
                       candidates = S.fromList [ b' | b <- S.toList boundary,
                                                      x <- b, y <- es LS.\\ b,
                                                      let b' = L.insert y (L.delete x b),
                                                      b' `S.notMember` interior',
                                                      b' `S.notMember` exterior ]
                       (boundary', exterior') = S.partition isBasis candidates
                   in closure interior' boundary' (S.union exterior exterior')
closure :: (Ord a) => Matroid a -> [a] -> [a]
closure m xs = [x | x <- es, x `elem` xs || rankfun m (L.insert x xs) == rankxs]
    where es = elements m
          rankxs = rankfun m xs
fromClosure :: (Ord a) => [a] -> ([a] -> [a]) -> Matroid a
fromClosure es cl = fromBases es bs
    where b = greedy (cl []) [] es 
          greedy span ls (r:rs) = let ls' = ls ++ [r] in
                                  if r `notElem` span 
                                  then greedy (cl ls') ls' rs
                                  else greedy span ls rs
          greedy _ ls [] = ls
          rk = length b
          isBasis b' = cl b' == es
          bs = closure S.empty (S.singleton b) S.empty 
          closure interior boundary exterior =
              if S.null boundary
              then S.toList interior
              else let interior' = interior `S.union` boundary
                       candidates = S.fromList [ b' | b <- S.toList boundary,
                                                      x <- b, y <- es LS.\\ b,
                                                      let b' = L.insert y (L.delete x b),
                                                      b' `S.notMember` interior',
                                                      b' `S.notMember` exterior ]
                       (boundary', exterior') = S.partition isBasis candidates
                   in closure interior' boundary' (S.union exterior exterior')
isFlat :: (Ord a) => Matroid a -> [a] -> Bool
isFlat m xs = closure m xs == xs
flats1 m = [xs | xs <- powersetbfs es, isFlat m xs]
    where es = elements m
coveringFlats m xs = coveringFlats' (es LS.\\ xs)
    where es = elements m
          coveringFlats' (y:ys) = let zs = closure m (L.insert y xs)
                                  in zs : coveringFlats' (ys LS.\\ zs)
          coveringFlats' [] = []
minimalFlat m = head $ filter (isFlat m) $ powersetbfs $ elements m
flats :: (Ord a) => Matroid a -> [[a]]
flats m = flats' S.empty [minimalFlat m]
    where flats' ls (r:rs) = if r `S.member` ls
                             then flats' ls rs
                             else flats' (S.insert r ls) (rs ++ coveringFlats m r)
          flats' ls [] = toShortlex $ S.toList ls
          
fromFlats :: (Ord a) => [[a]] -> Matroid a
fromFlats fs | isShortlex fs = fromFlats' fs
             | otherwise = error "fromFlats: flats must be in shortlex order"
fromFlats' fs = fromClosure es cl
    where es = last fs 
          cl xs = head [f | f <- fs, xs `LS.isSubset` f] 
isSpanning :: (Ord a) => Matroid a -> [a] -> Bool
isSpanning m xs = closure m xs == es
    where es = elements m
isHyperplane :: (Ord a) => Matroid a -> [a] -> Bool
isHyperplane m xs = isFlat m xs && rankfun m xs == rank m - 1
hyperplanes1 m = [h | h <- flats m, rankfun m h == rk - 1]
    where rk = rank m
hyperplanes :: (Ord a) => Matroid a -> [[a]]
hyperplanes m = toShortlex $ map complement $ cocircuits m
    where es = elements m
          complement cc = es LS.\\ cc
isMatroidHyperplanes :: (Ord a) => [a] -> [[a]] -> Bool
isMatroidHyperplanes es hs =
    es `notElem` hs &&
    isClutter hs &&
    and [ exists [h3 | h3 <- hs, h12e `LS.isSubset` h3] | (h1,h2) <- pairs hs,
                                                          e <- es LS.\\ (LS.union h1 h2),
                                                          let h12e = L.insert e (LS.intersect h1 h2) ]
fromHyperplanes1 es hs = fromFlats $ closure S.empty (S.fromList hs)
    where closure interior boundary =
              if S.null boundary
              then (toShortlex $ S.toList interior) ++ [es]
              else let interior' = S.union interior boundary
                       boundary' = S.fromList [ f1 `LS.intersect` f2 | (f1,f2) <- pairs (S.toList boundary) ]
                                   S.\\ interior'
                   in closure interior' boundary'
fromHyperplanes :: (Ord a) => [a] -> [[a]] -> Matroid a
fromHyperplanes es hs = fromCocircuits es $ map complement hs
    where fromCocircuits es = dual . fromCircuits es
          complement xs = es LS.\\ xs
affineMatroid :: (Eq k, Fractional k) => [[k]] -> Matroid Int
affineMatroid vs = vectorMatroid' $ map (1:) vs
fromGeoRep :: (Ord a) => [[a]] -> [[a]] -> [[a]] -> [[a]] -> Matroid a
fromGeoRep loops points lines planes =
    fromCircuits es $ minimal $
        loops ++
        concatMap (combinationsOf 2) points ++
        concatMap (combinationsOf 3) lines ++
        concatMap (combinationsOf 4) planes ++
        combinationsOf 5 es
    where es = toSet $ concat loops ++ concat points ++ concat lines ++ concat planes
minimal xss = minimal' [] xss
    where minimal' ls (r:rs) = if any (`LS.isSubset` r) ls
                               then minimal' ls rs
                               else minimal' (r:ls) rs
          minimal' ls [] = reverse ls
simpleFromGeoRep :: (Ord a) => [[a]] -> [[a]] -> Matroid a
simpleFromGeoRep lines planes = fromGeoRep [] []  lines planes
isSimpleGeoRep lines planes =
    all ( (<= 1) . length ) [ l1 `LS.intersect` l2 | (l1,l2) <- pairs lines ] &&
    all ( \i -> length i <= 2 || i `elem` lines ) [ p1 `LS.intersect` p2 | (p1,p2) <- pairs planes ] &&
    and [ any (u `LS.isSubset`) planes | (l1,l2) <- pairs lines, length (l1 `LS.intersect` l2) == 1, let u = l1 `LS.union` l2 ] &&
    and [ length i == 1 || i == l | l <- lines, p <- planes, let i = l `LS.intersect` p ]
isCircuitHyperplane m xs = isCircuit m xs && isHyperplane m xs
circuitHyperplanes :: (Ord a) => Matroid a -> [[a]]
circuitHyperplanes m = [ h | h <- hyperplanes m, isCircuit m h ]
relaxation :: (Ord a) => Matroid a -> [a] -> Matroid a
relaxation m b
    | isCircuitHyperplane m b = fromBases es bs
    | otherwise = error "relaxation: not a circuit-hyperplane"
    where es = elements m
          bs = b : bases m
ex161 = [ [1,2,6], [3,4,5,6], [2,3], [2,4,6] ]
transversalGraph as = [(Left x, Right i) | (a,i) <- zip as [1..], x <- a]
partialMatchings es = dfs [(S.empty, [], es)]
    where dfs (node@(vs,ls,rs): nodes) = ls : dfs (successors node ++ nodes)
          dfs [] = []
          successors (vs,ls,rs) =  [ (S.insert u $ S.insert v vs, L.insert r ls, rs')
                                   | r@(u,v):rs' <- L.tails rs, u `S.notMember` vs, v `S.notMember` vs ]
transversalMatroid :: (Ord a) => [a] -> [[a]] -> Matroid a
transversalMatroid es as = fromBases es bs
    where is@(i:_) = reverse $ toShortlex $ toSet $ (map . map) (unLeft . fst) $ partialMatchings (transversalGraph as)
          unLeft (Left x) = x
          l = length i
          bs = reverse $ takeWhile ( (== l) . length ) is
dual :: (Ord a) => Matroid a -> Matroid a
dual m = fromBases es bs'
    where es = elements m
          bs = bases m
          bs' = L.sort $ map (es LS.\\) bs
isCoindependent m xs = isIndependent (dual m) xs
isCobase m xs = isBase (dual m) xs
isCocircuit m xs = isCircuit (dual m) xs
cocircuits :: (Ord a) => Matroid a -> [[a]]
cocircuits m = circuits (dual m)
isColoop m e = isLoop (dual m) e
isCoparallel m f g = isParallel (dual m) f g
deletion :: (Ord a) => Matroid a -> [a] -> Matroid a
deletion m xs = restriction m (es LS.\\ xs)
    where es = elements m
(\\\) = deletion
contraction :: (Ord a) => Matroid a -> [a] -> Matroid a
contraction m xs = dual (deletion (dual m) xs)
(///) = contraction
isConnected :: (Ord a) => Matroid a -> Bool
isConnected m = and [any (pair `LS.isSubset`) cs | pair <- combinationsOf 2 es]
    where es = elements m
          cs = circuits m
component m x = closure S.empty (S.singleton x)
    where cs = circuits m
          closure interior boundary =
              if S.null boundary
              then S.toList interior
              else let interior' = S.union interior boundary
                       boundary' = S.fromList (concat [c | c <- cs, (not . null) (LS.intersect c (S.toList boundary)) ])
                                   S.\\ interior'
                   in closure interior' boundary'
dsum :: (Ord a, Ord b) => Matroid a -> Matroid b -> Matroid (Either a b)
dsum m1 m2 = fromBases es bs
    where es = map Left (elements m1) ++ map Right (elements m2)
          bs = [map Left b1 ++ map Right b2 | b1 <- bases m1, b2 <- bases m2]
matroidPG :: (Eq a, Fractional a) => Int -> [a] -> Matroid Int
matroidPG n fq = vectorMatroid' $ ptsPG n fq
matroidAG :: (Eq a, Fractional a) => Int -> [a] -> Matroid Int
matroidAG n fq = vectorMatroid' $ ptsAG n fq
fundamentalCircuitIncidenceMatrix :: (Ord a, Num k) => Matroid a -> [a] -> [[k]]
fundamentalCircuitIncidenceMatrix m b = L.transpose $ fundamentalCircuitIncidenceMatrix' m b
fundamentalCircuitIncidenceMatrix' m b =
    [ [if e `elem` fundamentalCircuit m b e' then 1 else 0 | e <- b]
    | e' <- elements m LS.\\ b ]
fcim = fundamentalCircuitIncidenceMatrix
fcim' = fundamentalCircuitIncidenceMatrix'
markNonInitialRCs mx = mark (replicate w False) mx
    where w = length $ head mx 
          mark cms (r:rs) = let (cms', r') = mark' False [] cms [] r in r' : mark cms' rs
          mark _ [] = []
          mark' rm cms' (cm:cms) ys (x:xs)
              | x == 0 = mark' rm (cm:cms') cms (Zero:ys) xs
              | x == 1 = if rm && cm
                         then mark' True (True:cms') cms (Star:ys) xs
                         else mark' True (True:cms') cms (One:ys) xs
          mark' _ cms' [] ys [] = (reverse cms', reverse ys)
substStars mx fq = substStars' mx
    where fq' = tail fq 
          substStars' (r:rs) = [r':rs' | r' <- substStars'' r, rs' <- substStars' rs]
          substStars' [] = [[]]
          substStars'' (Zero:xs) = map (0:) $ substStars'' xs
          substStars'' (One:xs) = map (1:) $ substStars'' xs
          substStars'' (Star:xs) = [x':xs' | x' <- fq', xs' <- substStars'' xs]
          substStars'' [] = [[]]
starSubstitutionsV fq' (Zero:xs) = map (0:) $ starSubstitutionsV fq' xs
starSubstitutionsV fq' (One:xs) = map (1:) $ starSubstitutionsV fq' xs
starSubstitutionsV fq' (Star:xs) = [x':xs' | x' <- fq', xs' <- starSubstitutionsV fq' xs]
starSubstitutionsV _ [] = [[]]
representations1 fq m = [ L.transpose d | d <- substStars dhash fq, let mx = ir ++ d,
                                          to1n m == (vectorMatroid' $ map snd $ L.sort $ zip (b ++ b') mx) ]
    where b = head $ bases m
          b' = elements m LS.\\ b
          r = length b 
          ir = idMx r 
          dhash = markNonInitialRCs $ fcim' m b
fcig m b = [ [e,e'] | e <- b, e' <- elements m LS.\\ b, e `elem` fundamentalCircuit m b e' ]
markedfcim m b = mark b b' (fcim m b)
    where b' = elements m LS.\\ b 
          entries = fcig m b 
          ones = head $ bases $ cycleMatroid' entries 
          stars = entries LS.\\ ones 
          mark (i:is) js (r:rs) = (mark' i js r) : mark is js rs
          mark [] _ [] = []
          mark' i (j:js) (x:xs)
              | x == 0 = Zero : mark' i js xs
              | x == 1 = (if [i,j] `elem` stars then Star else One) : mark' i js xs
          mark' _ [] [] = []
representations2 fq m = [ L.transpose mx | d <- substStars dhash' fq, let mx = ir ++ d,
                                           m' == (vectorMatroid' $ map snd $ L.sort $ zip (b ++ b') mx) ]
    where m' = to1n m
          es = elements m
          b = head $ bases m
          b' = es LS.\\ b
          r = length b 
          ir = idMx r 
          dhash' = L.transpose $ markedfcim m b
representations :: (Eq fq, Fractional fq, Ord a) => [fq] -> Matroid a -> [[[fq]]]
representations fq m = map L.transpose $ representations' (reverse $ zip b ir) (zip b' dhash')
    where fq' = tail fq 
          b = head $ bases m
          b' = elements m LS.\\ b
          r = length b 
          ir = idMx r 
          dhash' = L.transpose $ markedfcim m b
          representations' ls ((i,r):rs) = concat
              [ representations' ((i,r'):ls) rs
              | r' <- starSubstitutionsV fq' r,
                let (is,vs) = unzip $ L.sortBy cmpfst ((i,r'):ls),
                to1n (restriction m is) == (vectorMatroid' vs) ]
          representations' ls [] = [map snd $ reverse ls]
isRepresentable :: (Eq fq, Fractional fq, Ord a) => [fq] -> Matroid a -> Bool
isRepresentable fq m = (not . null) (representations fq m)
isBinary :: (Ord a) => Matroid a -> Bool
isBinary = isRepresentable f2
isTernary :: (Ord a) => Matroid a -> Bool
isTernary = isRepresentable f3
data LMR a b = L a | Mid | R b deriving (Eq, Ord, Show)
seriesConnection (m1,p1) (m2,p2)
    | not (isLoop m1 p1) && not (isColoop m1 p1) && not (isLoop m2 p2) && not (isColoop m2 p2) =
        fromCircuits es cs
    | otherwise = error "not yet implemented"
        where es = map L (elements m1 LS.\\ [p1]) ++ [Mid] ++ map R (elements m2 LS.\\ [p2])
              cs = (map . map) L (circuits $ m1 \\\ [p1]) ++
                   (map . map) R (circuits $ m2 \\\ [p2]) ++
                   [ map L (L.delete p1 c1) ++ [Mid] ++ map R (L.delete p2 c2)
                   | c1 <- circuits m1, p1 `elem` c1, c2 <- circuits m2, p2 `elem` c2]
parallelConnection (m1,p1) (m2,p2)
    | not (isLoop m1 p1) && not (isColoop m1 p1) && not (isLoop m2 p2) && not (isColoop m2 p2) =
        fromCircuits es cs
    | otherwise = error "not yet implemented"
        where es = map L (elements m1 LS.\\ [p1]) ++ [Mid] ++ map R (elements m2 LS.\\ [p2])
              cs = (map . map) L (circuits $ m1 \\\ [p1]) ++
                   [ map L (L.delete p1 c1) ++ [Mid] | c1 <- circuits m1, p1 `elem` c1 ] ++
                   (map . map) R (circuits $ m2 \\\ [p2]) ++
                   [ [Mid] ++ map R (L.delete p2 c2) | c2 <- circuits m2, p2 `elem` c2 ] ++
                   [ map L (L.delete p1 c1) ++ map R (L.delete p2 c2)
                   | c1 <- circuits m1, p1 `elem` c1, c2 <- circuits m2, p2 `elem` c2 ]
twoSum (m1,p1) (m2,p2)
    | not (isLoop m1 p1) && not (isColoop m1 p1) && not (isLoop m2 p2) && not (isColoop m2 p2) =
        fromCircuits es cs
    | otherwise = error "not yet implemented"
        where es = map L (elements m1 LS.\\ [p1]) ++ [Mid] ++ map R (elements m2 LS.\\ [p2])
              cs = (map . map) L (circuits $ m1 \\\ [p1]) ++
                   (map . map) R (circuits $ m2 \\\ [p2]) ++
                   [ map L (L.delete p1 c1) ++ map R (L.delete p2 c2)
                   | c1 <- circuits m1, p1 `elem` c1, c2 <- circuits m2, p2 `elem` c2]
matroidUnion m1 m2 = fromBases es bs
    where es = LS.union (elements m1) (elements m2)
          is = toShortlex $ toSet [ LS.union b1 b2 | b1 <- bases m1, b2 <- bases m2 ]
          r = length $ last is
          bs = dropWhile ( (< r) . length ) is
f7 :: Matroid Int
f7 = fromGeoRep [] [] [[1,2,3],[1,4,7],[1,5,6],[2,4,6],[2,5,7],[3,4,5],[3,6,7]] [[1..7]]
f7m :: Matroid Int
f7m = relaxation f7 [2,4,6]
pappus :: Matroid Int
pappus = fromGeoRep [] [] [[1,2,3],[1,5,7],[1,6,8],[2,4,7],[2,6,9],[3,4,8],[3,5,9],[4,5,6],[7,8,9]] [[1..9]]
nonPappus :: Matroid Int
nonPappus = relaxation pappus [7,8,9]
desargues :: Matroid Int
desargues = fromGeoRep [] [] [[1,2,5],[1,3,6],[1,4,7],[2,3,8],[2,4,9],[3,4,10],[5,6,8],[5,7,9],[6,7,10],[8,9,10]]
                              [[1,2,3,5,6,8],[1,2,4,5,7,9],[1,3,4,6,7,10],[2,3,4,8,9,10],[5,6,7,8,9,10]]
vamosMatroid1 = fromHyperplanes [1..8] (hs4 ++ hs3)
    where hs4 = [ [1,2,3,4], [1,4,5,6], [2,3,5,6], [1,4,7,8], [2,3,7,8] ]
          hs3 = [ h3 | h3 <- combinationsOf 3 [1..8], null [h4 | h4 <- hs4, h3 `LS.isSubset` h4] ]
vamosMatroid = fromGeoRep [] [] [] [[1,2,3,4],[1,4,5,6],[2,3,5,6],[1,4,7,8],[2,3,7,8]]
v8 :: Matroid Int
v8 = vamosMatroid
p8 :: Matroid Int
p8 = vectorMatroid $
    ( [ [1,0,0,0,  0, 1, 1,-1],
        [0,1,0,0,  1, 0, 1, 1],
        [0,0,1,0,  1, 1, 0, 1],
        [0,0,0,1, -1, 1, 1, 0] ] :: [[F3]] )
p8' = fromGeoRep [] [] [] [ [1,2,3,8], [1,2,4,7], [1,3,4,6], [1,4,5,8], [1,5,6,7], [2,3,4,5], [2,3,6,7], [2,5,6,8], [3,5,7,8], [4,6,7,8] ]
p8m :: Matroid Int
p8m = relaxation p8 [2,3,6,7]
p8mm :: Matroid Int
p8mm = relaxation p8m [1,4,5,8]
wheelGraph r = G.G vs es
    where vs = [0..r]
          es = [ [0,i] | i <- [1..r] ] ++ [ [i,i+1] | i <- [1..r-1] ] ++ [ [1,r] ]
mw4 = cycleMatroid $ G.edges $ wheelGraph 4
w4' = relaxation mw4 $ unique $ circuitHyperplanes mw4 
w4 = fromGeoRep [] [] [[1,2,5],[1,4,8],[2,3,6],[3,4,7]] [[1,2,3,5,6],[1,2,4,5,8],[1,3,4,7,8],[2,3,4,6,7]]
isBinary2 m = all (even . length) [ c `LS.intersect` cc | c <- circuits m, cc <- cocircuits m ]
[x,y] = map glexVar ["x","y"] :: [GlexPoly Integer String]
rankPoly1 m = sum [ x^(rm - r a) * y^(rm' - r' a') | a <- powersetdfs es, let a' = es LS.\\ a ]
    where es = elements m
          rm = rank m
          r = rankfun m
          m' = dual m
          rm' = rank m'
          r' = rankfun m'
rankPoly :: (Ord a) => Matroid a -> GlexPoly Integer String
rankPoly m
    | null es = 1
    | isLoop m e = (1+y) * rankPoly (m \\\ [e]) 
    | isColoop m e = (1+x) * rankPoly (m /// [e]) 
    | otherwise = rankPoly (m \\\ [e]) + rankPoly (m /// [e])
    where es = elements m
          e = head es
numBases m = unwrap $ rankPoly m `bind` (\v -> case v of "x" -> 0; "y" -> 0)
numIndeps m = unwrap $ rankPoly m `bind` (\v -> case v of "x" -> 1; "y" -> 0)
numSpanning m = unwrap $ rankPoly m `bind` (\v -> case v of "x" -> 0; "y" -> 1)
indepCounts m = map length $ L.groupBy eqfst $ [(length i, i) | i <- indeps m]
whitney2nd m = map length $ L.groupBy eqfst $ L.sort [(rankfun m f, f) | f <- flats m]
whitney1st m = alternatingSign $ map length $ L.groupBy eqfst $ L.sort [(rankfun m x, x) | x <- powersetdfs (elements m)]
    where alternatingSign (x:xs) = x : alternatingSign (map negate xs)
          alternatingSign [] = []