diff --git a/Codec/Binary/Base91.hs b/Codec/Binary/Base91.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Binary/Base91.hs
@@ -0,0 +1,35 @@
+-- Copyright 2015 Alvaro J. Genial (http://alva.ro) -- see LICENSE.md for more.
+-- Informed by Mario Rodriguez's C++ implementation.
+
+module Codec.Binary.Base91 (decoding, encoding) where
+
+import Data.Word (Word8)
+
+encoding :: [Char]
+encoding = [
+  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+  'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+  'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+  '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$',
+  '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=',
+  '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"']
+
+decoding :: [Word8]
+decoding = [
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 62, 90, 63, 64, 65, 66, 91, 67, 68, 69, 70, 71, 91, 72, 73,
+  52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 74, 75, 76, 77, 78, 79,
+  80,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+  15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 81, 91, 82, 83, 84,
+  85, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+  41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 86, 87, 88, 89, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
+  91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91]
diff --git a/Codec/Binary/Base91/ByteString.hs b/Codec/Binary/Base91/ByteString.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Binary/Base91/ByteString.hs
@@ -0,0 +1,58 @@
+-- Copyright 2015 Alvaro J. Genial (http://alva.ro) -- see LICENSE.md for more.
+-- Informed by Mario Rodriguez's C++ implementation.
+
+module Codec.Binary.Base91.ByteString (decode, encode) where
+
+import qualified Codec.Binary.Base91 as B91
+import Data.Bits ((.&.), (.|.), shiftL, shiftR)
+import Data.Char (ord)
+import Data.List (foldl')
+import Data.Word (Word8)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+
+-- | Encodes octets ('ByteString') to a '[Char]' in Base91; the opposite of 'decode'.
+encode :: ByteString -> [Char]
+encode = g . BS.foldl' f (0, 0, []) where
+
+  f :: (Int, Int, [Char]) -> Word8 -> (Int, Int, [Char])
+  f (queue, nbits, cs) w =
+    let queue' = queue .|. (fromIntegral w `shiftL` nbits)
+        nbits' = nbits + 8
+    in if nbits' <= 13 then (queue', nbits', cs) else
+      let val  = queue' .&. 8191
+          val' = queue' .&. 16383
+          (v, q, n)   = if val > 88
+            then (val,  queue' `shiftR` 13, nbits' - 13)
+            else (val', queue' `shiftR` 14, nbits' - 14)
+          trail       = [B91.encoding !! (v `mod` 91),
+                         B91.encoding !! (v `div` 91)]
+      in (q, n, cs ++ trail)
+
+  g :: (Int, Int, [Char]) -> [Char]
+  g (_,     0,     cs) = cs
+  g (queue, nbits, cs) = cs ++ [y] ++ z
+    where y = B91.encoding !! (queue `mod` 91)
+          z | nbits > 7 || queue > 90 = [B91.encoding !! (queue `div` 91)]
+            | otherwise               = []
+
+-- | Decodes octets ('ByteString') from a '[Char]' in Base91; the opposite of 'encode'.
+decode :: [Char] -> ByteString
+decode = g . foldl' f (0, 0, -1, BS.empty) where
+
+  f :: (Int, Int, Int, ByteString) -> Char -> (Int, Int, Int, ByteString)
+  f (queue, nbits, val, bs) c =
+    let d = fromIntegral $ B91.decoding !! ord c
+     in if d   == 91 then (queue, nbits, val, bs) else
+        if val == -1 then (queue, nbits, d,   bs) else
+            let v = val + (d * 91)
+                q = queue .|. (v `shiftL` nbits)
+                n = nbits + (if (v .&. 8191) > 88 then 13 else 14)
+                (queue', nbits', trail) = if n - 8 > 7
+                  then (q `shiftR` 16, n - 16, [q, q `shiftR` 8])
+                  else (q `shiftR` 8,  n - 8,  [q])
+             in (queue', nbits', -1, BS.append bs $ BS.pack (map fromIntegral trail))
+
+  g :: (Int, Int, Int, ByteString) -> ByteString
+  g (_,     _,     -1,  bs) = bs
+  g (queue, nbits, val, bs) = BS.snoc bs (fromIntegral $ queue .|. (val `shiftL` nbits))
diff --git a/Codec/Binary/Base91/Efficient.hs b/Codec/Binary/Base91/Efficient.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Binary/Base91/Efficient.hs
@@ -0,0 +1,59 @@
+-- Copyright 2015 Alvaro J. Genial (http://alva.ro) -- see LICENSE.md for more.
+-- Informed by Mario Rodriguez's C++ implementation.
+
+module Codec.Binary.Base91.Efficient (decode, encode) where
+
+import qualified Codec.Binary.Base91 as B91
+import Data.Bits ((.&.), (.|.), shiftL, shiftR)
+import Data.Char (ord)
+import Data.Word (Word8)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import Data.Text (Text)
+import qualified Data.Text as T
+
+-- | Encodes octets ('ByteString') to 'Text' in Base91; the opposite of 'decode'.
+encode :: ByteString -> Text
+encode = g . BS.foldl' f (0, 0, T.empty) where
+
+  f :: (Int, Int, Text) -> Word8 -> (Int, Int, Text)
+  f (queue, nbits, t) w =
+    let queue' = queue .|. (fromIntegral w `shiftL` nbits)
+        nbits' = nbits + 8
+    in if nbits' <= 13 then (queue', nbits', t) else
+      let val  = queue' .&. 8191
+          val' = queue' .&. 16383
+          (v, q, n)   = if val > 88
+            then (val,  queue' `shiftR` 13, nbits' - 13)
+            else (val', queue' `shiftR` 14, nbits' - 14)
+          trail       = [B91.encoding !! (v `mod` 91),
+                         B91.encoding !! (v `div` 91)]
+      in (q, n, T.append t (T.pack trail))
+
+  g :: (Int, Int, Text) -> Text
+  g (_,     0,     t) = t
+  g (queue, nbits, t) = T.append t (T.pack $ [y] ++ z)
+    where y = B91.encoding !! (queue `mod` 91)
+          z | nbits > 7 || queue > 90 = [B91.encoding !! (queue `div` 91)]
+            | otherwise               = []
+
+-- | Decodes octets ('ByteString') from 'Text' in Base91; the opposite of 'encode'.
+decode :: Text -> ByteString
+decode = g . T.foldl' f (0, 0, -1, BS.empty) where
+
+  f :: (Int, Int, Int, ByteString) -> Char -> (Int, Int, Int, ByteString)
+  f (queue, nbits, val, bs) c =
+    let d = fromIntegral $ B91.decoding !! ord c
+     in if d   == 91 then (queue, nbits, val, bs) else
+        if val == -1 then (queue, nbits, d,   bs) else
+            let v = val + (d * 91)
+                q = queue .|. (v `shiftL` nbits)
+                n = nbits + (if (v .&. 8191) > 88 then 13 else 14)
+                (queue', nbits', trail) = if n - 8 > 7
+                  then (q `shiftR` 16, n - 16, [q, q `shiftR` 8])
+                  else (q `shiftR` 8,  n - 8,  [q])
+             in (queue', nbits', -1, BS.append bs $ BS.pack (map fromIntegral trail))
+
+  g :: (Int, Int, Int, ByteString) -> ByteString
+  g (_,     _,     -1,  bs) = bs
+  g (queue, nbits, val, bs) = BS.snoc bs (fromIntegral $ queue .|. (val `shiftL` nbits))
diff --git a/Codec/Binary/Base91/String.hs b/Codec/Binary/Base91/String.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Binary/Base91/String.hs
@@ -0,0 +1,56 @@
+-- Copyright 2015 Alvaro J. Genial (http://alva.ro) -- see LICENSE.md for more.
+-- Informed by Mario Rodriguez's C++ implementation.
+
+module Codec.Binary.Base91.String (decode, encode) where
+
+import qualified Codec.Binary.Base91 as B91
+import Data.Bits ((.&.), (.|.), shiftL, shiftR)
+import Data.Char (ord)
+import Data.List (foldl')
+import Data.Word (Word8)
+
+-- | Encodes octets ('[Word8]') to a 'String' in Base91; the opposite of 'decode'.
+encode :: [Word8] -> String
+encode = g . foldl' f (0, 0, []) where
+
+  f :: (Int, Int, [Char]) -> Word8 -> (Int, Int, [Char])
+  f (queue, nbits, cs) w =
+    let queue' = queue .|. (fromIntegral w `shiftL` nbits)
+        nbits' = nbits + 8
+    in if nbits' <= 13 then (queue', nbits', cs) else
+      let val  = queue' .&. 8191
+          val' = queue' .&. 16383
+          (v, q, n)   = if val > 88
+            then (val,  queue' `shiftR` 13, nbits' - 13)
+            else (val', queue' `shiftR` 14, nbits' - 14)
+          trail       = [B91.encoding !! (v `mod` 91),
+                         B91.encoding !! (v `div` 91)]
+      in (q, n, cs ++ trail)
+
+  g :: (Int, Int, [Char]) -> [Char]
+  g (_,     0,     cs) = cs
+  g (queue, nbits, cs) = cs ++ [y] ++ z
+    where y = B91.encoding !! (queue `mod` 91)
+          z | nbits > 7 || queue > 90 = [B91.encoding !! (queue `div` 91)]
+            | otherwise               = []
+
+-- | Decodes octets ('[Word8]') from a 'String' in Base91; the opposite of 'encode'.
+decode :: String -> [Word8]
+decode = g . foldl' f (0, 0, -1, []) where
+
+  f :: (Int, Int, Int, [Word8]) -> Char -> (Int, Int, Int, [Word8])
+  f (queue, nbits, val, ws) c =
+    let d = fromIntegral $ B91.decoding !! ord c
+     in if d   == 91 then (queue, nbits, val, ws) else
+        if val == -1 then (queue, nbits, d,   ws) else
+            let v = val + (d * 91)
+                q = queue .|. (v `shiftL` nbits)
+                n = nbits + (if (v .&. 8191) > 88 then 13 else 14)
+                (queue', nbits', trail) = if n - 8 > 7
+                  then (q `shiftR` 16, n - 16, [q, q `shiftR` 8])
+                  else (q `shiftR` 8,  n - 8,  [q])
+             in (queue', nbits', -1, ws ++ map fromIntegral trail)
+
+  g :: (Int, Int, Int, [Word8]) -> [Word8]
+  g (_,     _,     -1,  ws) = ws
+  g (queue, nbits, val, ws) = ws ++ [fromIntegral $ queue .|. (val `shiftL` nbits)]
diff --git a/Codec/Binary/Base91/Text.hs b/Codec/Binary/Base91/Text.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Binary/Base91/Text.hs
@@ -0,0 +1,58 @@
+-- Copyright 2015 Alvaro J. Genial (http://alva.ro) -- see LICENSE.md for more.
+-- Informed by Mario Rodriguez's C++ implementation.
+
+module Codec.Binary.Base91.Text (decode, encode) where
+
+import qualified Codec.Binary.Base91 as B91
+import Data.Bits ((.&.), (.|.), shiftL, shiftR)
+import Data.Char (ord)
+import Data.List (foldl')
+import Data.Word (Word8)
+import Data.Text (Text)
+import qualified Data.Text as T
+
+-- | Encodes octets ('[Word8]') to 'Text' in Base91; the opposite of 'decode'.
+encode :: [Word8] -> Text
+encode = g . foldl' f (0, 0, T.empty) where
+
+  f :: (Int, Int, Text) -> Word8 -> (Int, Int, Text)
+  f (queue, nbits, t) w =
+    let queue' = queue .|. (fromIntegral w `shiftL` nbits)
+        nbits' = nbits + 8
+    in if nbits' <= 13 then (queue', nbits', t) else
+      let val  = queue' .&. 8191
+          val' = queue' .&. 16383
+          (v, q, n)   = if val > 88
+            then (val,  queue' `shiftR` 13, nbits' - 13)
+            else (val', queue' `shiftR` 14, nbits' - 14)
+          trail       = [B91.encoding !! (v `mod` 91),
+                         B91.encoding !! (v `div` 91)]
+      in (q, n, T.append t (T.pack trail))
+
+  g :: (Int, Int, Text) -> Text
+  g (_,     0,     t) = t
+  g (queue, nbits, t) = T.append t (T.pack $ [y] ++ z)
+    where y = B91.encoding !! (queue `mod` 91)
+          z | nbits > 7 || queue > 90 = [B91.encoding !! (queue `div` 91)]
+            | otherwise               = []
+
+-- | Decodes octets ('[Word8]') from 'Text' in Base91; the opposite of 'encode'.
+decode :: Text -> [Word8]
+decode = g . T.foldl' f (0, 0, -1, []) where
+
+  f :: (Int, Int, Int, [Word8]) -> Char -> (Int, Int, Int, [Word8])
+  f (queue, nbits, val, ws) c =
+    let d = fromIntegral $ B91.decoding !! ord c
+     in if d   == 91 then (queue, nbits, val, ws) else
+        if val == -1 then (queue, nbits, d,   ws) else
+            let v = val + (d * 91)
+                q = queue .|. (v `shiftL` nbits)
+                n = nbits + (if (v .&. 8191) > 88 then 13 else 14)
+                (queue', nbits', trail) = if n - 8 > 7
+                  then (q `shiftR` 16, n - 16, [q, q `shiftR` 8])
+                  else (q `shiftR` 8,  n - 8,  [q])
+             in (queue', nbits', -1, ws ++ map fromIntegral trail)
+
+  g :: (Int, Int, Int, [Word8]) -> [Word8]
+  g (_,     _,     -1,  ws) = ws
+  g (queue, nbits, val, ws) = ws ++ [fromIntegral $ queue .|. (val `shiftL` nbits)]
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Alvaro J. Genial
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,4 @@
+-- Copyright 2015 Alvaro J. Genial (http://alva.ro) -- see LICENSE.md for more.
+
+import Distribution.Simple
+main = defaultMain
diff --git a/Test.hs b/Test.hs
new file mode 100644
--- /dev/null
+++ b/Test.hs
@@ -0,0 +1,54 @@
+-- Copyright 2015 Alvaro J. Genial (http://alva.ro) -- see LICENSE.md for more.
+
+import Codec.Binary.Base91.ByteString as A (decode, encode)
+import Codec.Binary.Base91.Efficient  as B (decode, encode)
+import Codec.Binary.Base91.String     as C (decode, encode)
+import Codec.Binary.Base91.Text       as D (decode, encode)
+import qualified Data.ByteString as BS
+import qualified Data.Text as T
+import Test.QuickCheck
+
+main :: IO ()
+main = do
+    testByteString
+    testEfficient
+    testString
+    testText where
+
+  -- Note that the reverse identities, e.g. encode (decode cs) == cs, aren't true because not every
+  -- arbitrary character sequence is valid Base91, even if each character is constrained to the
+  -- Base91 alphabet.
+
+  testByteString = do
+      quickCheck $ prop_identity
+      quickCheck $ example ([], [])
+      quickCheck $ example helloWorld
+    where
+      prop_identity ws = A.decode (A.encode bs) == bs           where bs = BS.pack ws
+      example (ws, cs) = A.encode bs == cs && A.decode cs == bs where bs = BS.pack ws
+
+  testEfficient = do
+      quickCheck $ prop_identity
+      quickCheck $ example ([], [])
+      quickCheck $ example helloWorld
+    where
+      prop_identity ws = B.decode (B.encode bs) == bs          where bs = BS.pack ws
+      example (ws, cs) = B.encode bs == t && B.decode t  == bs where (bs, t) = (BS.pack ws, T.pack cs)
+
+  testString = do
+      quickCheck $ prop_identity
+      quickCheck $ example ([], [])
+      quickCheck $ example helloWorld
+    where
+      prop_identity ws = C.decode (C.encode ws) == ws
+      example (ws, cs) = C.encode ws == cs && C.decode cs == ws
+
+  testText = do
+      quickCheck $ prop_identity
+      quickCheck $ example ([], [])
+      quickCheck $ example helloWorld
+    where
+      prop_identity ws = D.decode (D.encode ws) == ws
+      example (ws, cs) = D.encode ws == t && D.decode t == ws where t = T.pack cs
+
+  helloWorld = ([72,101,108,108,111,44,32,119,111,114,108,100,33], ">OwJh>}A\"=r@@Y?F") -- Hello, World!
diff --git a/base91.cabal b/base91.cabal
new file mode 100644
--- /dev/null
+++ b/base91.cabal
@@ -0,0 +1,46 @@
+Name:                  base91
+Version:               0.1.0
+Author:                Alvaro J. Genial
+Maintainer:            ajg
+Homepage:              https://github.com/ajg/base91
+Synopsis:              A Base91 encoder & decoder
+Description:           An implementation of Base91 encoding & decoding of bytes to/from strings.
+License:               MIT
+License-File:          LICENSE.md
+Category:              Codec
+Build-Type:            Simple
+Cabal-Version:         >= 1.8
+
+Flag ByteString
+  Description:         Enable Data.ByteString support.
+  Default:             True
+
+Flag Text
+  Description:         Enable Data.Text support.
+  Default:             True
+
+Library
+  Build-Depends:       base >= 4 && < 5
+  Exposed-Modules:     Codec.Binary.Base91.String
+  Other-Modules:       Codec.Binary.Base91
+
+  if flag(ByteString)
+    Build-Depends:     bytestring
+    Exposed-Modules:   Codec.Binary.Base91.ByteString
+
+  if flag(Text)
+    Build-Depends:     text
+    Exposed-Modules:   Codec.Binary.Base91.Text
+
+  if flag(ByteString) && flag(Text)
+    Exposed-Modules:   Codec.Binary.Base91.Efficient
+
+Test-Suite tests
+  Hs-Source-Dirs:      .
+  Main-Is:             Test.hs
+  Type:                exitcode-stdio-1.0
+  Build-Depends:       base, base91, bytestring, text, QuickCheck
+
+Source-Repository head
+  type:                git
+  location:            https://github.com/ajg/base91/tree/master
