packages feed

ampersand-3.0.3: src/Database/Design/Ampersand/Basics/Auxiliaries.hs

{-# LANGUAGE ScopedTypeVariables #-}
module Database.Design.Ampersand.Basics.Auxiliaries 
         ( module Database.Design.Ampersand.Basics.Auxiliaries
         , module Debug.Trace) where

import Data.List
import Data.Graph (stronglyConnComp, SCC(CyclicSCC))
import Data.Maybe (fromMaybe)
import Data.Map (Map) 
import qualified Data.Map as Map 
import Data.Set (Set)
import qualified Data.Set as Set 
import Debug.Trace

-- | The 'eqClass' function takes an equality test function and a list and returns a list of lists such
-- that each sublist in the result contains only equal elements, and all equal elements are in
-- the same sublist.  For example,
--
-- Example> eqClass "Mississippi" = ["M","iiii","ssss","pp"]
--
eqClass :: (a -> a -> Bool) -> [a] -> [[a]]
eqClass _ [] = []
eqClass f (x:xs) = (x:[e |e<-xs, f x e]) : eqClass f [e |e<-xs, not (f x e)]

-- | eqCl is used for gathering things that are equal wrt some criterion f.
--   For instance, if you want to have persons with the same name:
--    'eqCl name persons' produces a list,in which each element is a list of persons with the same name.
-- Example> eqCl (=='s') "Mississippi" = "ssss"

eqCl :: Eq b => (a -> b) -> [a] -> [[a]]
eqCl _ [] = []
eqCl f (x:xs) = (x:[e |e<-xs, f x==f e]) : eqCl f [e |e<-xs, f x/=f e]

-- | getCycles returns a list of cycles in the edges list (each edge is a pair of a from-vertex
--   and a list of to-vertices)
getCycles :: Eq a => [(a, [a])] -> [[a]]
getCycles edges =
  let allVertices = nub . concat $ [ from : to | (from, to) <- edges ]
      keyFor v = fromMaybe (error "FATAL") $ elemIndex v allVertices
      graphEdges = [ (v, keyFor v , map keyFor vs)  | (v, vs) <- edges ]
  in  [ vs | CyclicSCC vs <- stronglyConnComp graphEdges ]


-- |  Warshall's transitive closure algorithm
transClosureMap :: (Eq a, Ord a) => Map a [a] -> Map a [a]
transClosureMap xs
  = foldl f xs (Map.keys xs `intersect` nub (concat (Map.elems xs)))
    where
     f :: (Eq a, Ord a) => Map a [a] -> a -> Map a [a]   -- The type is given for documentation purposes only
     f q x = Map.unionWith union q (Map.fromListWith union [(a, q Map.! x) | (a, bs) <- Map.assocs q, x `elem` bs])

-- The following function can be used to determine how much of a set of alternative expression is already determined
-- | The 'combinations' function returns all possible combinations of lists of list.
-- For example,
--
-- > combinations [[1,2,3],[10,20],[4]] == [[1,10,4],[1,20,4],[2,10,4],[2,20,4],[3,10,4],[3,20,4]]
combinations :: [[a]] -> [[a]]
combinations []       = [[]]
combinations (es:ess) = [ x:xs | x<-es, xs<-combinations ess]

-- Convert list of a's with associated b's to a list of b's with associated a's.
-- Each b in the result is unique, and so is each a per b, eg.: 
-- converse [(1,[2,2,3]),(1,[3,4]),(2,[4,5])]  == [(2,[1]),(3,[1]),(4,[1,2]),(5,[2])]
converse :: forall a b . (Ord a, Ord b) => [(a, [b])] -> [(b, [a])]
converse aBss = let asPerB ::(Ord a, Ord b) =>  Map b (Set a)
                    asPerB = foldl (.) id [ Map.insertWith Set.union b (Set.singleton a)  | (a,bs) <- aBss, b <- bs ] $ Map.empty
                in Map.toList $ fmap Set.toList asPerB -- first convert each Set to a list, and then the whole Map to a list of tuples

commaEng :: String -> [String] -> String
commaEng str [a,b,c] = a++", "++b++", "++str++" "++c
commaEng str [a,b]   = a++" "++str++" "++b
commaEng _   [a]     = a
commaEng str (a:as)  = a++", "++commaEng str as
commaEng _   []      = ""

commaNL :: String -> [String] -> String
commaNL str [a,b]  = a++" "++str++" "++b
commaNL  _  [a]    = a
commaNL str (a:as) = a++", "++commaNL str as
commaNL  _  []     = ""

fst3 :: (a,b,c)->a
snd3 :: (a,b,c)->b
thd3 :: (a,b,c)->c
fst3 (a,_,_) = a
snd3 (_,b,_) = b
thd3 (_,_,c) = c

class Flippable a where
  flp :: a -> a


-- Trace shorthands

showTrace :: Show a => a -> a
showTrace a = traceShowId a

showTraceTag :: Show a => String -> a -> a
showTraceTag tag x = trace (tag ++ ": " ++ show x) x


-- Code formatting utils

blockParenthesize :: String -> String -> String -> [[String]] -> [String]
blockParenthesize open close sep liness =
  case liness of
    [] -> [open ++ close]
    _  -> concat [ zipWith (++) (pre:repeat "  ") linez
                 | (pre, linez) <- zip ((open++" "): repeat (sep++" ")) liness ] ++ [close]
-- [["line"], ["line1", "line2", "line3"],["linea", "lineb"] ->
-- ( line
-- , line1
--   line2
--   line3
-- , linea
--   lineb
-- )

addToLastLine :: String -> [String] -> [String]
addToLastLine str [] = [str]
addToLastLine str liness = init liness ++ [last liness ++ str]

indent :: Int -> [String] -> [String]
indent n liness = [ replicate n ' ' ++ line | line <- liness ]