diff --git a/biopsl.cabal b/biopsl.cabal
--- a/biopsl.cabal
+++ b/biopsl.cabal
@@ -1,5 +1,5 @@
 Name:                biopsl
-Version:             0.3
+Version:             0.4
 Synopsis:            Library and executables for working with PSL files
 Description:         The library contains the functionality for reading and writing
 		     PSL files (alignment data, e.g. from BLAT output). It duplicates
@@ -23,6 +23,12 @@
   Build-depends:   base >= 3 && < 5, biocore >= 0.1, bytestring
   Hs-Source-Dirs:  src
   Ghc-Options:     -Wall
+
+Executable pslstats
+  Main-Is:        PslStats.hs
+  Hs-Source-Dirs: src, examples
+  Build-depends:  unordered-containers
+  Ghc-Options:    -Wall
 
 Executable psluniq
   Main-Is:        PslUniq.hs
diff --git a/examples/PslStats.hs b/examples/PslStats.hs
new file mode 100644
--- /dev/null
+++ b/examples/PslStats.hs
@@ -0,0 +1,34 @@
+{-| Generate stats from PSL files, similar to 'samtools stats' for
+    BAM files.  Each target sequence is listed with its length and 
+    number of matching query sequences.
+-}
+
+import Bio.Alignment.PSL
+import System.Environment (getArgs)
+import Data.HashMap.Strict as H hiding (map)
+import qualified Data.ByteString.Lazy.Char8 as L
+import Data.List (foldl', intercalate)
+
+main :: IO ()
+main = do
+  fs <- getArgs
+  ps <- mapM readPSL fs
+  putStrLn ("target\tlenght\t"++intercalate "\t" fs)
+  putStr $ unlines $ format $ countTargets ps
+
+-- | map of lengths, and map of count per input file
+countTargets :: [[PSL]] -> [HashMap L.ByteString Int]
+countTargets = go [] empty
+  where go cs ls (ps:pss) = let (l,c) = count1 ls ps
+                            in go (c:cs) l pss
+        go cs ls [] = (ls:reverse cs)
+        count1 ls ps = Data.List.foldl' ins1 (ls,empty) ps
+        ins1 (l,c) p = l `seq` c `seq` (H.insert (tname p) (tsize p) l,H.insertWith (+) (tname p) 1 c)
+
+
+format :: [HashMap L.ByteString Int] -> [String]
+format (l:cs) = [unwords' $ (L.unpack s:show len:map (show . H.lookupDefault 0 s) cs) | (s,len) <- toList l]
+format _ = error "format"
+
+unwords' :: [String] -> String
+unwords' = intercalate "\t"
diff --git a/examples/PslUniq.hs b/examples/PslUniq.hs
--- a/examples/PslUniq.hs
+++ b/examples/PslUniq.hs
@@ -5,6 +5,7 @@
 import Bio.Alignment.PSL
 import System.Environment (getArgs)
 import qualified Data.ByteString.Lazy as L
+import Data.List (sortBy)
 
 main :: IO ()
 main = do
@@ -17,10 +18,16 @@
   
 uniq :: [PSL] -> [PSL]
 uniq [] = []
-uniq (p:ps) = go p ps
+uniq (p:ps) 
+  -- protein matches are sorted first by strand, thus we need to resort to sorting
+  | L.length (strand p) > 1 = let (q:qs) = sortOn qname (p:ps) in go q qs
+  | otherwise               = go p ps
   
 go :: PSL -> [PSL] -> [PSL]
 go p1 (q:qs) | qname q /= qname p1 = p1 : go q qs
              | match q > match p1  = go q qs
              | otherwise           = go p1 qs
 go p1 [] = [p1]
+
+sortOn :: Ord a1 => (a -> a1) -> [a] -> [a]
+sortOn f = sortBy (\x y -> compare (f x) (f y))
