diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644
--- /dev/null
+++ b/README
@@ -0,0 +1,49 @@
+
+SYNOPSIS
+
+    rselect - select a random set of sequences from a FASTA file.
+    reps    - extract exact k-word repeats based that occur in
+              sequences grouped in different clusters.
+
+
+INSTALLATION
+
+    You'll need GHC or possibly another Haskell system, and the
+    Haskell bioinformatics library.  The Makefile should work to
+    build and install (by default to your home directory) the
+    executables.
+
+USAGE
+
+      rselect n [m] input.seq
+
+    Selects n sequences from the file input.seq.  If the optional
+    m is given, this limits the selection to happen only from the first
+    m sequences in the file, which may be more efficient.
+
+    The selected sequences are written to standard output, so you
+    probably want to redirect them to a file.
+
+      reps k clustered.seq
+
+    Generate a list of repeated k-words (or k-grams) found in the sequences.
+    The sequences are expected to be on UniGene format, i.e. a FASTA file
+    with #-initiated comments separating the clusters.
+
+    A k-word is considered repeated if it is found in more than one of the
+    clusters.
+
+      reps k clustering.lst sequences.seq
+
+    As above, but take a separate input clustering (and ignore any #'s in the
+    sequences.  The clustering should consist of one line per cluster, with each
+    line containing the sequence identifier (first word after the initial '>'
+    in the FASTA header).
+
+BUGS
+
+    Do let me know about them, at <ketil@malde.org>!
+
+HOMEPAGE
+
+   http://malde.org/~ketil/
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/estreps.cabal b/estreps.cabal
new file mode 100644
--- /dev/null
+++ b/estreps.cabal
@@ -0,0 +1,34 @@
+Name:                estreps
+Version:             0.1
+License:             GPL
+License-file:        LICENSE
+Author:              Ketil Malde
+Maintainer:          Ketil Malde <ketil@malde.org>
+Stability:           Beta
+Category:            Bioinformatics
+Synopsis:            Repeats from ESTs
+Description:         rselect - select a random set of sequences from a FASTA file.
+                     reps    - extract exact k-word repeats based that occur in
+                               sequences grouped in different clusters.
+                     .
+                     The Darcs repository is at: <http://malde.org/~ketil/biohaskell/estreps>.
+Homepage:            http://malde.org/~ketil/
+
+Tested-With:         GHC==6.8.2
+Build-Type:          Simple
+Cabal-Version:       >= 1.2
+
+Data-Files:          README
+
+Executable rselect
+           Hs-source-dirs: src
+           Main-Is:        RSelect.lhs
+           Build-Depends:  base>3, random, containers, bio>=0.3.3, bytestring
+           ghc-options:    -Wall
+           Other-Modules:  Unigene
+
+Executable reps
+           Hs-source-dirs: src
+           Main-Is:     Repeats.lhs
+           Other-Modules:  Util
+
diff --git a/src/RSelect.lhs b/src/RSelect.lhs
new file mode 100644
--- /dev/null
+++ b/src/RSelect.lhs
@@ -0,0 +1,79 @@
+
+RSelect - randomly select a number of sequences from a FASTA file
+
+Usage: rselect m < file.seq  # prints m random sequences to stdout
+       rselect m n < file.seq # print m of the first n seq.s (faster)
+
+\begin{code}
+
+module Main where
+
+import Data.Set
+import System.Environment (getArgs)
+import System.Random
+import System.IO
+
+import Bio.Sequence.Fasta
+
+import Unigene
+
+main :: IO ()
+main = do
+  as <- getArgs
+  case as of [m,f] ->   select (read m) Nothing f
+             [m,n,f] -> select (read m) (Just (read n)) f
+             _ -> error "Usage: rselect n [m] input.seq > output.seq"
+
+-- | Select m out of maybe n sequences from f
+--   Not specifying a maximum increases memory consumption
+select :: Integer -> Maybe Integer -> FilePath -> IO ()
+select m mn f = do
+  seed <- newStdGen
+  hPutStrLn stderr ("Random seed: "++show seed)
+  ss <- ugRead f
+  n <- case mn of Nothing -> do hPutStrLn stderr "Counting sequences..."
+                                n' <- countSeqs f
+                                return (fromIntegral n')
+                  Just n' -> return n'
+  let ds = if m>n then error "rselect: m is larger than n, that won't work."
+           else if m==n then take (fromIntegral n) $ repeat 1
+           else if m<=n `div` 2 then diffs $ pick m $ randomRs (1,n) seed
+           else diffs $ invert n 1 $ pick (n-m) $ randomRs (1,n) seed
+      out = Prelude.filter (not.Prelude.null) $ choose ds ss
+  n `seq` hPutStrLn stderr ("N = "++show n++", M = "++show m)
+  mapM_ (\o -> putStrLn "# cluster delimiter" >> hWriteFasta stdout o) out
+
+-- | choose elements according to a list of deltas
+choose :: Show a => [Integer] -> [[a]] -> [[a]]
+choose = choose' []
+
+choose' :: Show a => [a] -> [Integer] -> [[a]] -> [[a]]
+choose' acc [] _ = [reverse acc]
+choose' acc (0:ds) xss = choose' acc ds xss
+choose' acc (d:ds) (xs:xss) = let ys = drop (fromIntegral d-1) xs
+                             in if not (Prelude.null ys) then choose' (head ys:acc) ds (tail ys:xss)
+                                else reverse acc : choose' [] ((d-fromIntegral (length xs)):ds) xss
+choose' _ _ [] = error "rselect: cannot choose from empty list.\nPerhaps you specified 'n' too large?"
+
+-- | convert indices to deltas
+diffs :: [Integer] -> [Integer]
+diffs xs = diffs' (0:xs)
+    where
+      diffs' (a:b:cs) = if b<=a then error ("diffs needs a monotonically increasing sequence"++show (take 10 (a:b:cs)))
+                        else (b-a) : diffs' (b:cs)
+      diffs' [_] = []
+
+-- | convert a list to the list of missing values
+invert :: Integer -> Integer -> [Integer] -> [Integer]
+invert n i (a:cs) = [i..a-1] ++ invert n (a+1) cs
+invert n i [] = [i..n]
+
+
+-- | pick m unique integers
+pick :: Integer -> [Integer] -> [Integer]
+pick = pick' empty
+    where pick' tmp 0 _ = elems tmp
+          pick' tmp m (i:is) = if member i tmp then pick' tmp m is
+                               else pick' (insert i tmp) (m-1) is
+
+\end{code}
diff --git a/src/Repeats.lhs b/src/Repeats.lhs
new file mode 100644
--- /dev/null
+++ b/src/Repeats.lhs
@@ -0,0 +1,100 @@
+
+Cluster all ESTs against the genome
+
+Construct the de-Bruijn graph for each cluster
+(i.e. collect the set of all k-words in each cluster)
+What about multiplicity?
+
+For each pair of clusters, calculate the intersection.
+Repeat words = U (Ci /\ Cj)  -- also keep count of each word.
+
+(Can we use a more sophisticated data structure?  I.e. keep track of
+longest common exact string?)
+
+Check each repeat word against: repbase, simple repeats, complexity
+
+Parameters: word length k, and file to read clusters from
+
+\begin{code}
+
+module Main where
+
+import qualified Data.Set as S
+import qualified Data.Map as M
+
+import qualified Data.ByteString.Lazy.Char8 as B
+import Bio.Sequence
+import Bio.Sequence.HashWord
+
+import Unigene
+
+import Control.Monad (when)
+import Data.List
+import Data.Maybe
+import System.Environment (getArgs)
+
+import Util
+
+-- type Word = Int
+
+main = do
+  args <- getArgs
+  when (length args < 2 || length args > 3)
+    (error "usage: reps k ugfile\n         or: reps k clusterfile sequencefile")
+  (k,rs,rl) <- initialize args
+  if (k>16)
+     then putStrLn (myshow k rl)
+     else putStrLn (myshow k rs)
+
+myshow k = unlines . map show1
+    where show1 (key,count) = ">" ++ show key ++ " " ++ show count ++ "\n" ++ B.unpack (k2n k key) ++ "\n"
+
+initialize args = do
+  let k = case reads (head args) of [(k',"")] -> k'
+		  		    _ -> error "k must be a positive integer"
+  case tail args of [csfile,sqsfile] -> do
+                      cs <- {- countIO 10 "clusters: " . -} return . filter (not.null) . map words . lines =<< readFile csfile
+                      sqs <- readFasta sqsfile
+                      let rs = repeats k $ clusters cs sqs :: [(Int,Int)]
+                          rl = repeats k $ clusters cs sqs :: [(Integer,Int)]
+                      return (k,rs,rl)
+                    [ugfile] -> do
+                      ugdata <- {- countIO 10 "clusters: " =<< -} ugRead ugfile
+                      let rs = repeats k ugdata :: [(Int,Int)]
+                          rl = repeats k ugdata :: [(Integer,Int)]
+                      return (k,rs,rl)
+
+-- build clusters from [[Label]] and [Seq Label sdata]
+clusters :: [[String]] -> [Sequence] -> [[Sequence]]
+clusters labels seqs = map (map mylookup) labels
+    where smap = M.fromListWith (error "duplicate sequences in input!") $
+                 map (\s -> (B.dropWhile (=='>') $ seqlabel s,s)) seqs
+          mylookup s = case (flip M.lookup $ smap) . B.pack $ s of
+                         Nothing -> error ("sequence '"++s++"' in the clustering is not found in data set")
+                         Just x  -> x
+
+-- extract words from clusters
+debruijn :: Integral w => Int -> [Sequence] -> S.Set w
+debruijn k = foldl1' S.union . map (S.fromList . keys k . seqdata)
+-- slightly faster than: "foldl' (flip S.insert) S.empty . concatMap (keys k)"
+
+keys k = map fst . hashes (rcontig k)
+
+-- calculate word counts
+freqs :: Integral w => [S.Set w] -> M.Map w Int
+freqs = foldl' union M.empty
+    where union :: Integral w => M.Map w Int -> S.Set w -> M.Map w Int
+          union a b = foldl' insert a $ S.toList b
+          insert a k = case M.lookup k a of
+                         Just x  -> let v' = x+1 in v' `seq` M.insert k v' a
+                         Nothing -> M.insert k 1 a
+
+-- inefficient?
+toMap :: Integral w => S.Set w -> M.Map w Int
+toMap = M.fromList . map (\x -> (x,1)) . S.toList
+
+-- given word length k, calculate repeats from clusters
+repeats :: Integral w => Int -> [[Sequence]] -> [(w,Int)]
+repeats k = filter ((>1).snd) . M.toList . freqs . map (debruijn k)
+
+\end{code}
diff --git a/src/Unigene.hs b/src/Unigene.hs
new file mode 100644
--- /dev/null
+++ b/src/Unigene.hs
@@ -0,0 +1,25 @@
+-- read a clustering on Unigene format
+-- each cluster separated by '^#.*$'
+
+module Unigene (ugRead) where
+
+import Control.Monad
+import System.IO
+import qualified Data.ByteString.Lazy.Char8 as B
+
+import Bio.Sequence (Sequence(..))
+import Bio.Sequence.Fasta (mkSeqs)
+import Data.List (groupBy)
+
+-- Isn't this lazy?
+ugRead :: FilePath -> IO [[Sequence]]
+ugRead f = do
+  s <- B.readFile f
+  let ls = dropWhile B.null $ B.lines $ s
+  when (B.head (head ls) /= '#') (hPutStrLn stderr ("Warning: '"++f++"' does not look like Unigene format"))
+  return (clusters $ ls)
+
+clusters :: [B.ByteString] -> [[Sequence]]
+clusters (l:ls) = let ls' = if B.head l == '#' then (l:ls) else (B.pack "# dummy UG hdr" :l:ls)
+                      ss = groupBy (const (('#' /=) . B.head)) $ filter (not . B.null) ls'
+                  in map (mkSeqs . tail) ss
diff --git a/src/Util.hs b/src/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/Util.hs
@@ -0,0 +1,12 @@
+module Util where
+
+import System.IO
+import System.IO.Unsafe
+
+countIO step msg xs = sequence $ map unsafeInterleaveIO $ ct 0 xs
+   where ct s xs = let (a,b) = splitAt (step-1) xs
+                       next  = s+step
+                       output c = hPutStr stderr ((take (length (show c)+2+length msg) $ repeat ' ')++'\r':msg++show c) >> hFlush stderr
+                   in if null b then map return a ++ []
+                      else map return a ++ [output next >> return (head b)]
+                           ++ ct next (tail b)
