diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2011, Jinjing Wang
+
+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 Jinjing Wang nor the names of other
+      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
+OWNER 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/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,4 @@
+#! /usr/bin/env runhaskell
+
+> import Distribution.Simple
+> main = defaultMain
diff --git a/air-extra.cabal b/air-extra.cabal
new file mode 100644
--- /dev/null
+++ b/air-extra.cabal
@@ -0,0 +1,40 @@
+Name:                 air-extra
+Version:              2011.6.11
+Build-type:           Simple
+Synopsis:             air-extra
+Description:          An alternative Haskell Prelude library, extra helpers
+License:              BSD3
+Author:               Jinjing Wang
+Maintainer:           Jinjing Wang <nfjinjing@gmail.com>
+Build-Depends:        base
+Cabal-version:        >= 1.2
+category:             Development
+license-file:         LICENSE
+homepage:             http://github.com/nfjinjing/air-extra
+data-files:           readme.md, changelog.md, known-issues.md
+
+library
+  ghc-options: -Wall
+
+      
+  build-depends:  
+                  base >= 4 && < 5
+                , containers
+                , array
+                , parallel
+                , old-time
+                , time
+                , bytestring >= 0.9
+                , regexpr >= 0.3.4
+                , parsec >= 2
+                , utf8-string >= 0.3.3
+                , directory
+                , old-locale
+                , filepath
+                , air
+                
+  hs-source-dirs: src/
+  exposed-modules:  
+                    Air.Extra
+                  , Air.Heavy
+                  , Air.UTF8
diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,5 @@
+2011.6.11
+---------
+
+* init
+
diff --git a/known-issues.md b/known-issues.md
new file mode 100644
--- /dev/null
+++ b/known-issues.md
diff --git a/readme.md b/readme.md
new file mode 100644
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,2 @@
+An alternative Haskell Prelude library, extra helpers.
+
diff --git a/src/Air/Extra.hs b/src/Air/Extra.hs
new file mode 100644
--- /dev/null
+++ b/src/Air/Extra.hs
@@ -0,0 +1,155 @@
+module Air.Extra where
+
+import Control.Parallel
+import Data.Char
+import Data.List (transpose, (\\))
+import Data.Maybe
+import Data.Time
+import Data.Time.Clock.POSIX
+import Air.Light
+import Numeric
+import Prelude hiding ((.), (^), (>), (<), (/), elem, foldl)
+import System.Directory
+import System.IO
+import System.Locale (defaultTimeLocale)
+import System.Time
+import Text.RegexPR
+import qualified Codec.Binary.UTF8.String as Codec
+import qualified Data.ByteString.Lazy.Char8 as B
+import qualified Data.List as L
+import qualified Prelude as Prelude
+import qualified System.IO.Unsafe as Unsafe
+
+
+-- Parallel
+p_eval, p_eval' :: [a] -> [a]
+p_reduce, p_reduce' :: (a -> a -> a) -> [a] -> a
+p_map, p_map' :: (a -> b) -> [a] -> [b]
+p_split_to :: Int -> [t] -> [[t]]
+p_map_reduce_to :: Int -> ([a] -> b) -> (b -> b -> b) -> [a] -> b
+p_map_reduce :: ([a] -> b) -> (b -> b -> b) -> [a] -> b
+
+p_eval xs                = xs.par(xs.reduce(par))
+p_reduce op xs           = xs.p_eval.reduce(op)
+p_map op xs              = xs.map(op).p_eval
+
+p_eval' xs               = xs.pseq( xs.reduce par )
+p_reduce' op xs          = xs.p_eval'.reduce op
+p_map' op xs             = xs.map op .p_eval'
+
+
+p_split_to n xs          = xs.in_group_of(n).L.transpose
+p_map_reduce_to n m r xs = xs.split_to n .map m .p_reduce' r
+p_map_reduce m r xs      = p_map_reduce_to 16 m r xs
+
+-- Date
+date :: Integer -> Int -> Int -> Data.Time.Day
+splash_date :: Data.Time.Day -> (Integer, Int, Int)
+date        = fromGregorian
+splash_date = toGregorian
+
+-- String
+split_raw :: String -> String -> [String]
+split_raw re xs
+  | xs.match re .isJust = splitRegexPR re xs
+  | otherwise           = [xs]
+
+split :: String -> String -> [String]
+split re xs = split_raw re xs .reject empty
+
+split' :: String -> [String]
+split' s = s.lines.reject empty
+
+sub :: String -> String -> String -> String
+sub = subRegexPR
+
+gsub :: String -> String -> String -> String
+gsub = gsubRegexPR
+
+type RegexResult = ( String, (String, String) )
+type MatchList   = [ (Int, String) ]
+match :: String -> String -> Maybe (RegexResult, MatchList)
+match = matchRegexPR
+
+strip :: String -> String
+strip s = s.sub "^\\s*" "" .reverse .sub "^\\s*" "" .reverse
+
+empty :: String -> Bool
+empty s = case s.match("\\S") of
+  Just _ -> False
+  Nothing -> True
+  
+
+-- Integer
+collapse :: (Integral a, Num b) => [a] -> b
+collapse xs = collapse' (xs.reverse.map from_i) (0 :: Int) (0 :: Int) .fromIntegral
+  where
+    collapse' [] _ r     = r
+    collapse' (x:xs') q r = collapse' xs' (q+1) (r + x * 10 Prelude.^ q)
+
+base :: (Integral a) => a -> a -> String
+base p n = showIntAtBase p intToDigit n ""
+    
+-- String
+camel_case, snake_case :: String -> String
+camel_case = split "_" > map capitalize > join'
+snake_case = gsub "\\B[A-Z]" "_\\&" > lower
+
+-- IO
+purify :: IO a -> a
+purify = Unsafe.unsafePerformIO
+
+ls :: String -> IO [String]
+ls s = getDirectoryContents s ^ (\\ [".", ".."])
+
+file_size :: String -> IO Integer
+file_size path = withFile (path.u2b) ReadMode hFileSize
+
+file_mtime :: String -> IO UTCTime
+file_mtime path = 
+  getModificationTime (path.u2b) ^ seconds ^ from_i ^ posixSecondsToUTCTime
+  where seconds (TOD s _) = s
+
+read_binary_file :: String -> IO String
+read_binary_file path = path.u2b.B.readFile ^ B.unpack
+
+get_permissions :: String -> IO Permissions
+get_permissions path = getPermissions (path.u2b) 
+
+get_current_directory :: IO String
+get_current_directory = getCurrentDirectory ^ b2u
+
+-- Time
+now :: IO UTCTime
+now = getCurrentTime
+
+format_time :: String -> UTCTime -> String
+format_time = formatTime defaultTimeLocale
+
+simple_time_format :: String
+simple_time_format = "%Y-%m-%d %H:%M:%S %Z"
+
+parse_time :: String -> String -> UTCTime
+parse_time = readTime defaultTimeLocale
+
+t2i :: UTCTime -> Integer
+t2i = utcTimeToPOSIXSeconds > floor
+
+t2f :: (Fractional a) => UTCTime -> a
+t2f = utcTimeToPOSIXSeconds > toRational > fromRational
+
+i2t :: Integer -> UTCTime
+i2t = from_i > posixSecondsToUTCTime
+
+f2t :: (Real a) => a -> UTCTime
+f2t = toRational > fromRational > posixSecondsToUTCTime
+
+-- Text
+filter_comment :: String -> String
+filter_comment = 
+  lines > map strip > reject null > reject (head > (== '#')) > unlines
+
+-- UTF8
+b2u, u2b :: String -> String
+b2u = Codec.decodeString
+u2b = Codec.encodeString
diff --git a/src/Air/Heavy.hs b/src/Air/Heavy.hs
new file mode 100644
--- /dev/null
+++ b/src/Air/Heavy.hs
@@ -0,0 +1,48 @@
+module Air.Heavy where
+
+import Data.Char
+import Air.Light
+import Prelude hiding ((.), (^), (>), (<), (/), elem, foldl)
+import qualified Prelude as P
+import Text.ParserCombinators.Parsec (many, char, many1, digit, (<|>), Parser, anyChar, try)
+import qualified Data.ByteString.Lazy.Char8 as B
+import qualified Text.ParserCombinators.Parsec as P
+
+
+-- compress
+  
+-- Parser
+parse :: P.GenParser tok () a -> [tok] -> a
+parse p s = case (P.parse p "" s) of
+  Left err -> err.show.error
+  Right x  -> x
+
+
+-- XML
+unescape_xml, escape_xml :: String -> String
+unescape_xml s = parse unescape_parser s
+  where
+    unicode_char :: Parser Char
+    unicode_char = do
+      char '&'
+      char '#'
+      word <- many1 digit
+      char ';'
+      return $ chr (read word)
+
+    unescape_parser :: Parser String
+    unescape_parser = many (try unicode_char <|> anyChar)
+
+escape_xml = concatMap fixChar
+    where
+      fixChar '<' = "<"
+      fixChar '>' = ">"
+      fixChar '&' = "&"
+      fixChar '"' = "\""
+      fixChar c | ord c P.< 0x80 = [c]
+      fixChar c = "&#" ++ show (ord c) ++ ";"
+
+-- backward compatible
+unescape_unicode_xml, escape_unicode_xml :: String -> String
+unescape_unicode_xml = unescape_xml
+escape_unicode_xml = escape_xml
diff --git a/src/Air/UTF8.hs b/src/Air/UTF8.hs
new file mode 100644
--- /dev/null
+++ b/src/Air/UTF8.hs
@@ -0,0 +1,48 @@
+module Air.UTF8 where
+
+import Air.Light
+import Air.Extra as Air
+import Prelude hiding ((.), (^), readFile, writeFile, (>))
+import System.Directory
+import System.IO.UTF8 (readFile, writeFile)
+import qualified Air as Air
+
+-- io
+read_file :: String -> IO String
+read_file  = readFile
+
+write_file :: String -> String -> IO ()
+write_file = writeFile
+
+ls :: String -> IO [String]
+ls x = Air.ls (x.u2b) ^ map b2u
+
+mkdir_p :: String -> IO ()
+mkdir_p = u2b > createDirectoryIfMissing True
+
+file_exist :: String -> IO Bool
+file_exist = u2b > doesFileExist
+
+dir_exist :: String -> IO Bool
+dir_exist = u2b > doesDirectoryExist
+
+split :: String -> String -> [String]
+split x y  = Air.split (x.u2b) (y.u2b) .map b2u
+
+gsub :: String -> String -> String -> String
+gsub x y z = Air.gsub (x.u2b) (y.u2b) (z.u2b) .b2u
+
+sub :: String -> String -> String -> String
+sub x y z  = Air.sub (x.u2b) (y.u2b) (z.u2b) .b2u
+
+match :: String -> String -> Maybe (RegexResult, MatchList)
+match x y  = Air.match (x.u2b) (y.u2b)
+
+strip :: String -> String
+strip x = Air.strip (x.u2b) .b2u
+
+rm :: String -> IO ()
+rm = u2b > removeFile
+
+rm_rf :: String -> IO ()
+rm_rf = u2b > removeDirectoryRecursive
