diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2015, atzedijkstra
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of delimiter-separated nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# delimiter-separated
+Haskell library for dealing with tab and/or comma (or other) separated files
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,11 @@
+import Distribution.Simple
+main = defaultMain
+
+{-
+import Distribution.Simple (defaultMainWithHooks)
+import Distribution.Simple.UUAGC (uuagcLibUserHook)
+import UU.UUAGC (uuagc)
+
+main :: IO ()
+main = defaultMainWithHooks (uuagcLibUserHook uuagc)
+-}
diff --git a/delimiter-separated.cabal b/delimiter-separated.cabal
new file mode 100644
--- /dev/null
+++ b/delimiter-separated.cabal
@@ -0,0 +1,30 @@
+Name:                delimiter-separated
+Version:             0.1.0.0
+Copyright:           Utrecht University, Department of Information and Computing Sciences, Software Technology group
+Description:         Delimeter separated file handling
+Synopsis:            Library for dealing with tab and/or comma (or other) separated files
+Homepage:            https://github.com/atzedijkstra/delimiter-separated
+Bug-Reports:         https://github.com/atzedijkstra/delimiter-separated/issues
+License:             BSD3
+License-file:        LICENSE
+Author:              atze@uu.nl
+Maintainer:          atze@uu.nl
+Category:            Development
+Build-Type:          Simple
+Cabal-Version:       >= 1.8
+Extra-Source-Files:  
+data-files:          LICENSE, README.md, test/mk, test/test.hs, test/data.csv, test/data.tsv
+
+Source-Repository head
+  Type:              git
+  Location:          https://github.com/atzedijkstra/delimiter-separated
+
+Library
+  Hs-Source-Dirs:    src
+  Extensions:        RankNTypes, FlexibleContexts, TypeSynonymInstances, FlexibleInstances
+  Exposed-Modules:   Text.DelimiterSeparated
+  Other-Modules:     
+  Build-Depends:     base >= 4 && < 5,
+                     uhc-util >= 0.1.1.0,
+                     uulib >= 0.9
+
diff --git a/src/Text/DelimiterSeparated.hs b/src/Text/DelimiterSeparated.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/DelimiterSeparated.hs
@@ -0,0 +1,404 @@
+-------------------------------------------------------------------------------------------
+-- Input/output of delimiter separated strings
+-------------------------------------------------------------------------------------------
+
+{-|
+Module      : Text.DelimiterSeparated
+Description : Library for manipulating delimiter separated records.
+Copyright   : (c) Atze Dijkstra, 2015
+License     : BSD3
+Maintainer  : atze@uu.nl
+Stability   : experimental
+Portability : POSIX
+
+The library provides parsing/unparsing of 'Records' as well as interpreting those records to a datatype of your choice,
+via 'toRecords' and 'fromRecords' using class 'DelimSepRecord', where each individual field can be interpreted via class 'DelimSepField'.
+
+The following example demonstrates the basic parsing/unparsing:
+
+> module Main where
+> 
+> import Text.DelimiterSeparated
+> import System.IO
+> import Control.Monad
+> 
+> main = do
+>   txt <- readFile "data.csv"
+>   putStrLn txt
+>   case recordsFromDelimiterSeparated csv txt of
+>     Left es -> forM_ es putStrLn
+>     Right recs -> do
+>       putStrLn $ show recs
+>       writeFile "data-out-csv.csv" $ recordsToDelimiterSeparated csv recs
+>       writeFile "data-out-csv.tsv" $ recordsToDelimiterSeparated tsv recs
+> 
+>   txt <- readFile "data.tsv"
+>   -- putStrLn txt
+>   case recordsFromDelimiterSeparated tsv txt of
+>     Left es -> forM_ es putStrLn
+>     Right recs -> do
+>       putStrLn $ show recs
+>       writeFile "data-out-tsv.csv" $ recordsToDelimiterSeparated csv recs
+
+-}
+
+module Text.DelimiterSeparated
+  ( -- * Types
+    Field(..)
+  , Record
+  , Records
+  , DelimiterStyle(..)
+  , csv, tsv
+  
+  -- * Construction
+  , emptyField
+  
+  -- * Encoding, decoding
+  , recordsToDelimiterSeparated
+  , recordsFromDelimiterSeparated
+  
+  -- * Checks, fixes
+  , Check(..)
+  , checkAll
+  , recordsCheck
+  
+  , Fix(..)
+  , recordsFix
+  
+  -- * Construction
+  , recordsFromHeaderStr
+  , recordsAddHeaderStr
+  
+  -- * Manipulation
+  , recordsPartitionRows
+  , recordsPartitionColsBasedOnHeader
+  , recordsSpan
+  , recordsSplitHeader
+  
+  -- * Conversion
+  , recordsToStrings
+  , recordsFromStrings
+  
+  -- * Overloaded conversion
+  , DelimSepField(..)
+  , DelimSepRecord(..)
+  
+  , toRecords
+  , toRecordsWithHeader
+  , toRecordsWithHeaderStr
+  
+  , fromRecords
+  )
+  where
+
+-------------------------------------------------------------------------------------------
+import UU.Parsing
+import UU.Parsing.CharParser
+import UHC.Util.ParseUtils
+import UHC.Util.Utils
+import Data.List
+-------------------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------------------
+-- Types
+-------------------------------------------------------------------------------------------
+
+-- | Field
+data Field
+  = Field
+      { fldStr      :: String       -- base case
+      }
+  deriving (Eq)
+
+-- | Empty field
+emptyField :: Field
+emptyField = Field ""
+
+instance Show Field where
+  show = showField tsv
+
+-- | Show field, depending on delimiter style
+showField :: DelimiterStyle -> Field -> String
+showField (CSV {}) (Field s) = "\"" ++ s ++ "\""
+showField (TSV {}) (Field s) =         s
+
+-- | Record is sequence of fields (representation may change in future)
+type Record = [Field]
+
+-- | Records is sequence of records (representation may change in future)
+type Records = [Record]
+
+-- | Style of delimitation
+data DelimiterStyle
+  = CSV
+      { styleFieldDelimChars        :: [Char]       -- field delimiters, first one used when unparsing
+      , styleRecordDelimChars       :: [Char]       -- record delimiters, first one used when unparsing
+      }
+  | TSV
+
+-- | Predefined delimiter style for comma field separated, newline/return record separated
+csv :: DelimiterStyle
+csv = CSV "," "\n\r"
+
+-- | Predefined delimiter style for tab field separated, newline/return record separated
+tsv :: DelimiterStyle
+tsv = TSV
+
+-------------------------------------------------------------------------------------------
+-- Parsing, encoding, decoding
+-------------------------------------------------------------------------------------------
+
+type P p = PlainParser Char p
+
+-- | Parsing of records, given a delimiterstyle
+pRecords :: DelimiterStyle -> P Records
+pRecords style
+  = concat <$> pList1Sep_ng pNL pRecordsNonEmpty -- <* (pNL `opt` '?')
+  where pField = case style of
+          TSV -> Field <$> pList_ng (pExcept (minBound, maxBound, '?') "\t\n\r")
+          CSV {styleFieldDelimChars=fs, styleRecordDelimChars=ls} ->
+              Field <$>
+                (   pDQ *> pList_ng pQChar <* pDQ
+                <|> pList_ng pChar
+                )
+            where pQChar
+                    =   pExcept (minBound, maxBound, '?') "\""
+                    <|> pDQ <* pDQ
+                  pChar
+                    =   pExcept (minBound, maxBound, '?') ("\"" ++ fs ++ ls)
+        pRecord = pList1Sep_ng pSep pField
+        pRecordsNonEmpty = chk <$> pRecord
+          where chk []         = []
+                chk [Field ""] = []
+                chk fs         = [fs]
+
+        pNL = case style of
+          TSV -> pAnySym "\n\r"
+          CSV {styleRecordDelimChars=ls} -> pAnySym ls
+        
+        pDQ = pSym '"'
+        pSep = case style of
+          TSV -> pAnySym "\t"
+          CSV {styleFieldDelimChars=fs} -> pAnySym fs
+
+-- | Encode internal representation in external delimiter separated format
+recordsToDelimiterSeparated :: DelimiterStyle -> Records -> String
+recordsToDelimiterSeparated style recs
+  = (mk ls $ map (mk fs . map (showField style)) recs) ++ "\n"
+  where (fs,ls) = case style of
+          TSV -> ('\t', '\n')
+          CSV {styleFieldDelimChars=(fs:_), styleRecordDelimChars=(ls:_)} -> (fs,ls)
+        mk sep = concat . intersperse [sep]
+
+-- | Decode internal representation from external delimiter separated format, possible failing with error messages
+recordsFromDelimiterSeparated :: DelimiterStyle -> String -> Either [String] Records
+recordsFromDelimiterSeparated style str
+  | null errs = Right res
+  | otherwise = Left $ map show errs
+  where (res,errs) = parseToResMsgs (pRecords style) str
+
+-------------------------------------------------------------------------------------------
+-- Construction
+-------------------------------------------------------------------------------------------
+
+-- | Convert a String representation of a header to actual record
+recordsFromHeaderStr :: String -> Records
+recordsFromHeaderStr s = recordsFromStrings [words s]
+
+-- | Add a header described by string holding whitespaced separated labels
+recordsAddHeaderStr :: String -> Records -> Records
+recordsAddHeaderStr s = (recordsFromHeaderStr s ++)
+
+-------------------------------------------------------------------------------------------
+-- Manipulation
+-------------------------------------------------------------------------------------------
+
+-- | Lift a predicate to a Record
+liftRecPred :: ([String] -> Bool) -> (Record -> Bool)
+liftRecPred pred = pred . map fldStr
+
+-- | Do something with records, taking into account header
+recordsDoHeader
+  :: Bool                           -- ^ first rec is header?
+  -> (Records -> Records -> res)    -- ^ do it, given header (if any) and records
+  -> Records
+  -> res
+recordsDoHeader fstIsHdr mk recs
+  | fstIsHdr  = mk [hd] tl
+  | otherwise = mk []   recs
+  where (hd:tl) = recs
+
+-- | Partition record rows.
+-- Fst of tuple holds the possible header, if indicated it is present.
+-- Snd of tuple holds records failing the predicate.
+-- Assumes >0 records
+recordsPartitionRows :: Bool -> ([String] -> Bool) -> Records -> (Records, Records)
+recordsPartitionRows fstIsHdr pred recs
+  = recordsDoHeader fstIsHdr mk recs
+  where mk hd rs = (hd++y,n)
+          where (y,n) = partition (liftRecPred pred) rs
+
+-- | Partition record columns, fst of tuple holds the obligatory header upon wich partitioning is done
+-- Assumes header and records all have same nr of fields
+recordsPartitionColsBasedOnHeader :: (String -> Bool) -> Records -> (Records, Records)
+recordsPartitionColsBasedOnHeader pred hr@(hdr:_)
+  = let spl = split hdr in unzip $ map spl hr
+  where split (h:t) | pred (fldStr h) = let tspl = split t in \(rh:rt) -> let (r1,r2) = tspl rt in (rh:r1,    r2)
+                    | otherwise       = let tspl = split t in \(rh:rt) -> let (r1,r2) = tspl rt in (   r1, rh:r2)
+        split []                      =                       \[]      ->                          (   [],    [])
+
+-- | Partition records, fst of tuple holds the possible header, if indicated it is present.
+-- Assumes >0 records
+recordsSpan :: Bool -> ([String] -> Bool) -> Records -> (Records, Records)
+recordsSpan fstIsHdr pred recs
+  = recordsDoHeader fstIsHdr mk recs
+  where mk hd rs = (hd++y,n)
+          where (y,n) = span (liftRecPred pred) rs
+
+-- | Split of header, assuming there is one
+recordsSplitHeader :: Records -> (Record, Records)
+recordsSplitHeader (h:t) = (h, t)
+
+
+-------------------------------------------------------------------------------------------
+-- Conversion
+-------------------------------------------------------------------------------------------
+
+-- | Get all fields as strings
+recordsToStrings :: Records -> [[String]]
+recordsToStrings = map (map fldStr)
+
+-- | Lift strings as Records
+recordsFromStrings :: [[String]] -> Records
+recordsFromStrings = map (map Field)
+
+-------------------------------------------------------------------------------------------
+-- Check(s) & fixes
+-------------------------------------------------------------------------------------------
+
+-- | Which checks are to be done by 'recordsCheck'
+data Check
+  = Check_DupHdrNms             -- ^ check for duplicate header names
+  | Check_EqualSizedRecs        -- ^ check for equal sized records (ignoring possible header)
+  | Check_AtLeast1Rec           -- ^ check for at least 1 record (ignoring possible header)
+  | Check_EqualSizedHdrRecs     -- ^ check for equal sized header and records
+  | Check_NoRecsLargerThanHdr   -- ^ check for records not larger than header
+  | Check_NoRecsSmallerThanHdr  -- ^ check for records not smaller than header
+  deriving (Eq,Enum,Bounded)
+
+-- | All checks
+checkAll :: [Check]
+checkAll = [minBound .. maxBound]
+
+-- | Check records, possibly yielding errors
+recordsCheck :: Bool -> [Check] -> Records -> Maybe String
+recordsCheck fstIsHdr chks recs
+  --- | null recs    = Just $ "no records nor header"
+
+  | (Check_AtLeast1Rec `elem` chks || checksHdrSizeAndRecs) && 
+    not (has1Rec recs)
+                 = Just $ "not at least 1 record" ++ (if fstIsHdr then " and header" else "")
+
+  | Check_EqualSizedRecs `elem` chks && 
+    length tlSzs > 1
+                 = Just $ "records have varying sizes: " ++ show tlSzs
+
+  | checksHdrSizeAndRecs && fstIsHdr &&
+    not (null cmp_tlSzs)
+                 = Just $ "header size=" ++ show hdLen ++ " and records sizes=" ++ show cmp_tlSzs ++ " differ"
+
+  | otherwise    = Nothing
+
+  where has1Rec (_:_:_) | fstIsHdr      = True
+        has1Rec (_:_  ) | fstIsHdr      = False
+                        | otherwise     = True
+        has1Rec _                       = False
+        ~(~[rhd],rtl)                   = recordsDoHeader fstIsHdr (,) recs
+        dupnms                          = concat $ map head $ filter (\l -> length l > 1) $ groupSortOn id $ map fldStr rhd
+        tlNrAndLen                      = zipWith (\i r -> (i, length r)) [1::Int ..] rtl
+        hdLen                           = length rhd
+        tlSzs@(~(hd_tlSzs@(~(hd_tlSz,_)):_))
+                                        = [ (l, map fst nl) | nl@((_,l):_) <- groupSortOn snd tlNrAndLen ]
+        checksGT                        = Check_NoRecsLargerThanHdr `elem` chks
+        checksLT                        = Check_NoRecsSmallerThanHdr `elem` chks
+        checksHdrSizeAndRecs            = checksGT || checksLT
+        cmpSz | checksGT && checksLT    = (/=)
+              | checksGT                = (>)
+              | checksLT                = (<)
+              | otherwise               = \_ _ -> False
+        cmp_tlSzs                       = filter (\(sz,_) -> cmpSz sz hdLen) tlSzs
+
+-- | Which fixes are to be done by 'recordsCheck'
+data Fix
+  = Fix_Pad                 -- ^ pad
+  | Fix_PadToHdrLen         -- ^ in combi with pad, pad to header len
+  deriving (Eq,Enum,Bounded)
+
+-- | Fix sizes of records by padding to max size
+recordsFix :: Bool -> [Fix] -> Records -> Records
+recordsFix fstIsHdr fxs recs
+  = map fix recs
+  where ~(~[rhd],rtl) = recordsDoHeader fstIsHdr (,) recs
+        maxl | Fix_PadToHdrLen `elem` fxs && fstIsHdr = length rhd
+             | otherwise                              = maximum $ map length rtl
+        fix  | Fix_Pad `elem` fxs = \r -> r ++ take (maxl - length r) p
+             | otherwise          = id
+             where p = repeat emptyField
+
+-------------------------------------------------------------------------------------------
+-- Additional conversion/interpretation of field, i.e. show/read (why not use it like that?)
+-------------------------------------------------------------------------------------------
+
+-- | Conversion to/from Field, i.e. kinda show/read
+class DelimSepField x where
+  toDelimSepField :: x -> Field
+  fromDelimSepField :: Field -> x
+
+instance DelimSepField Field where
+  toDelimSepField = id
+  fromDelimSepField = id
+
+instance {-# OVERLAPPABLE #-} DelimSepField x => DelimSepField [x] where
+  toDelimSepField = toDelimSepField . unwords . map (fromDelimSepField . toDelimSepField)
+  fromDelimSepField = map (fromDelimSepField . toDelimSepField) . words . fromDelimSepField
+
+instance DelimSepField String where
+  toDelimSepField = Field
+  fromDelimSepField = fldStr
+
+instance DelimSepField Integer where
+  toDelimSepField = Field . show
+  fromDelimSepField (Field x) = read x
+
+instance DelimSepField Int where
+  toDelimSepField = Field . show
+  fromDelimSepField (Field x) = read x
+
+instance DelimSepField Double where
+  toDelimSepField = Field . show
+  fromDelimSepField (Field x) = read x
+
+-- | Conversion to/from Record
+class DelimSepRecord x where
+  toDelimSepRecord :: x -> Record
+  fromDelimSepRecord :: Record -> x
+
+instance {-# OVERLAPPABLE #-} DelimSepRecord Record where
+  toDelimSepRecord = id
+  fromDelimSepRecord = id
+
+-- | Convert to records
+toRecords :: DelimSepRecord x => [x] -> Records
+toRecords = map toDelimSepRecord
+
+-- | Convert to records, with a header described by string holding whitespaced separated labels
+toRecordsWithHeader :: DelimSepRecord x => [Record] -> [x] -> Records
+toRecordsWithHeader h = (h++) . toRecords
+
+-- | Convert to records, with a header described by string holding whitespaced separated labels
+toRecordsWithHeaderStr :: DelimSepRecord x => String -> [x] -> Records
+toRecordsWithHeaderStr s = toRecordsWithHeader (recordsFromHeaderStr s)
+
+-- | Convert from records
+fromRecords :: DelimSepRecord x => Records -> [x]
+fromRecords = map fromDelimSepRecord
diff --git a/test/data.csv b/test/data.csv
new file mode 100644
--- /dev/null
+++ b/test/data.csv
@@ -0,0 +1,3 @@
+/Users/sk/Music/iTunes/iTunes%20Music/Unknown%20Artist/Unknown%20Album/bummbumm.wav.mp3,0.03200000151991844,0.19200000166893005,0.9995909654314382,1.1448329230877248,1.2714721054530917,1.2927777950383028,1.2258539469919074,1.1275260497327948,1.007627284617382,0.8307895437172607,0.6963983522352151,0.6038522898665477,0.678277532511434,0.7829649869777328,3.019877011746812,3.6721289260958424E-4,9438.807662644498,-2.447088900865536,4.100193553539682,-0.12943993495929387,-6.90922812653433,-13.69428137030968,-16.283890170623266,-17.687202641965037,-18.27143813252461,-18.798327477661285,-19.160884562437893,-17.029512009859257,-17.21379450993305,-15.913488567673545,-15.645628338658279,-15.72830189645247,-15.172482398670333,-16.751137186808432,-17.27609905292102,-18.129456660727165,-19.898483803478577,-19.28359581490902,-20.188461066548225,-22.167085506958017,-23.54305927156909,-21.682182052480297,-22.153204790166512,-24.50021501266152,-23.334023569377504,-23.447214006292278,-26.578179434691254,-26.078093008351296,-28.165330414236333,-28.425195787464286,-28.294948662424982,-29.356971966031182,-31.862794824902593,-31.840246726994543,-34.02344292400379,-36.438152658158785,-39.388729121022585,4.592778742826233,-0.31367607962311617,1.6169361392277137,0.7541350976164396,1.612616595297597,0.6450404351749938,0.634337005415671,0.022760598948171867,0.3218182829449279,-0.20349345012470121,0.07679334369738107,-0.10771340255616313,-0.12116588702235398,8.42302919684374,559.9434664045244,0.02197556215395824
+/Users/sk/Music/iTunes/iTunes%20Music/Unknown%20Artist/Unknown%20Album/bummbumm.wav.mp3,0.2240000069141388,0.17599999904632568,0.9746707219498666,1.1671316438307684,1.3217604253949975,1.3828134645667698,1.2989620627858258,1.1563957348822362,0.9634028904047387,0.7673071183828495,0.5922354279255881,0.47882394077814583,0.5900238647057049,0.7476595489716499,2.989648106174724,5.386914912802615E-4,-151.09917630417988,9.040418955722165,12.395741959638872,6.008055291943099,-6.839326872211888,-11.204253199081952,-13.119510468139424,-14.375215790385868,-16.576616220553976,-15.323830297118384,-13.77417497632024,-12.848404743424814,-13.952865456744265,-13.966724904298731,-14.555360175243893,-15.501099463044596,-16.009909151050763,-18.836763574779763,-18.19246489227611,-18.467274533418163,-19.540353998361542,-21.04046760741786,-21.188974332815135,-26.889314345456267,-29.90088232887947,-28.901550500650238,-29.686160668903067,-31.728787280339365,-33.32795663091535,-35.37592513485504,-37.10127227873265,-38.474977149586664,-40.63697155978034,-40.547451533398494,-40.68477781761101,-41.23272850385577,-42.82473910265789,-43.717349059141235,-46.502886211806384,-51.553083760413045,-60.26314334291782,9.177974659608198,-0.6244261856181933,1.6427387060867982,1.0651933986383453,1.8280220826563138,0.6031410422576562,1.365597990730021,0.34270934233824013,0.8851693746488324,-0.3176470132766348,0.3812310911561604,-0.07083169283783974,0.1360119733629103,0.0,189.7818034175284,1.1536715598368582E-4
+/Users/sk/Music/iTunes/iTunes%20Music/Unknown%20Artist/Unknown%20Album/bummbumm.wav.mp3,0.4000000059604645,0.17599999904632568,1.0778043854808024,1.0996704580888912,1.1487601586562055,1.1411909160115201,1.0963204656317105,1.078084115118316,1.0454150811852605,0.9502169045231982,0.8070326833748533,0.7079010249241344,0.794816496851268,0.9289501193855842,2.9116156361552967,3.05210818747569E-4,-151.36600053200883,4.666259800504124,9.971417036411308,4.7735836392492885,-7.096358064223474,-9.723187042441586,-11.597554162187746,-11.421930965342126,-10.568355253918915,-9.947068106146446,-10.895314766510497,-11.37826882145405,-10.221172256327332,-9.77027137935714,-7.9837351046751985,-9.555067817109219,-11.231954691121311,-11.795521621444255,-11.110885401005955,-13.44970387792948,-14.674790094399008,-15.660669966092824,-15.779094112887847,-20.911414567281177,-23.51697494799904,-24.335406373422074,-24.823170797285805,-28.26341142931034,-32.69296823091563,-35.25031522743438,-36.74496610944522,-38.471303942147834,-39.61459693086177,-40.85385502609932,-42.02048510162983,-43.446151263585286,-43.87175287534318,-46.32075547618171,-49.40872977361136,-53.05420616345488,-58.2618264908827,9.77390580532916,-2.391068841981746,1.2629106043314486,1.0243722740997256,1.439629736552944,0.39717447322803223,1.1286081918079884,0.08450535243464721,0.7164428107635228,0.0021199767634070693,0.33667722867924005,0.0369097979127717,0.09114738100836475,0.0,319.2953702272283,0.0011021323970666528
diff --git a/test/data.tsv b/test/data.tsv
new file mode 100644
--- /dev/null
+++ b/test/data.tsv
@@ -0,0 +1,3 @@
+/Users/sk/Music/iTunes/iTunes%20Music/Unknown%20Artist/Unknown%20Album/bummbumm.wav.mp3	0.03200000151991844	0.19200000166893005	0.9995909654314382	1.1448329230877248	1.2714721054530917	1.2927777950383028	1.2258539469919074	1.1275260497327948	1.007627284617382	0.8307895437172607	0.6963983522352151	0.6038522898665477	0.678277532511434	0.7829649869777328	3.019877011746812	3.6721289260958424E-4	9438.807662644498	-2.447088900865536	4.100193553539682	-0.12943993495929387	-6.90922812653433	-13.69428137030968	-16.283890170623266	-17.687202641965037	-18.27143813252461	-18.798327477661285	-19.160884562437893	-17.029512009859257	-17.21379450993305	-15.913488567673545	-15.645628338658279	-15.72830189645247	-15.172482398670333	-16.751137186808432	-17.27609905292102	-18.129456660727165	-19.898483803478577	-19.28359581490902	-20.188461066548225	-22.167085506958017	-23.54305927156909	-21.682182052480297	-22.153204790166512	-24.50021501266152	-23.334023569377504	-23.447214006292278	-26.578179434691254	-26.078093008351296	-28.165330414236333	-28.425195787464286	-28.294948662424982	-29.356971966031182	-31.862794824902593	-31.840246726994543	-34.02344292400379	-36.438152658158785	-39.388729121022585	4.592778742826233	-0.31367607962311617	1.6169361392277137	0.7541350976164396	1.612616595297597	0.6450404351749938	0.634337005415671	0.022760598948171867	0.3218182829449279	-0.20349345012470121	0.07679334369738107	-0.10771340255616313	-0.12116588702235398	8.42302919684374	559.9434664045244	0.02197556215395824
+/Users/sk/Music/iTunes/iTunes%20Music/Unknown%20Artist/Unknown%20Album/bummbumm.wav.mp3	0.2240000069141388	0.17599999904632568	0.9746707219498666	1.1671316438307684	1.3217604253949975	1.3828134645667698	1.2989620627858258	1.1563957348822362	0.9634028904047387	0.7673071183828495	0.5922354279255881	0.47882394077814583	0.5900238647057049	0.7476595489716499	2.989648106174724	5.386914912802615E-4	-151.09917630417988	9.040418955722165	12.395741959638872	6.008055291943099	-6.839326872211888	-11.204253199081952	-13.119510468139424	-14.375215790385868	-16.576616220553976	-15.323830297118384	-13.77417497632024	-12.848404743424814	-13.952865456744265	-13.966724904298731	-14.555360175243893	-15.501099463044596	-16.009909151050763	-18.836763574779763	-18.19246489227611	-18.467274533418163	-19.540353998361542	-21.04046760741786	-21.188974332815135	-26.889314345456267	-29.90088232887947	-28.901550500650238	-29.686160668903067	-31.728787280339365	-33.32795663091535	-35.37592513485504	-37.10127227873265	-38.474977149586664	-40.63697155978034	-40.547451533398494	-40.68477781761101	-41.23272850385577	-42.82473910265789	-43.717349059141235	-46.502886211806384	-51.553083760413045	-60.26314334291782	9.177974659608198	-0.6244261856181933	1.6427387060867982	1.0651933986383453	1.8280220826563138	0.6031410422576562	1.365597990730021	0.34270934233824013	0.8851693746488324	-0.3176470132766348	0.3812310911561604	-0.07083169283783974	0.1360119733629103	0.0	189.7818034175284	1.1536715598368582E-4
+/Users/sk/Music/iTunes/iTunes%20Music/Unknown%20Artist/Unknown%20Album/bummbumm.wav.mp3	0.4000000059604645	0.17599999904632568	1.0778043854808024	1.0996704580888912	1.1487601586562055	1.1411909160115201	1.0963204656317105	1.078084115118316	1.0454150811852605	0.9502169045231982	0.8070326833748533	0.7079010249241344	0.794816496851268	0.9289501193855842	2.9116156361552967	3.05210818747569E-4	-151.36600053200883	4.666259800504124	9.971417036411308	4.7735836392492885	-7.096358064223474	-9.723187042441586	-11.597554162187746	-11.421930965342126	-10.568355253918915	-9.947068106146446	-10.895314766510497	-11.37826882145405	-10.221172256327332	-9.77027137935714	-7.9837351046751985	-9.555067817109219	-11.231954691121311	-11.795521621444255	-11.110885401005955	-13.44970387792948	-14.674790094399008	-15.660669966092824	-15.779094112887847	-20.911414567281177	-23.51697494799904	-24.335406373422074	-24.823170797285805	-28.26341142931034	-32.69296823091563	-35.25031522743438	-36.74496610944522	-38.471303942147834	-39.61459693086177	-40.85385502609932	-42.02048510162983	-43.446151263585286	-43.87175287534318	-46.32075547618171	-49.40872977361136	-53.05420616345488	-58.2618264908827	9.77390580532916	-2.391068841981746	1.2629106043314486	1.0243722740997256	1.439629736552944	0.39717447322803223	1.1286081918079884	0.08450535243464721	0.7164428107635228	0.0021199767634070693	0.33667722867924005	0.0369097979127717	0.09114738100836475	0.0	319.2953702272283	0.0011021323970666528
diff --git a/test/mk b/test/mk
new file mode 100644
--- /dev/null
+++ b/test/mk
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+ghc -i../src --make test.hs -XRankNTypes -XFlexibleContexts -XTypeSynonymInstances -XFlexibleInstances
diff --git a/test/test.hs b/test/test.hs
new file mode 100644
--- /dev/null
+++ b/test/test.hs
@@ -0,0 +1,24 @@
+module Main where
+
+import Text.DelimiterSeparated
+import System.IO
+import Control.Monad
+
+main = do
+  txt <- readFile "data.csv"
+  putStrLn txt
+  case recordsFromDelimiterSeparated csv txt of
+    Left es -> forM_ es putStrLn
+    Right recs -> do
+      putStrLn $ show recs
+      writeFile "data-out-csv.csv" $ recordsToDelimiterSeparated csv recs
+      writeFile "data-out-csv.tsv" $ recordsToDelimiterSeparated tsv recs
+
+  txt <- readFile "data.tsv"
+  -- putStrLn txt
+  case recordsFromDelimiterSeparated tsv txt of
+    Left es -> forM_ es putStrLn
+    Right recs -> do
+      putStrLn $ show recs
+      writeFile "data-out-tsv.csv" $ recordsToDelimiterSeparated csv recs
+
