diff --git a/TODO b/TODO
new file mode 100644
--- /dev/null
+++ b/TODO
@@ -0,0 +1,13 @@
+* DONE replace error call in mkMod with parser error
+   (Not needed)
+* DONE create preprocessor for concat split long lines & remove comments
+* DONE refactoring of packages, create Types, Parser and Proc
+* TODO update parseDNStr to parse all kind of DN notations
+* TODO error handling, modify add: <attrX> and than <attrY>:<value>,
+         need to be asserted that <attrX> and <attrY> are the same.
+* TODO Implement the base64 parsing
+* TODO Implement modifyDN parsing
+* TODO update serializers back to LDIF for base64 & all DN notations
+* TODO add module tests for versioned LDIFs
+* TODO add module tests for base64 parsing
+
diff --git a/ldif.cabal b/ldif.cabal
--- a/ldif.cabal
+++ b/ldif.cabal
@@ -1,28 +1,42 @@
 Name:            ldif
-Version:         0.0.1
+Version:         0.0.2
 License:         BSD3
 License-File:    LICENSE
 Synopsis:        The LDAP Data Interchange Format (LDIF) parser 
 Author:          Radoslav Dorcik <radoslav.dorcik@gmail.com>
 Maintainer:      radoslav.dorcik@gmail.com
 Description:     
-   LDIF files parser implementation using Parsec and based
-   on RFC 2849 - The LDAP Data Interchange Format (LDIF).
-
+ LDIF files parser implementation using Parsec and based
+ on RFC 2849 - The LDAP Data Interchange Format (LDIF).
+ .
+ Current implementation is unfinished and need to be enhanced
+ for base64 encoded values and various DN escaping. 
+ .
+ It includes following tool:
+ .
+ - diffLDIF command generates change LDIF between two 
+   content LDIF files. 
+ .
 Category:        Text
 Stability:       experimental
 Build-Type:      Simple
 Cabal-Version:   >= 1.6
 Extra-Source-Files:
+    run_tests.sh
+    TODO
     doc/rfc2253.txt
     doc/rfc2849.txt
     tests/TestMain.hs
-    tests/data/OK_simple03.modify.ldif
-    tests/data/OK_simple01.modify.ldif
-    tests/data/OK_simple02.content.ldif
+    tests/data/OK_diff01.content.ldif
+    tests/data/OK_diff02.content.ldif
     tests/data/OK_multivalue.modify.ldif
     tests/data/OK_simple01.content.ldif
-
+    tests/data/OK_simple01.modify.ldif
+    tests/data/OK_simple02.content.ldif
+    tests/data/OK_simple03.modify.ldif
+    tests/data/OK_simpleComment.modify.ldif
+    tests/data/OK_simpleWrap.modify.ldif
+    
 Source-Repository head
   type:     darcs
   location: http://rampa.sk/repo/ldif
@@ -44,6 +58,14 @@
 
   Exposed-modules:
         Text.LDIF
+        Text.LDIF.Types
+        Text.LDIF.Parser
+        Text.LDIF.Printer
+        Text.LDIF.Proc
+
+Executable diffLDIF
+  Hs-Source-Dirs:  src
+  Main-Is:         diffLDIF.hs
 
 Executable test
   Hs-Source-Dirs:  src, tests
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100644
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+cabal clean && cabal configure -ftest && cabal build && cd tests && ../dist/build/test/test
diff --git a/src/Text/LDIF.hs b/src/Text/LDIF.hs
--- a/src/Text/LDIF.hs
+++ b/src/Text/LDIF.hs
@@ -1,213 +1,14 @@
 module Text.LDIF (
-	parseLDIFStr,
-	parseLDIFFile,
- 	LDIF(..),   
-        Record(..),
-        Change(..),
-        Modify(..), 
-        DN, Attribute, Value, AttrValue
+  module Text.LDIF.Types,
+  module Text.LDIF.Parser,
+  module Text.LDIF.Printer,
+  module Text.LDIF.Proc
 )
 where
-import Text.ParserCombinators.Parsec
-import Data.Either
-import Data.Char
 
-type Attribute = String
-type Value = String
-type AttrValue = (Attribute, Value)
-type DN = String
-
--- | Represents LDIF structure, it can be either simply LDIF data dump or
--- | changes LDIF with LDAP operations 
-data LDIF = LDIFContent { lcVersion :: Maybe String, lcEntries :: [Record] }
-          | LDIFChanges { lcVersion :: Maybe String, lcEntries :: [Record] } deriving Show
-
--- | Represents one record or entry within LDIF file with DN and content
-data Record = AttrValRecord { recDN :: DN, recAttrVals :: [AttrValue] }  
-	    | ChangeRecord  { recDN :: DN, recOp :: Change } deriving Show
-
--- | Represents one LDAP operation within changes LDIF
-data Change = ChangeAdd     { chAttrVals :: [AttrValue] }
-            | ChangeDelete 
-            | ChangeModify  { chMods :: [Modify] }
-            | ChangeModDN  deriving Show
-
--- | Represents ChangeModify operations upon one entry within given DN
-data Modify = ModAdd     { modAttr :: Attribute, modAttrVals :: [AttrValue] }
-            | ModDelete  { modAttr :: Attribute, modAttrVals :: [AttrValue] }
-            | ModReplace { modAttr :: Attribute, modAttrVals :: [AttrValue] } deriving Show
-
--- | Parse string as LDIF content and return LDIF or ParseError
-parseLDIFStr :: String -> Either ParseError LDIF
-parseLDIFStr = parse pLdif "(param)" 
-
--- | Read and parse provided file and return LDIF or ParseError
-parseLDIFFile :: String -> IO (Either ParseError LDIF)
-parseLDIFFile name = do
-	input <- readFile name
-	return $ parse pLdif name input
-
--- | Parsec ldif parser
-pLdif :: CharParser st LDIF
-pLdif = try pLdifChanges <|> pLdifContent
-
-pLdifChanges :: CharParser st LDIF
-pLdifChanges = do
-    ver <- optionMaybe pVersionSpec
-    recs <- sepEndBy1 pChangeRec pSEPs
-    return $ LDIFChanges ver recs
-
-pLdifContent :: CharParser st LDIF
-pLdifContent = do
-    ver <- optionMaybe pVersionSpec
-    recs <- sepEndBy1 pAttrValRec pSEPs
-    return $ LDIFContent ver recs
-
-pAttrValRec ::  CharParser st Record
-pAttrValRec = do
-    dn <- pDNSpec
-    pSEP
-    attrVals <- sepEndBy1 pAttrValSpec pSEP
-    return $ AttrValRecord dn attrVals
-
-pChangeRec :: CharParser st Record
-pChangeRec = try pChangeAdd
-         <|> try pChangeDel
-         <|> try pChangeMod
-         <|> pChangeModDN
-
-pChangeAdd :: CharParser st Record
-pChangeAdd = do
-    dn <- pDNSpec
-    pSEP
-    string "changetype:"
-    pFILL
-    string "add"
-    pSEP
-    vals <- sepEndBy1 pAttrValSpec pSEP
-    return $ ChangeRecord dn (ChangeAdd vals)
-
-pChangeDel :: CharParser st Record
-pChangeDel = do
-    dn <- pDNSpec
-    pSEP
-    string "changetype:"
-    pFILL
-    string "delete"
-    pSEP
-    return $ ChangeRecord dn ChangeDelete
-
-pChangeMod :: CharParser st Record
-pChangeMod = do
-    dn <- pDNSpec
-    pSEP
-    string "changetype:"
-    pFILL
-    string "modify"
-    pSEP
-    mods <- sepEndBy1 pModSpec (char '-' >> pSEP)
-    return $ ChangeRecord dn (ChangeModify mods)
-
-pChangeModDN :: CharParser st Record
-pChangeModDN = do
-    dn <- pDNSpec
-    pSEP
-    string "changetype:"
-    pFILL
-    string "modrdn" 
-    pSEP
-    string "newrdn:"
-    pFILL 
-    pRDN
-    pSEP
-    string "deleteoldrdn:"
-    pFILL
-    oneOf "01"
-    pSEP
-    return $ ChangeRecord dn ChangeModDN
-
-pRDN :: CharParser st String
-pRDN = pSafeString
-
-pDNSpec :: CharParser st DN
-pDNSpec = do
-    string "dn:"
-    pFILL
-    pSafeString
-
-pVersionSpec :: CharParser st String
-pVersionSpec = do
-   string "version:"
-   pFILL
-   many1 digit
-
-pModSpec :: CharParser st Modify
-pModSpec = do
-   modType <- pModType
-   pFILL
-   att <- pAttributeDescription 
-   pSEP 
-   vals <- sepEndBy pAttrValSpec pSEP
-   return $ mkMod modType att vals
-
--- TODO: Use something safe instead of error
-mkMod :: String -> String -> [AttrValue] -> Modify
-mkMod modType att vals | modType == "add:" = ModAdd att vals
-                       | modType == "delete:" = ModDelete att vals
-                       | modType == "replace:" = ModReplace att vals
-                       | otherwise = error $ "unexpected mod:" ++ modType
-
-pModType :: CharParser st String
-pModType = try (string "add:")
-       <|> try (string "delete:")
-       <|> string "replace:"
-
-pAttributeDescription :: CharParser st String
-pAttributeDescription = pAttributeType
-
-pAttributeType :: CharParser st String
-pAttributeType = try pLdapOid
-             <|> (do { l <- letter; o <- pAttrTypeChars; return $ l:o } )
-
-pAttrValSpec :: CharParser st AttrValue
-pAttrValSpec = do
-   name <- pAttributeDescription
-   val  <- pValueSpec
-   return (name, val)
-
-pValueSpec :: CharParser st Value
-pValueSpec = try (char ':' >> char ':' >> pFILL >> pBase64String)
-         <|> try (char ':' >> pFILL >> pSafeString) 
-         <|> (char ':' >> char '<' >> pFILL >> pURL)
-
-pURL :: CharParser st String
-pURL = pSafeString
-
-pSafeString :: CharParser st String
-pSafeString = do
-   c <- noneOf "\n\r :<"
-   r <- many (noneOf "\n\r")
-   return $ c:r
- 
-pBase64String :: CharParser st String
-pBase64String = pSafeString
-
-pAttrTypeChars :: CharParser st String
-pAttrTypeChars = many (satisfy (\x -> isAlphaNum x || x == '-'))
-
-pLdapOid :: CharParser st String
-pLdapOid = do
-   num <- many1 digit
-   rest <- many (do { string "."; n <- many1 digit; return $ '.':n})
-   return $ num ++ concat rest
-
-pFILL :: CharParser st ()
-pFILL = spaces
-
-pSEP :: CharParser st ()
-pSEP = try (char '\r' >> char '\n' >> return () )
-   <|> (char '\n' >> return () )
-
-pSEPs :: CharParser st ()
-pSEPs = many pSEP >> return ()
+import Text.LDIF.Types
+import Text.LDIF.Parser
+import Text.LDIF.Printer
+import Text.LDIF.Proc
 
+  
diff --git a/src/Text/LDIF/Parser.hs b/src/Text/LDIF/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/LDIF/Parser.hs
@@ -0,0 +1,232 @@
+module Text.LDIF.Parser (
+	parseLDIFStr,
+	parseLDIFFile,
+        parseDNStr
+)
+where
+import Text.LDIF.Types
+import Text.ParserCombinators.Parsec
+import Data.Either
+import Data.Char
+import Data.List (isPrefixOf)
+
+-- | Parse string as LDIF content and return LDIF or ParseError
+parseLDIFStr :: String -> Either ParseError LDIF
+parseLDIFStr = parse pLdif "(param)" . preproc 
+
+-- | Read and parse provided file and return LDIF or ParseError
+parseLDIFFile :: String -> IO (Either ParseError LDIF)
+parseLDIFFile name = do
+	input <- readFile name
+	return $ parse pLdif name (preproc input)
+
+-- | Parse string as DN and return DN type or ParseError
+parseDNStr :: String -> Either ParseError DN
+parseDNStr = parse pDN "(param)" 
+
+-- | Preprocessing for concat wrapped lines and remove comment lines
+preproc :: String -> String
+preproc = unwrap . stripComments
+
+-- | Remove Comment Lines
+stripComments :: String -> String
+stripComments input = unlines $ filter (not . isPrefixOf "#") $ lines input
+
+-- | Unwrap lines, lines with space at begin is continue of previous line 
+unwrap :: String -> String
+unwrap input = unlines $ preprocLines $ lines input
+   where 
+    preprocLines xs = unbox $ foldl (preprocLine) ([],Nothing) xs
+    preprocLine (ys,r) []                 = (addLineMaybe ys r,Just []) 
+    preprocLine (ys,r) (l:lx) | l == ' '  = (ys,concatLineMaybe r lx)
+                              | otherwise = (addLineMaybe ys r, Just $ l:lx)
+    concatLineMaybe Nothing  x = Just x
+    concatLineMaybe (Just y) x = Just (y++x)
+    addLineMaybe xs Nothing  = xs
+    addLineMaybe xs (Just x) = xs++[x]
+    unbox (ys,Nothing) = ys
+    unbox (ys,Just x)  = ys++[x]
+
+-- | Parsec ldif parser
+pLdif :: CharParser st LDIF
+pLdif = try pLdifChanges <|> pLdifContent
+
+pLdifChanges :: CharParser st LDIF
+pLdifChanges = do
+    ver <- optionMaybe pVersionSpec
+    recs <- sepEndBy1 pChangeRec pSEPs
+    return $ LDIFChanges ver recs
+
+pLdifContent :: CharParser st LDIF
+pLdifContent = do
+    ver <- optionMaybe pVersionSpec
+    recs <- sepEndBy1 pAttrValRec pSEPs
+    return $ LDIFContent ver recs
+
+pAttrValRec ::  CharParser st ContentRecord
+pAttrValRec = do
+    dn <- pDNSpec
+    pSEP
+    attrVals <- sepEndBy1 pAttrValSpec pSEP
+    return $ ContentRecord dn attrVals
+
+pChangeRec :: CharParser st ChangeRecord
+pChangeRec = try pChangeAdd
+         <|> try pChangeDel
+         <|> try pChangeMod
+         <|> pChangeModDN
+
+pChangeAdd :: CharParser st ChangeRecord
+pChangeAdd = do
+    dn <- pDNSpec
+    pSEP
+    string "changetype:"
+    pFILL
+    string "add"
+    pSEP
+    vals <- sepEndBy1 pAttrValSpec pSEP
+    return $ ChangeRecord dn (ChangeAdd vals)
+
+pChangeDel :: CharParser st ChangeRecord
+pChangeDel = do
+    dn <- pDNSpec
+    pSEP
+    string "changetype:"
+    pFILL
+    string "delete"
+    pSEP
+    return $ ChangeRecord dn ChangeDelete
+
+pChangeMod :: CharParser st ChangeRecord
+pChangeMod = do
+    dn <- pDNSpec
+    pSEP
+    string "changetype:"
+    pFILL
+    string "modify"
+    pSEP
+    mods <- sepEndBy1 pModSpec (char '-' >> pSEP)
+    return $ ChangeRecord dn (ChangeModify mods)
+
+pChangeModDN :: CharParser st ChangeRecord
+pChangeModDN = do
+    dn <- pDNSpec
+    pSEP
+    string "changetype:"
+    pFILL
+    string "modrdn" 
+    pSEP
+    string "newrdn:"
+    pFILL 
+    pRDN
+    pSEP
+    string "deleteoldrdn:"
+    pFILL
+    oneOf "01"
+    pSEP
+    return $ ChangeRecord dn ChangeModDN
+
+pRDN :: CharParser st String
+pRDN = pSafeString
+
+pDNSpec :: CharParser st DN
+pDNSpec = do
+    string "dn:"
+    pDN
+
+pDN :: CharParser st DN
+pDN = do
+   pFILL
+   avals <- sepEndBy pAttrEqValue (char ',')  
+   return $ DN avals
+
+pAttrEqValue :: CharParser st AttrValue
+pAttrEqValue = do
+   att <- pAttributeType
+   char '='
+   val <- pAttrValueDN
+   return (att,val)
+
+pAttrValueDN :: CharParser st Value
+pAttrValueDN = do
+   many (noneOf stringChars)
+   where 
+     specialChars = [',','=','+','<','>','#',';','\n','\r']
+     stringChars  = '\\':'"':specialChars 
+
+pVersionSpec :: CharParser st String
+pVersionSpec = do
+   string "version:"
+   pFILL
+   many1 digit
+
+pModSpec :: CharParser st Modify
+pModSpec = do
+   modType <- pModType
+   pFILL
+   att <- pAttributeDescription 
+   pSEP 
+   vals <- sepEndBy pAttrValSpec pSEP
+   return $ mkMod modType att vals
+
+mkMod :: String -> String -> [AttrValue] -> Modify
+mkMod modType att vals | modType == "add:" = ModAdd att (map (snd) vals)
+                       | modType == "delete:" = ModDelete att (map (snd) vals)
+                       | modType == "replace:" = ModReplace att (map (snd) vals)
+                       | otherwise = error $ "unexpected mod:" ++ modType 
+                         -- error can not be reached because pModType
+
+pModType :: CharParser st String
+pModType = try (string "add:")
+       <|> try (string "delete:")
+       <|> string "replace:"
+
+pAttributeDescription :: CharParser st String
+pAttributeDescription = pAttributeType
+
+pAttributeType :: CharParser st String
+pAttributeType = try pLdapOid
+             <|> (do { l <- letter; o <- pAttrTypeChars; return $ l:o } )
+
+pAttrValSpec :: CharParser st AttrValue
+pAttrValSpec = do
+   name <- pAttributeDescription
+   val  <- pValueSpec
+   return (name, val)
+
+pValueSpec :: CharParser st Value
+pValueSpec = try (char ':' >> char ':' >> pFILL >> pBase64String)
+         <|> try (char ':' >> pFILL >> pSafeString) 
+         <|> (char ':' >> char '<' >> pFILL >> pURL)
+
+pURL :: CharParser st String
+pURL = pSafeString
+
+pSafeString :: CharParser st String
+pSafeString = do
+   c <- noneOf "\n\r :<"
+   r <- many (noneOf "\n\r")
+   return $ c:r
+ 
+pBase64String :: CharParser st String
+pBase64String = pSafeString
+
+pAttrTypeChars :: CharParser st String
+pAttrTypeChars = many (satisfy (\x -> isAlphaNum x || x == '-'))
+
+pLdapOid :: CharParser st String
+pLdapOid = do
+   num <- many1 digit
+   rest <- many (do { string "."; n <- many1 digit; return $ '.':n})
+   return $ num ++ concat rest
+
+pFILL :: CharParser st ()
+pFILL = spaces
+
+pSEP :: CharParser st ()
+pSEP = try (char '\r' >> char '\n' >> return () )
+   <|> (char '\n' >> return () )
+
+pSEPs :: CharParser st ()
+pSEPs = many pSEP >> return ()
+
diff --git a/src/Text/LDIF/Printer.hs b/src/Text/LDIF/Printer.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/LDIF/Printer.hs
@@ -0,0 +1,49 @@
+-- | LDIF serializers
+module Text.LDIF.Printer (
+	ldif2Str,
+        dn2Str,
+	change2Str,
+	content2Str
+)
+where
+import Text.LDIF.Types
+import Data.List
+
+-- | Serialize LDIF in LDIF Format
+ldif2Str :: LDIF -> String
+ldif2Str (LDIFContent v xs) = unlines $ (ver2Str v) ++ (map (content2Str) xs)
+ldif2Str (LDIFChanges v xs) = unlines $ (ver2Str v) ++ (map (change2Str) xs)
+
+-- | Serialize version to LDIF Format Lines
+ver2Str :: Maybe String -> [String]
+ver2Str Nothing = []
+ver2Str (Just v) = ["version: "++v]
+
+-- | Serialize DN to LDIF Format
+dn2Str :: DN -> String
+dn2Str (DN xs) = intercalate "," $ map (\(n,v) -> n++"="++v) xs
+
+-- | Serialize Content Record in LDIF Format
+content2Str :: ContentRecord -> String
+content2Str (ContentRecord dn xs) = unlines $ [ "dn: "++(dn2Str dn) ] ++ (attrVals2Ln xs)
+
+-- | Serialize Change Record in LDIF Format
+change2Str :: ChangeRecord -> String
+change2Str (ChangeRecord dn (ChangeDelete))     = unlines   [ "dn: "++(dn2Str dn), "changetype: delete" ]
+change2Str (ChangeRecord dn (ChangeAdd xs))     = unlines $ [ "dn: "++(dn2Str dn), "changetype: add"    ] ++ (attrVals2Ln xs)
+change2Str (ChangeRecord dn (ChangeModify xs))  = unlines $ [ "dn: "++(dn2Str dn), "changetype: modify" ] ++ (mods2Ln xs)
+change2Str (ChangeRecord dn (ChangeModDN))      = unlines $ [ "dn: "++(dn2Str dn), "changetype: moddn"  ]
+
+attrVals2Ln :: [AttrValue] -> [String]
+attrVals2Ln xs = map (attrVal2Ln) xs
+
+attrVal2Ln :: AttrValue -> String
+attrVal2Ln (n,v) = n ++ ": "++v
+
+mods2Ln :: [Modify] -> [String]
+mods2Ln xs = intercalate ["-"] $ map (mod2Ln) xs
+
+mod2Ln :: Modify -> [String]
+mod2Ln (ModAdd     nm xs) = [ attrVal2Ln ("add",nm)     ] ++ (map (\v -> attrVal2Ln (nm,v)) xs) 
+mod2Ln (ModDelete  nm xs) = [ attrVal2Ln ("delete",nm)  ] ++ (map (\v -> attrVal2Ln (nm,v)) xs)
+mod2Ln (ModReplace nm xs) = [ attrVal2Ln ("replace",nm) ] ++ (map (\v -> attrVal2Ln (nm,v)) xs)
diff --git a/src/Text/LDIF/Proc.hs b/src/Text/LDIF/Proc.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/LDIF/Proc.hs
@@ -0,0 +1,62 @@
+-- | LDIF related operations
+module Text.LDIF.Proc (
+        findChangesByDN,
+        findContentsByDN,
+	findContentByDN,
+	diffLDIF,
+        diffRecord 
+)
+where
+import Text.LDIF.Types
+import Data.Maybe
+
+-- | Find all Changes with given DN
+findChangesByDN :: LDIF -> DN -> [ChangeRecord]
+findChangesByDN _ _ = error "not implemented"
+
+-- | Find all Contents with given DN
+findContentsByDN :: LDIF -> DN -> [ContentRecord]
+findContentsByDN (LDIFContent _ entries) dn = filter (\x -> (coDN x) == dn) entries
+findContentsByDN _ _ = []
+
+-- | Find first Content with given DN
+findContentByDN :: LDIF -> DN -> Maybe ContentRecord
+findContentByDN ldif dn = case findContentsByDN ldif dn of
+                                 []   -> Nothing
+                                 xs   -> Just (head xs)
+
+-- | Create Change LDIF between to LDIF contents. If any
+-- | of input argument is not LDIFContent it returns Nothing. 
+-- | If there is not difference the Change LDIF with empty
+-- | change list is returned.
+-- |
+-- | Unsing following strategy: 
+-- | 1. Iterate over L1 DN's and Modify / Remove Content 
+-- | 2. Iterate over L2 and Add Content not in L1
+diffLDIF :: LDIF -> LDIF -> Maybe LDIF
+diffLDIF l1@(LDIFContent v1 c1) l2@(LDIFContent v2 c2) = Just (LDIFChanges v2 changes) 
+   where 
+      changes = filter (not . isDummyChangeRecord) $ foldl (processEntry) [] c1
+      processEntry xs e1 = let me2 = findContentByDN l2 (coDN e1) 
+                               change = case me2 of
+					   Nothing -> ChangeRecord (coDN e1) ChangeDelete
+                                           Just e2 -> fromJust $ diffRecord e1 e2
+                           in xs ++ [change]
+diffLDIF _ _ = Nothing
+
+-- | Diff two AttrVal Records if any of provided. 
+-- | Implementation uses inefficient algorithm for large count of attributes within ContentRecord.
+diffRecord :: ContentRecord -> ContentRecord -> Maybe ChangeRecord
+diffRecord r1 r2 | (coDN r1) /= (coDN r2) = Nothing
+                 | otherwise = Just (ChangeRecord (coDN r1) (ChangeModify mods))
+   where
+      mods = delMods ++ addMods
+      addMods = map (\x -> ModAdd (fst x) [(snd x)]) addVals
+      delMods = map (\x -> ModDelete (fst x) [(snd x)]) delVals
+      addVals = filter (\x -> not $ elem x (coAttrVals r1)) (coAttrVals r2) :: [AttrValue]
+      delVals = filter (\x -> not $ elem x (coAttrVals r2)) (coAttrVals r1) :: [AttrValue]
+
+-- | Change record without any impact
+isDummyChangeRecord :: ChangeRecord -> Bool
+isDummyChangeRecord (ChangeRecord _ (ChangeModify [])) = True 
+isDummyChangeRecord _ = False
diff --git a/src/Text/LDIF/Types.hs b/src/Text/LDIF/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/LDIF/Types.hs
@@ -0,0 +1,43 @@
+-- | LDIF related types
+module Text.LDIF.Types (
+ 	LDIF(..),   
+        ContentRecord(..),
+        ChangeRecord(..),
+        Change(..),
+        Modify(..), 
+        DN(..), 
+        Attribute, Value, AttrValue
+)
+where
+import Data.Char
+
+type Attribute = String
+type Value = String
+type AttrValue = (Attribute, Value)
+
+-- | Represents LDIF structure, it can be either simply LDIF data dump or
+-- | changes LDIF with LDAP operations 
+data LDIF = LDIFContent { lcVersion :: Maybe String, lcEntries :: [ContentRecord] }
+          | LDIFChanges { lcVersion :: Maybe String, lcChanges :: [ChangeRecord] } deriving (Show, Eq)
+
+-- | Represents one data record within LDIF file with DN and content
+data ContentRecord =ContentRecord { coDN :: DN, coAttrVals :: [AttrValue] } deriving (Show, Eq)
+
+-- | Represents one change record within LDIF file with DN and content
+data ChangeRecord = ChangeRecord  { chDN :: DN, chOp :: Change } deriving (Show, Eq)
+
+-- | Represents one LDAP operation within changes LDIF
+data Change = ChangeAdd     { chAttrVals :: [AttrValue] }
+            | ChangeDelete 
+            | ChangeModify  { chMods :: [Modify] }
+            | ChangeModDN  deriving (Show, Eq)
+
+-- | Represents ChangeModify operations upon one entry within given DN
+data Modify = ModAdd     { modAttr :: Attribute, modAttrVals :: [Value] }
+            | ModDelete  { modAttr :: Attribute, modAttrVals :: [Value] }
+            | ModReplace { modAttr :: Attribute, modAttrVals :: [Value] } deriving (Show, Eq)
+
+-- | Represents Distinguished Name (DN)
+data DN = DN { dnAttrVals :: [AttrValue] } deriving (Show, Eq)
+
+
diff --git a/src/diffLDIF.hs b/src/diffLDIF.hs
new file mode 100644
--- /dev/null
+++ b/src/diffLDIF.hs
@@ -0,0 +1,16 @@
+-- | Make Change LDIF based on two Content LDIFs
+import Data.List
+import Data.Either
+import Data.Maybe
+import Control.Monad 
+import System.FilePath 
+import System.Environment
+import Text.LDIF
+
+main = do
+  args <- getArgs
+  ml1 <- parseLDIFFile (args !! 0)
+  ml2 <- parseLDIFFile (args !! 1)
+  case rights [ml1,ml2] of 
+       [l1,l2] -> putStrLn $ ldif2Str $ fromJust $ diffLDIF l1 l2  
+       _       -> print $ lefts [ml1,ml2] 
diff --git a/tests/TestMain.hs b/tests/TestMain.hs
--- a/tests/TestMain.hs
+++ b/tests/TestMain.hs
@@ -12,6 +12,17 @@
     ls <- getLDIFs ldifDir
     runTestTT (tests ls)
 
+tests ls = TestList $ (testCasesParseOK ls) ++ (testCasesDIFF)
+
+--
+-- Test Cases
+--
+testCasesDIFF = [TestCase (assertEqual "dummy" True True)]
+testCasesParseOK ls = map (\x -> TestCase (assertParsedOK x)) $ filter (isOK) ls
+
+--
+-- Support Methods
+--
 getLDIFs :: String -> IO [String]
 getLDIFs dr = do
     liftM (map (dr </>)) $ liftM (filter isLDIF) $ getDirectoryContents dr
@@ -19,10 +30,6 @@
 isOK x = isPrefixOf "OK" (takeFileName x)
 isLDIF x = isSuffixOf ".ldif" x
 
-tests ls = TestList (testCasesParseOK ls)
-
-testCasesParseOK ls = map (\x -> TestCase (assertParsedOK x)) $ filter (isOK) ls
-
 assertParsedOK filename = do
      ret <- parseLDIFFile filename 
      either (\e -> assertFailure (show e)) (\ldif -> assertParsedType filename ldif) ret
@@ -31,9 +38,9 @@
                            | (isSuffixOf ".content.ldif" name) = assertTypeContent name ldif
                            | otherwise = assertFailure $ "Unexpected filename: (not .modify.ldif or .content.ldif " ++ name
 
-assertTypeContent n l@(LDIFContent _ _) = assertBool "Valid Content Type" True >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))
+assertTypeContent n l@(LDIFContent _ _) = assertBool "Valid Content Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))
 assertTypeContent n x = assertFailure $ n ++ " is not type of LDIFContent"
 
-assertTypeChanges n l@(LDIFChanges _ _) = assertBool "Valid Changes Type" True >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))
+assertTypeChanges n l@(LDIFChanges _ _) = assertBool "Valid Changes Type" True -- >> (putStrLn $ "\n\n" ++ n ++ "\n\n" ++ (show l))
 assertTypeChanges n x = assertFailure $ n ++ " is not type of LDIFChanges"
   
diff --git a/tests/data/OK_diff01.content.ldif b/tests/data/OK_diff01.content.ldif
new file mode 100644
--- /dev/null
+++ b/tests/data/OK_diff01.content.ldif
@@ -0,0 +1,10 @@
+dn: cn=The Postmaster,dc=example,dc=com
+objectClass: organizationalRole
+cn: The Postmaster
+oldAttr: attrValue1
+oldAttr: attrValue2
+
+dn: cn=The Postmaster Remove,dc=example,dc=com
+objectClass: organizationalRole
+cn: The Postmaster Remove
+
diff --git a/tests/data/OK_diff02.content.ldif b/tests/data/OK_diff02.content.ldif
new file mode 100644
--- /dev/null
+++ b/tests/data/OK_diff02.content.ldif
@@ -0,0 +1,4 @@
+dn: cn=The Postmaster,dc=example,dc=com
+objectClass: organizationalRole
+cn: The Postmaster
+newAttribute: newValue
diff --git a/tests/data/OK_simpleComment.modify.ldif b/tests/data/OK_simpleComment.modify.ldif
new file mode 100644
--- /dev/null
+++ b/tests/data/OK_simpleComment.modify.ldif
@@ -0,0 +1,48 @@
+#Hello World
+dn: CN=John Smith,OU=Legal,DC=example,DC=com
+changetype: modify
+# In middle
+replace:employeeID
+employeeID: 1234
+-
+# Here
+replace:employeeNumber
+employeeNumber: 98722
+-
+replace: extensionAttribute6
+extensionAttribute6: JSmith98
+-
+
+# Test
+# coment is the # comment
+dn: CN=Jane Smith,OU=Accounting,DC=example,DC=com
+changetype: modify
+replace:employeeID
+employeeID: 5678
+-
+replace:employeeNumber
+employeeNumber: 76543
+-
+replace: extensionAttribute6
+extensionAttribute6: JSmith14
+-
+
+
+dn: CN=Jane Smith,OU=Accounting,DC=example,DC=com
+changetype: modify
+add:employeeID
+employeeID: 5678
+-
+delete:employeeNumber
+employeeNumber: 76543
+-
+replace: extensionAttribute6
+extensionAttribute6: JSmith14
+extensionAttribute6: JSmith18
+-
+add: extensionAttribute6
+extensionAttribute6: JSmith14
+extensionAttribute6: JSmith15
+-
+delete:employeeNumber
+-
diff --git a/tests/data/OK_simpleWrap.modify.ldif b/tests/data/OK_simpleWrap.modify.ldif
new file mode 100644
--- /dev/null
+++ b/tests/data/OK_simpleWrap.modify.ldif
@@ -0,0 +1,45 @@
+dn: CN=John Smith,OU=Legal,DC=example,
+ DC=com
+change
+ type: modify
+replace:employeeID
+employeeID: 1234
+-
+replace:employeeNumber
+employeeNumber: 98722
+-
+replace: extensionAttribute6
+extensionAttribute6: JSmith98
+-
+
+dn: CN=Jane Smith,OU=Accounting,DC=example,DC=com
+changetype: modify
+replace:employeeID
+employeeID: 5678
+-
+replace:employeeNumber
+employeeNumber: 76543
+-
+replace: extensionAttribute6
+extensionAttribute6: JSmith14
+-
+
+
+dn: CN=Jane Smith,OU=Accounting,DC=example,DC=com
+changetype: modify
+add:employeeID
+employeeID: 5678
+-
+delete:employeeNumber
+employeeNumber: 76543
+-
+replace: extensionAttribute6
+extensionAttribute6: JSmith14
+extensionAttribute6: JSmith18
+-
+add: extensionAttribute6
+extensionAttribute6: JSmith14
+extensionAttribute6: JSmith15
+-
+delete:employeeNumber
+-
