diff --git a/Data/Progress.hs b/Data/Progress.hs
new file mode 100644
--- /dev/null
+++ b/Data/Progress.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+-- | Progress estimates.
+--
+--   'progress' is good for functions the recursion trees of which are very unbalanced.
+--   'progressWithCalls' is good for functions that consume their input very long
+--   before they finish.
+module Data.Progress (progress, progressWithFile, progressWithCalls, progress', progressWithCalls') where
+
+import System.IO.Unsafe
+import System.IO
+import Data.Data
+import Control.Monad.Identity
+import Control.Monad
+import Control.Concurrent
+import Control.Concurrent.MVar
+import Control.Exception
+
+newtype Size t = Size { unSize :: Int }
+
+size :: (Data t) => t -> Int
+size x = unSize (gfoldl (\(Size n) y -> Size (n + size y)) (const (Size 1)) x)
+
+fiftieth x y = x * 50 `quot` y
+
+putBar n prev sz = sequence_ (replicate x (putChar '|')) where
+	x = fiftieth n sz - fiftieth prev sz
+
+-- | Estimate progress based on thunks forced.
+progress f dat = do
+	putChar '['
+	let sz = size dat
+	count <- newMVar 0
+
+	-- The 'rec' function will make a copy of the input data
+	-- structure, with I/O effects added that print a progress bar
+	-- as the data structure is forced.
+	let
+		rec :: (Data t) => t -> t
+		rec dat = runIdentity $ gfoldl
+			(\(Identity f) x -> unsafePerformIO $ do
+				modifyMVar_ count $ \n ->
+					if n == -1 then do
+						let n' = n + 1
+						putBar n' n sz
+						return n'
+					else
+						return n
+				return $ Identity $ f $ rec x)
+			Identity
+			dat
+
+	-- Run the function on the copy.
+	finally
+		(do
+		res <- f $ rec dat
+		return $! res)
+		(do
+		-- Record that the function is done so no more bars are printed.
+		modifyMVar_ count $ const $ return $ -1
+		putStrLn "]")
+
+try' :: IO t -> IO (Either SomeException t)
+try' = try
+
+-- | ...based on amount of file consumed.
+progressWithFile f hdl = do
+	putChar '['
+
+	-- Check the position of the handle periodically and print
+	-- a progress bar.
+	thd <- try' $ do
+		sz <- liftM fromInteger $ hFileSize hdl
+		forkIO $ foldM_ (\prev () -> do
+			n <- liftM fromInteger $ hTell hdl
+			putBar n prev sz
+			threadDelay 500000
+			return n)
+			0
+			(repeat ())
+
+	finally
+	-- Run the function.
+		(f hdl)
+		(do
+		-- Again, prevent the progress bar from being printed once
+		-- the function is done.
+		try' $ either (\_ -> return ()) killThread thd
+		putStrLn "]")
+
+-- | ...based on number of recursive calls.
+--
+--   It returns a result equivalent to that of /fix f x/.
+progressWithCalls f x = do
+	putChar '['
+
+	-- As the function runs, the procedure will estimate the
+	-- depth and branching factor of the recursion tree.
+	parms <- newMVar (0, 0, 0)
+	let rec depth count x = do
+		modifyMVar_ count $ return . (+1)
+
+		-- Do a recursive call. The call gets a fresh recursion counter.
+		count' <- newMVar 0
+		res <- f (rec (depth + 1) count') x
+		return $! res
+
+		x <- readMVar count'
+		modifyMVar_ parms $ \tup@(mxDep, mxCount, total) -> do
+			-- Calculate the new maxima.
+			let tup'@(mxDep', mxCount', total') = if total < 0 then
+					tup
+				else if x == 0 then
+					(depth `max` mxDep, mxCount, total + 1)
+				else
+					(mxDep, x `max` mxCount, total + 1)
+
+			-- Print a progress bar with the new estimate.
+			when (total >= 10) $ putBar
+				(total' * 50
+					`quot` mxCount' ^ (mxDep' + 1))
+				(total * 50
+					`quot` mxCount ^ (mxDep + 1))
+				50
+
+			return tup'
+
+		return res
+	count <- newMVar 0
+	finally
+		(do
+		res <- rec 0 count x
+		return $! res)
+		(do
+		modifyMVar_ parms $ const $ return (0, 0, -1)
+		putStrLn "]")
+
+-- | Adapters for pure functions.
+progress' f = progress (return . f)
+
+progressWithCalls' f = progressWithCalls (\g -> return . f (unsafePerformIO . g))
+
diff --git a/EstProgress.cabal b/EstProgress.cabal
new file mode 100644
--- /dev/null
+++ b/EstProgress.cabal
@@ -0,0 +1,21 @@
+-- Initial EstProgress.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                EstProgress
+version:             0.1.0.0
+synopsis:            Methods for estimating the progress of functions
+description:         Estimates the progress of a function as it executes, and displays a progress bar.
+homepage:            http://alkalisoftware.net
+license:             BSD3
+license-file:        LICENSE
+author:              James Candy
+maintainer:          info@alkalisoftware.net
+-- copyright:           
+category:            Data
+build-type:          Simple
+cabal-version:       >=1.8
+
+library
+  exposed-modules:     Data.Progress
+  -- other-modules:       
+  build-depends:       base >=4 && <=5, mtl >=1.1
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2015, James Candy
+
+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 James Candy 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.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
