hgraph/src/Main.hs
Stefan Dresselhaus e6a81f3352 made createOutput more generic
with modular item and line-seperator
2013-11-29 19:09:19 +01:00

194 lines
7.8 KiB
Haskell

{-# LANGUAGE CPP #-}
{-# LANGUAGE TemplateHaskell #-}
-----------------------------------------------------------------------------
--
-- Module : Main
-- Copyright :
-- License : AllRightsReserved
--
-- Maintainer :
-- Stability :
-- Portability :
--
-- |
--
-----------------------------------------------------------------------------
module Main (
main
) where
import DCB
import Control.DeepSeq
import Control.Monad (unless)
import Control.Monad.Par.Scheds.Trace
import Control.Parallel.Strategies
import Data.Array.Repa as A hiding ((++))
import Data.Array.Repa.Repr.Unboxed
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Either (lefts, rights)
import qualified Data.List as L
import qualified Data.Stream as S
import qualified Data.Text as T
import Data.Text.Encoding
import Debug.Trace
import System.Environment
import System.Exit (exitFailure)
import Test.QuickCheck.All (quickCheckAll)
import Data.Functor.Identity
-- TODO: Give createGraph a presized Array and no dynamic [Int].
-- should be createGraph :: T.Text -> Either (Vector Int) T.Text
createGraph :: T.Text -> Either [Int] T.Text
createGraph input = createGraph' input (Left [])
where
createGraph' :: T.Text -> Either [Int] T.Text -> Either [Int] T.Text
createGraph' a r
| T.null a = r
| otherwise =
let next = (createGraph' (T.tail a) r) in -- flip cases for less function-calls?
case next of
Left xs ->
case T.head a of
'0' -> Left $ 0:xs
'1' -> Left $ 1:xs
_ -> Right $ T.append (T.pack "cannot parse ") a
Right errstr ->
Right errstr
createAttr :: T.Text -> Either [Double] T.Text
createAttr input = createAttr' (T.split (=='\t') input) (Left [])
where
createAttr' :: [T.Text] -> Either [Double] T.Text -> Either [Double] T.Text
createAttr' [] r = r
createAttr' (a:as) r =
let this = read (T.unpack a) :: Double in
(if isNaN this then
Right $ T.append (T.pack "cannot parse ") a
else
(let next = (createAttr' as r) in
case next of
Left rs -> Left (this : rs)
_ -> next))
emptyLine :: T.Text -> Bool
emptyLine a
| T.null a = True
| otherwise = False
emptyLog :: [T.Text] -> Bool
emptyLog [] = True
emptyLog a = False --emptyLine $ foldl True (&&) (map emptyLine a)
-- TODO: implement calculation
--doCalculation :: Matrix Int -> B.ByteString
doCalculation graph attr = createOutput attr
--default output with "," within items and "\n" within dimensions
createOutput :: (Unbox a, Show a) => Array U DIM2 a -> B.ByteString
createOutput a = _createOutput a "," "\n"
--output Array a with "itt" within items and "lt" within dimensions
_createOutput :: (Unbox a, Show a) => Array U DIM2 a -> String -> String -> B.ByteString
_createOutput a itt lt = B.concat $ L.map B.pack (_createOutput' (extent a) a itt lt)
_createOutput' :: (Unbox a, Show a) => DIM2 -> Array U DIM2 a -> String -> String -> [String]
_createOutput' shape@(Z :. si :. sj) a itt lt = [(_createOutput'' shape i 0 a itt) ++ lt | i <- [0..(si - 1)]]
_createOutput'' :: (Unbox a, Show a) => DIM2 -> Int -> Int -> Array U DIM2 a -> String -> String
_createOutput'' shape@(Z :. si :. sj) i j a itt
| sj-1 == j = show (a!(ix2 i j)) -- no "," for last one..
| otherwise = show (a!(ix2 i j)) ++ itt ++ (_createOutput'' shape i (j+1) a itt)
{-
T.intercalate (T.singleton ',') (L.map (T.pack . show) a)
createOutput' (a:as) = T.append
(T.append
(T.intercalate (T.singleton ',')
(L.map (T.pack . show) a))
(T.singleton '\n'))
(createOutput' as)
-}
getAttrLength :: Either [Double] T.Text -> Int
getAttrLength (Left a) = length a
getAttrLength (Right _) = 0
showHelp = undefined
infixl 1 +||
(+||) :: a -> Strategy a -> a
a +|| b = a `using` b
checkError :: T.Text -> IO ()
checkError a
| T.null a = return ()
| otherwise = do
B.putStr $ encodeUtf8 $ T.append (T.append (T.pack "Error detected:\n") a) (T.pack "\n\n")
exitFailure
exeMain = do
-- args <- getArgs
-- input <- case args of
-- ["--help"] -> showHelp
-- ["-h"] -> showHelp
-- [] -> error "Error: No filename or stdinput (-) given."
-- [adj, attr] -> Prelude.mapM B.readFile [adj, attr]
-- _ -> error "Wrong arguments given"
input <- Prelude.mapM B.readFile ["sampledata.adj","sampledata.adj.atr"]
-- read file and clean
adjMat <- return $ L.filter (not . emptyLine) (T.lines (decodeUtf8 (head input)))
attrMat <- return $ L.filter (not . emptyLine) (T.lines (decodeUtf8 ((head . L.tail) input)))
adjLines <- return $ length adjMat
attrLines <- return $ length attrMat
-- TODO: concat with foldl1' kills us later -> use presized/preallocated array so we
-- dont copy that much lateron. Best would be Matrix Int
-- unrefined_graph::[Either [Int] String] - [Int] is Adjacency-Line, String is parse-Error
unrefined_graph <- return $ (L.map (createGraph) adjMat)
-- +|| (parBuffer 100 rdeepseq) --run parallel, evaluate fully
unrefined_attr <- return $ (L.map (createAttr) attrMat)
-- +|| (parBuffer 100 rdeepseq) --run parallel, evaluate fully
attrNum <- return $ getAttrLength (head unrefined_attr)
putStrLn $ show (adjLines, attrLines, attrNum)
----- CHECK FOR ERRORS
-- print out any read-errors and abort
if adjLines /= attrLines then
checkError $ T.pack $ "Adjacency-Matrix size "++ show adjLines ++
" differs from Attribute-Matrix " ++ show attrLines ++
".\n"
else
return ()
checkError $ T.intercalate (T.singleton '\n') (rights unrefined_graph)
checkError $ T.intercalate (T.singleton '\n') (rights unrefined_attr)
putStrLn $ show (length (L.foldl1 (++) (lefts unrefined_graph)),length (L.foldl1 (++) (lefts unrefined_attr)))
----- EXTRACT MATRICES
graph <- return $ A.fromListUnboxed (Z :. adjLines :. adjLines) (L.foldl1' (++) (lefts unrefined_graph)) -- concatenated graph
attr <- return $ A.fromListUnboxed (Z :. attrLines :. attrNum) (L.foldl1' (++) (lefts unrefined_attr)) -- concatenated attr
----- CALCULATE
output <- return $ doCalculation graph attr
B.putStr output
-- Entry point for unit tests.
testMain = do
allPass <- $quickCheckAll -- Run QuickCheck on all prop_ functions
unless allPass exitFailure
-- This is a clunky, but portable, way to use the same Main module file
-- for both an application and for unit tests.
-- MAIN_FUNCTION is preprocessor macro set to exeMain or testMain.
-- That way we can use the same file for both an application and for tests.
#ifndef MAIN_FUNCTION
#define MAIN_FUNCTION exeMain
#endif
main = MAIN_FUNCTION