diff --git a/LICENCE b/LICENCE
new file mode 100644
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,30 @@
+Copyright (c) 2008 Stephen Peter Tetley
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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/LICENCE-LGPL b/LICENCE-LGPL
new file mode 100644
--- /dev/null
+++ b/LICENCE-LGPL
@@ -0,0 +1,507 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 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.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations
+below.
+
+  When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it
+becomes a de-facto standard.  To achieve this, non-free programs must
+be allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, 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 library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+  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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+  If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at least
+    three years, to give the same user the materials specified in
+    Subsection 6a, above, for a charge no more than the cost of
+    performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be 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.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+  9. 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 Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+  11. 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 Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  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 library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; 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.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,4 @@
+#!/usr/bin/env runhaskell
+
+import Distribution.Simple
+main = defaultMain
diff --git a/precis.cabal b/precis.cabal
new file mode 100644
--- /dev/null
+++ b/precis.cabal
@@ -0,0 +1,62 @@
+name:             precis
+version:          0.3.1
+license:          OtherLicense
+license-file:     LICENCE
+copyright:        Stephen Tetley <stephen.tetley@gmail.com>
+maintainer:       Stephen Tetley <stephen.tetley@gmail.com>
+homepage:         http://code.google.com/p/copperbox/
+category:         Distribution
+synopsis:         Diff Cabal packages.
+description:
+  Summarize API differences between revisions of Cabal packages.
+  .
+  Note Precis library is BSD3, Precis executable is LGPL apropos 
+  the dependency on CppHs.
+  .
+
+
+build-type:         Simple
+stability:          unstable
+cabal-version:      >= 1.2
+extra-source-files:
+  LICENCE,
+  LICENCE-LGPL
+
+library
+  build-depends:  base               <  5,
+                  containers         <  0.5,
+                  Cabal              >= 1.8  && < 2.0,
+                  filepath           >= 1.1  && < 2.0,
+                  directory          >= 1.0  && < 2.0,
+                  haskell-src-exts   >= 1.8  && < 2.0,
+                  cpphs              >= 1.11 && < 2.0
+                  
+  hs-source-dirs: src
+
+  exposed-modules: 
+    Precis.CabalPackage,
+    Precis.Datatypes,
+    Precis.HsSrcUtils,
+    Precis.ModuleProperties,
+    Precis.PathUtils,
+    Precis.PPShowS,
+    Precis.Properties,
+    Precis.Utils
+  
+executable precis
+  main-is: Main.hs
+
+  hs-source-dirs: src
+    
+  other-modules:   
+    CPP,
+    Precis.CabalPackage,
+    Precis.Datatypes,
+    Precis.HsSrcUtils,
+    Precis.ModuleProperties,
+    Precis.PathUtils,
+    Precis.PPShowS,
+    Precis.Properties,
+    Precis.Utils
+
+
diff --git a/src/CPP.hs b/src/CPP.hs
new file mode 100644
--- /dev/null
+++ b/src/CPP.hs
@@ -0,0 +1,44 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  CPP
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  LGPL (depends on CppHs)
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+-- Preprocess with HSCPP
+--
+--------------------------------------------------------------------------------
+
+
+module CPP 
+  (
+    preprocessFile
+  , precisCpphsOptions
+  ) where
+
+import Precis.Datatypes
+
+import Language.Preprocessor.Cpphs
+
+
+preprocessFile :: CpphsOptions -> FilePath -> IO MacroExpandedSrcFile
+preprocessFile opts file_name = do
+  input  <- readFile file_name
+  output <- runCpphs opts file_name input
+  return $ MacroExpandedSrcFile file_name output
+
+
+precisCpphsOptions :: CpphsOptions
+precisCpphsOptions = updBoolOpts (\s -> s {hashline = False}) defaultCpphsOptions
+
+    
+updBoolOpts :: (BoolOptions -> BoolOptions) -> CpphsOptions -> CpphsOptions
+updBoolOpts f b = let opts = boolopts b in b { boolopts = f opts }
+
+
+
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,215 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Main
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  LGPL (depends on CppHs)
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+--
+--------------------------------------------------------------------------------
+
+module Main where
+
+import CPP
+import Precis.CabalPackage
+import Precis.Datatypes
+import Precis.HsSrcUtils
+import Precis.ModuleProperties
+import Precis.PPShowS
+import Precis.Properties
+
+import Language.Haskell.Exts ( Module )         -- package: haskell-src-exts
+
+import System.Environment
+import System.Exit
+import System.Console.GetOpt
+
+-- | REMEMBER TO CHANGE THIS!
+-- *****************************************************************************
+version_number :: String
+version_number = "0.3.0"
+
+header :: String
+header = "Usage: precis <new_cabal_file> <old_cabal_file>"
+
+help_message :: String
+help_message = unlines $  
+    [ "Summarize the API differences between two revisions of a" 
+    , "Cabal package."
+    ]
+
+data Flag = Usage
+          | Version
+  deriving (Eq, Show)
+
+options :: [OptDescr Flag]
+options =
+    [ Option ['h'] ["help"]     (NoArg Usage)        help_message
+    , Option ['v'] ["version"]  (NoArg Version)      "show version"
+    ]
+
+main :: IO ()
+main = do { args <- getArgs
+          ; let (opts, nonopts, errs) = getOpt Permute options args
+          ; main2 opts nonopts errs
+          }
+  
+main2 :: [Flag] -> [FilePath] -> [String] -> IO ()
+main2 opts _           _ 
+  | Usage       `elem` opts = precisExit $ usageInfo header options
+  | Version     `elem` opts = precisExit $ 
+                                "precis version " ++ version_number
+
+main2 _    [newc,oldc] []   = runCompare newc oldc
+main2 _    _           errs = 
+    precisExitFail 1 (concat errs ++ usageInfo header options)
+
+precisExit :: String -> IO ()
+precisExit s = putStrLn s >> exitWith ExitSuccess
+
+precisExitFail :: Int -> String -> IO ()
+precisExitFail i s = putStrLn s >> exitWith (ExitFailure i)
+
+
+runCompare :: FilePath -> FilePath -> IO ()
+runCompare new_cabal_file old_cabal_file = do 
+    ans1 <- extractPrecis new_cabal_file known_extensions
+    ans2 <- extractPrecis old_cabal_file known_extensions
+
+    case (ans1,ans2) of
+      (Right new_cp, Right old_cp) -> sk new_cp old_cp
+      (Left err, _)                -> precisExitFail 2 $ cabalFileErrorMsg err
+      (_, Left err)                -> precisExitFail 2 $ cabalFileErrorMsg err
+  where
+   sk new_cp old_cp = do 
+      { printPackageNameAndVersions new_cp old_cp
+      ; printModuleCountSummary new_cp old_cp
+      ; compareExposedModules (exposedModulesProp new_cp) 
+                              (exposedModulesProp old_cp)
+      }
+
+
+printPackageNameAndVersions :: CabalPrecis -> CabalPrecis -> IO ()
+printPackageNameAndVersions new_cp old_cp = putShowSLine $ vsep
+    [ repeatChar 50 '-'
+    , packageNames    (package_name new_cp)    (package_name old_cp) 
+    , repeatChar 50 '-'
+    , packageVersions (package_version new_cp) (package_version old_cp)
+    , newline
+    ]
+  where
+    packageNames a b | a == b    = text a
+                     | otherwise = text a <+> text "*** Warning: comparing to " 
+                                          <+> text b
+    packageVersions a b = text "Version:" <+> text a 
+                       <+> text "compared to Version:" 
+                       <+> text b
+
+printModuleCountSummary :: CabalPrecis -> CabalPrecis -> IO ()
+printModuleCountSummary new_cp old_cp = putShowSLine $ vsep
+    [ text "Exposed modules:"
+    , summarizeAddedRemoved "file" "files" id expos
+    , text "Internal modules:"
+    , summarizeAddedRemoved "file" "files" id privs
+    , newline
+    ]
+  where
+    new_pm        = packageModulesProp new_cp
+    old_pm        = packageModulesProp old_cp
+    (expos,privs) = diffPackageModulesProps new_pm old_pm
+                                  
+
+compareExposedModules :: ExposedModulesProp -> ExposedModulesProp -> IO ()
+compareExposedModules new_expos old_expos = 
+   mapM_ compareSrcFileEdit $ diffExposedModulesProps new_expos old_expos  
+
+compareSrcFileEdit :: Edit SourceFile -> IO ()
+compareSrcFileEdit (Conflict a b) = compareSourceFiles a b
+compareSrcFileEdit _              = return ()
+
+
+compareSourceFiles :: SourceFile -> SourceFile -> IO ()
+compareSourceFiles new_sf old_sf = do 
+  putShowSLine $ newline <> text (module_name new_sf) 
+  new_ans <- fullParseModule new_sf
+  old_ans <- fullParseModule old_sf
+  case (new_ans, old_ans) of 
+    (Right new_modu, Right old_modu) -> compareModules new_modu old_modu
+    (Left err,_)                     -> putStrLn $ moduleParseErrorMsg err
+    (_, Left err)                    -> putStrLn $ moduleParseErrorMsg err
+
+
+compareModules :: Module -> Module -> IO ()
+compareModules new_modu old_modu =
+    compareExports   new_modu old_modu  >>
+    compareDataDecls new_modu old_modu  >>
+    compareTypeSigs  new_modu old_modu  >>
+    compareInstances new_modu old_modu          
+
+
+compareExports :: Module -> Module -> IO ()
+compareExports new_modu old_modu = putShowSLine $ vsep
+    [ text "Explicit exports:"
+    , summarizeAddedConflictRemoved "export" "exports" txt expos
+    ]
+  where
+    new_expos = exportsProp new_modu
+    old_expos = exportsProp old_modu
+    expos     = diffExportsProps new_expos old_expos
+
+    txt (ModuleExport s)   = s
+    txt (DataOrClass  _ r) = r
+    txt (Variable     s)   = s 
+
+compareInstances :: Module -> Module -> IO ()
+compareInstances new_modu old_modu = putShowSLine $ vsep
+    [ text "Instances:"
+    , summarizeAddedConflictRemoved "instance" "instances" txt expos
+    ]
+  where
+    new_insts = instancesProp new_modu
+    old_insts = instancesProp old_modu
+    expos     = diffInstancesProps new_insts old_insts
+
+    txt (InstanceDecl _ _ r)   = r
+
+
+compareDataDecls :: Module -> Module -> IO ()
+compareDataDecls new_modu old_modu = putShowSLine $ vsep
+    [ text "Exported data type decls:"
+    , summarizeAddedConflictRemoved "data type" "data types" datatype_rep ddecls
+    ]
+  where
+    new_ddecls = dataDeclsProp new_modu
+    old_ddecls = dataDeclsProp old_modu
+    ddecls     = diffDataDeclsProps new_ddecls old_ddecls
+
+
+compareTypeSigs :: Module -> Module -> IO ()
+compareTypeSigs new_modu old_modu = putShowSLine $ vsep
+    [ text "Exported type sigs:"
+    , summarizeAddedConflictRemoved "type signature" 
+                                    "type signatures" ppty tysigs
+    ]
+  where
+    new_tysigs = typeSigsProp new_modu
+    old_tysigs = typeSigsProp old_modu
+    tysigs     = diffTypeSigsProps new_tysigs old_tysigs
+
+    ppty (TypeSigDecl n r) = n ++ " :: " ++ r
+
+-- | macro-expand and parse
+--
+fullParseModule :: SourceFile -> IO (Either ModuleParseError Module)
+fullParseModule (UnresolvedFile name) = 
+    return $ Left $ ERR_MODULE_FILE_MISSING name
+fullParseModule (SourceFile _ file_name) = do
+    mx_src <- preprocessFile precisCpphsOptions file_name
+    return $ readModule mx_src
+
+
diff --git a/src/Precis/CabalPackage.hs b/src/Precis/CabalPackage.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/CabalPackage.hs
@@ -0,0 +1,181 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.CabalPackage
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.CabalPackage 
+  ( 
+    Extension
+  , extractPrecis
+  , known_extensions
+
+  ) where
+
+import Precis.Datatypes
+import Precis.PathUtils
+import Precis.Utils
+
+import Distribution.ModuleName
+import Distribution.Package
+import Distribution.PackageDescription
+import Distribution.PackageDescription.Parse
+import Distribution.Verbosity
+import Distribution.Version
+
+import Control.Monad
+import Data.List ( intersperse, nub )
+import System.Directory
+import System.FilePath
+
+type Extension = String
+
+
+extractPrecis :: FilePath -> [Extension] -> IO (Either CabalFileError CabalPrecis)
+extractPrecis cabal_file exts = do
+    exists <- doesFileExist cabal_file
+    if exists then extractP cabal_file exts `onSuccessM` post
+              else return $ Left $ ERR_CABAL_FILE_MISSING cabal_file
+  where
+    post = return . nubSourceFiles
+
+
+known_extensions :: [Extension]
+known_extensions = ["hs", "lhs"]
+
+extractP :: FilePath -> [String] -> IO (Either CabalFileError CabalPrecis)
+extractP cabal_file_path exts =
+    safeReadPackageDescription normal cabal_file_path `onSuccessM` sk
+  where
+    root_to_cabal = dropFileName cabal_file_path  
+    sk gen_pkg = do { (expos,privs) <- getSourceFiles gen_pkg root_to_cabal exts
+                    ; return $ CabalPrecis 
+                                 { package_name          = getName gen_pkg
+                                 , package_version       = getVersion gen_pkg
+                                 , path_to_cabal_file    = cabal_file_path
+                                 , exposed_modules       = expos
+                                 , internal_modules      = privs
+                                 }
+                    }
+
+type SafeGPD = Either CabalFileError GenericPackageDescription
+
+safeReadPackageDescription :: Verbosity -> FilePath -> IO SafeGPD
+safeReadPackageDescription verbo path = 
+  catch (liftM Right $ readPackageDescription verbo path)
+        (\e -> return $ Left $ ERR_CABAL_FILE_PARSE $ show e)
+
+--------------------------------------------------------------------------------
+-- Extract from Package description
+
+getName    :: GenericPackageDescription -> String
+getName    = extrNameText    . package . packageDescription 
+
+getVersion :: GenericPackageDescription -> String
+getVersion = extrVersionText . package . packageDescription 
+                                  
+
+extrNameText :: PackageIdentifier  -> String
+extrNameText = fn . pkgName
+  where fn (PackageName str) = str 
+
+extrVersionText :: PackageIdentifier -> String
+extrVersionText = fn . versionBranch . pkgVersion
+  where fn = concat . intersperse "." . map show
+
+
+
+-- extract source files
+
+getSourceFiles :: GenericPackageDescription 
+           -> FilePath
+           -> [String] 
+           -> IO ([SourceFile], [SourceFile])
+getSourceFiles pkg_desc root exts = do 
+    lib_mods <- mapM (resolveLibrary root exts)    $ allLibraries   pkg_desc
+    exe_mods <- mapM (resolveExecutable root exts) $ allExecutables pkg_desc
+    let (lib_expos, lib_privs) = foldr fn ([],[]) lib_mods 
+    return (lib_expos, lib_privs ++ concat exe_mods)
+  where 
+    fn (a,b) (xs,ys) = (a++xs,b++ys)                                 
+
+
+allLibraries :: GenericPackageDescription -> [Library]
+allLibraries = maybe [] fn . condLibrary 
+  where
+   fn :: CondTree ConfVar [Dependency] Library -> [Library]
+   fn = ctfold (:) []
+
+ 
+allExecutables :: GenericPackageDescription -> [Executable]
+allExecutables = concat . map (ctfold (:) [] . snd) . condExecutables
+
+
+resolveLibrary :: FilePath -> [String] -> Library -> IO ([SourceFile], [SourceFile])
+resolveLibrary root exts lib = liftM2 (,) (fn expos) (fn others)
+  where
+    fn mods                    = resolveFiles root src_paths mods exts
+    (src_paths, expos, others) = libraryContents lib  
+
+libraryContents :: Library -> ([FilePath], [ModuleName], [ModuleName])
+libraryContents lib = (src_paths, expo_modules, other_modules)
+  where
+    src_paths       = hsSourceDirs   $ libBuildInfo lib
+    expo_modules    = exposedModules lib
+    other_modules   = otherModules   $ libBuildInfo lib
+
+resolveExecutable :: FilePath -> [String] -> Executable -> IO [SourceFile]
+resolveExecutable root exts exe = resolveFiles root src_paths mods exts
+  where
+    (src_paths, mods) = executableModules exe
+
+
+executableContents :: Executable -> ([FilePath], FilePath, [ModuleName])
+executableContents exe = (src_paths, exe_main_module, other_modules)
+  where
+    src_paths       = hsSourceDirs   $ buildInfo exe
+    exe_main_module = modulePath exe
+    other_modules   = otherModules   $ buildInfo exe
+
+
+-- All executable modules considered internal...
+
+executableModules :: Executable -> ([FilePath], [ModuleName])
+executableModules = fn . executableContents 
+  where
+    fn (as,exe,cs) = (as, exeModuleName exe : cs)
+
+--------------------------------------------------------------------------------
+-- reconcile modules
+
+-- Initially a precis may have dublicates (via Cabal\'s 
+-- conditional mechanism).
+--
+nubSourceFiles :: CabalPrecis -> CabalPrecis
+nubSourceFiles cp@(CabalPrecis _ _ _ exs ins) = 
+    cp { exposed_modules = exs', internal_modules = ins' }
+  where
+    exs'  = nub exs
+    ins'  = filter (not . (`elem` exs')) $ nub ins 
+
+--------------------------------------------------------------------------------
+-- General helper
+
+ctfold :: (a -> b -> b) -> b -> (CondTree v c a) -> b
+ctfold op initial node = foldr compfold x (condTreeComponents node)
+  where
+    x                           = condTreeData node `op` initial
+    compfold (_,t1, Nothing) b  = ctfold op b t1
+    compfold (_,t1, Just t2) b  = ctfold op (ctfold op b t1) t2
+                         
+
diff --git a/src/Precis/Datatypes.hs b/src/Precis/Datatypes.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/Datatypes.hs
@@ -0,0 +1,163 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.Datatypes
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.Datatypes
+  (
+    StrName
+  , TextRep
+  , CabalFileError(..)
+  , cabalFileErrorMsg
+
+  , CabalPrecis(..)
+  , SourceFile(..)
+  , sourceFile 
+  , sourceFileName
+
+  , MacroExpandedSrcFile(..)
+  , ModuleParseError(..)
+  , moduleParseErrorMsg
+
+  , ExportItem(..)
+  , exportItemName
+
+  , InstanceDecl(..)
+  , instanceDeclName
+
+  , DatatypeDecl(..)
+  , datatypeDeclName
+
+  , TypeSigDecl(..)
+  , typeSigDeclName
+
+  ) where
+
+
+import System.FilePath
+
+
+-- Don\'t use the Name type from haskell-src-exts.
+-- Precis doesn\'t need its distinction between identifiers
+-- and symbols.
+--
+
+type StrName = String
+type TextRep = String
+
+data CabalFileError = ERR_CABAL_FILE_MISSING FilePath
+                    | ERR_CABAL_FILE_PARSE   String
+  deriving (Eq,Show)
+
+
+cabalFileErrorMsg :: CabalFileError -> String
+cabalFileErrorMsg (ERR_CABAL_FILE_MISSING s) = "*** Error: missing file - " ++ s
+cabalFileErrorMsg (ERR_CABAL_FILE_PARSE   s) = "*** Error: parse error - " ++ s
+
+
+data CabalPrecis = CabalPrecis
+      { package_name            :: StrName
+      , package_version         :: String
+      , path_to_cabal_file      :: FilePath
+      , exposed_modules         :: [SourceFile]
+      , internal_modules        :: [SourceFile]
+      }
+  deriving (Eq,Show)
+
+-- 
+data SourceFile
+      = SourceFile     { module_name            :: StrName
+                       , full_path_to           :: FilePath 
+                       }
+      | UnresolvedFile { unresolved_file_name   :: StrName }  
+  deriving (Eq,Ord,Show)
+
+
+-- smart constructor
+
+sourceFile :: String -> FilePath -> SourceFile
+sourceFile name path = SourceFile name (normalise path)
+
+
+
+sourceFileName :: SourceFile -> StrName
+sourceFileName (SourceFile n _)   = n
+sourceFileName (UnresolvedFile n) = n 
+
+
+
+
+--------------------------------------------------------------------------------
+-- Precis for individual source files
+
+data MacroExpandedSrcFile = MacroExpandedSrcFile
+      { source_file_name    :: String
+      , expanded_source     :: String
+      }
+
+
+-- | Module file names are derived from the cabal file.
+-- So the name is tracked if it is missing...
+--
+data ModuleParseError = ERR_MODULE_FILE_MISSING String
+                      | ERR_MODULE_FILE_PARSE   String
+  deriving (Eq,Show)
+
+moduleParseErrorMsg :: ModuleParseError -> String
+moduleParseErrorMsg (ERR_MODULE_FILE_MISSING s) = 
+    "*** Error: missing file - " ++ s
+moduleParseErrorMsg (ERR_MODULE_FILE_PARSE   s) = 
+    "*** Error: parse error - " ++ s
+
+
+data ExportItem = ModuleExport StrName
+                | DataOrClass  StrName TextRep
+                | Variable     StrName 
+  deriving (Eq,Show)
+
+exportItemName :: ExportItem -> StrName
+exportItemName (ModuleExport s)   = s
+exportItemName (DataOrClass  s _) = s
+exportItemName (Variable     s)   = s 
+
+
+data InstanceDecl = InstanceDecl 
+       { class_name :: StrName
+       , type_rep   :: TextRep
+       , full_rep   :: TextRep
+       }
+  deriving (Eq,Show)
+
+instanceDeclName :: InstanceDecl -> StrName
+instanceDeclName = class_name
+
+
+data DatatypeDecl = DatatypeDecl
+      { datatype_name :: StrName
+      , datatype_rep  :: TextRep
+      }
+  deriving (Eq,Show)
+
+datatypeDeclName :: DatatypeDecl -> StrName
+datatypeDeclName = datatype_name
+
+
+data TypeSigDecl = TypeSigDecl
+      { type_decl_name  :: StrName
+      , type_signature  :: TextRep
+      }
+  deriving (Eq,Show)
+
+typeSigDeclName :: TypeSigDecl -> StrName
+typeSigDeclName = type_decl_name
diff --git a/src/Precis/HsSrcUtils.hs b/src/Precis/HsSrcUtils.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/HsSrcUtils.hs
@@ -0,0 +1,104 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.HsSrcUtils
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.HsSrcUtils
+  (
+
+    readModule 
+  , parseModuleWithExts
+  , extractQName
+  , extractCName
+  , extractModuleName
+  , extractName
+  , extractSpecialCon
+
+  , getModuleName
+
+  , namedDecls
+
+  , hsppList
+
+  ) where
+
+import Precis.Datatypes
+import Precis.PPShowS
+
+import Language.Haskell.Exts hiding ( name )      -- package: haskell-src-exts
+
+
+readModule :: MacroExpandedSrcFile -> Either ModuleParseError Module
+readModule (MacroExpandedSrcFile filename mx_src) = 
+    case parseModuleWithExts knownExtensions filename mx_src of
+      ParseFailed loc msg -> Left $ ERR_MODULE_FILE_PARSE $ mkMsg msg loc
+      ParseOk a           -> Right $ a
+  where
+    mkMsg msg loc        = toString $ text msg <+> ppLoc loc
+    ppLoc (SrcLoc _ l c) = parens $ 
+        (text "line:" <+> int l <+> text ", column:" <+> int c)
+
+parseModuleWithExts :: [Extension] -> FilePath -> String -> ParseResult Module
+parseModuleWithExts exts file_name txt = parseModuleWithMode pmode txt
+  where
+    pmode = defaultParseMode { extensions            = exts
+                             , parseFilename         = file_name
+                             , ignoreLinePragmas     = False      }
+
+
+extractQName :: QName -> String
+extractQName (Qual mname name)  = extractModuleName mname ++ extractName name
+extractQName (UnQual name)      = extractName name
+extractQName (Special spc)      = extractSpecialCon spc
+
+extractCName :: CName -> String
+extractCName (VarName name)     = extractName name
+extractCName (ConName name)     = extractName name
+
+extractModuleName :: ModuleName -> String 
+extractModuleName (ModuleName name) = name
+
+extractName :: Name -> String
+extractName (Ident name)  = name
+extractName (Symbol name) = name
+
+extractSpecialCon :: SpecialCon -> String
+extractSpecialCon = prettyPrint
+
+--------------------------------------------------------------------------------
+
+getModuleName :: Module -> ModuleName 
+getModuleName (Module _ mn _ _ _ _ _) = mn 
+
+
+--------------------------------------------------------------------------------
+
+--
+namedDecls :: Decl -> [(StrName,TextRep)]
+namedDecls t@(TypeDecl    _ n _ _)            = [(extractName n, prettyPrint t)]
+namedDecls t@(TypeFamDecl _ n _ _)            = [(extractName n, prettyPrint t)]
+namedDecls t@(DataDecl    _ _ _ n _ _ _)      = [(extractName n, prettyPrint t)]
+namedDecls t@(GDataDecl   _ _ _ n _ _ _ _)    = [(extractName n, prettyPrint t)]
+namedDecls t@(DataFamDecl _ _ n _ _)          = [(extractName n, prettyPrint t)]
+namedDecls t@(ClassDecl   _ _ n _ _ _)        = [(extractName n, prettyPrint t)]
+namedDecls t@(InstDecl    _ _ q _ _)          = [(extractQName q, prettyPrint t)]
+namedDecls t@(DerivDecl   _ _ q _)            = [(extractQName q, prettyPrint t)]
+namedDecls t@(TypeSig     _ ns _)             = map fn ns
+   where fn n = (extractName n, prettyPrint t)
+
+namedDecls _                                  = []
+
+
+hsppList :: Pretty a => [a] -> String
+hsppList = unwords . map prettyPrint
diff --git a/src/Precis/ModuleProperties.hs b/src/Precis/ModuleProperties.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/ModuleProperties.hs
@@ -0,0 +1,320 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.ModuleProperties
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+-- 
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.ModuleProperties
+  (
+   
+    PackageModulesProp
+  , packageModulesProp
+  , PackageModulesList(..)
+  , diffPackageModulesProps
+
+  , ExposedModulesProp
+  , exposedModulesProp
+  , ExposedModulesList
+  , diffExposedModulesProps
+
+  --
+  , ExportsProp
+  , exportsProp
+  , diffExportsProps
+
+  --
+  , InstancesProp
+  , instancesProp
+  , diffInstancesProps
+
+  -- 
+  , DataDeclsProp
+  , dataDeclsProp
+  , diffDataDeclsProps
+
+  , TypeSigsProp
+  , typeSigsProp
+  , diffTypeSigsProps
+
+  ) where
+
+import Precis.Datatypes
+import Precis.HsSrcUtils
+import Precis.Properties
+
+import Language.Haskell.Exts hiding ( name, op )    -- package: haskell-src-exts
+
+import Data.Maybe ( catMaybes )
+
+
+
+--------------------------------------------------------------------------------
+-- all modules (exposed and internal) in a cabal file
+
+type PackageModulesProp = Property PackageModulesList
+
+data PackageModulesList = PackageModulesList
+      { public_modules  :: [StrName]
+      , private_modules :: [StrName]
+      }
+  deriving (Eq,Show)
+
+makePackageModulesProp :: PackageModulesList -> PackageModulesProp
+makePackageModulesProp  = Property name descr 
+  where
+    name  = "Modules list"
+    descr = "Lists of exposed and internal modules in a package."
+
+packageModulesProp :: CabalPrecis -> PackageModulesProp
+packageModulesProp = makePackageModulesProp . packageModulesList
+
+packageModulesList :: CabalPrecis -> PackageModulesList
+packageModulesList cp = PackageModulesList expos privs
+  where
+    expos = map sourceFileName $ exposed_modules  cp
+    privs = map sourceFileName $ internal_modules cp
+
+
+
+diffPackageModulesProps :: PackageModulesProp 
+                        -> PackageModulesProp 
+                        -> ([Edit StrName],[Edit StrName])
+diffPackageModulesProps = diffProperty cmp
+  where
+    cmp :: PackageModulesList -> PackageModulesList 
+                              -> ([Edit StrName],[Edit StrName])
+    cmp (PackageModulesList expos privs) (PackageModulesList expos' privs') = 
+        (xs,ys)
+       where
+         xs = difference (==) (/=) expos expos'         
+         ys = difference (==) (/=) privs privs'
+
+--------------------------------------------------------------------------------
+-- exposed modules in cabal file
+
+type ExposedModulesProp = Property ExposedModulesList
+
+type ExposedModulesList = [SourceFile]
+
+makeExposedModulesProp :: ExposedModulesList -> ExposedModulesProp
+makeExposedModulesProp  = Property name descr 
+  where
+    name  = "Exposed modules"
+    descr = "Exposed modules (with paths) in a package."
+
+exposedModulesProp :: CabalPrecis -> ExposedModulesProp
+exposedModulesProp = makeExposedModulesProp . exposed_modules
+
+
+diffExposedModulesProps :: ExposedModulesProp 
+                        -> ExposedModulesProp 
+                        -> [Edit SourceFile]
+diffExposedModulesProps = diffProperty cmp
+  where
+    cmp :: ExposedModulesList -> ExposedModulesList -> [Edit SourceFile]
+    cmp es1 es2 = difference (lift2a (==)) (/=) es1 es2         
+        
+
+    lift2a :: (StrName -> StrName -> b) -> SourceFile -> SourceFile -> b
+    lift2a op s1 s2 = sourceFileName s1 `op` sourceFileName s2
+
+--------------------------------------------------------------------------------
+-- export lists
+
+
+type ExportsProp = Property ExportsList
+
+makeExportsProp :: ExportsList -> ExportsProp
+makeExportsProp  = Property name descr 
+  where
+    name  = "Module exports"
+    descr = "Explicit exports from a module (class instances not counted)."
+
+
+type ExportsList = [ExportItem]
+
+
+exportsProp :: Module -> ExportsProp
+exportsProp modu = makeExportsProp (exportsList modu)
+
+
+exportsList :: Module -> ExportsList
+exportsList (Module _ _ _ _ mb_expos _ _) = 
+    maybe [] (map makeExportItem)  mb_expos 
+
+
+makeExportItem :: ExportSpec -> ExportItem
+makeExportItem (EModuleContents name) = ModuleExport $ extractModuleName name 
+makeExportItem (EVar name)            = Variable $ extractQName name
+makeExportItem s@(EAbs name)          = 
+    DataOrClass (extractQName name) (prettyPrint s)
+makeExportItem s@(EThingAll name)     = 
+    DataOrClass (extractQName name) (prettyPrint s)
+makeExportItem s@(EThingWith name _)  = 
+    DataOrClass (extractQName name) (prettyPrint s)
+
+
+diffExportsProps :: ExportsProp -> ExportsProp -> [Edit ExportItem]
+diffExportsProps = diffProperty cmp
+  where
+    cmp :: ExportsList -> ExportsList -> [Edit ExportItem]
+    cmp es1 es2 = difference (lift2a (==)) (/=) es1 es2         
+        
+    lift2a :: (StrName -> StrName -> b) -> ExportItem -> ExportItem -> b
+    lift2a op s1 s2 = exportItemName s1 `op` exportItemName s2
+
+
+--------------------------------------------------------------------------------
+-- class instances
+
+type InstancesProp = Property InstancesList
+
+makeInstancesProp :: InstancesList -> InstancesProp
+makeInstancesProp  = Property name descr 
+  where
+    name  = "Instance declarations"
+    descr = "Instance declarations defined in the module."
+
+
+type InstancesList = [InstanceDecl]
+
+instancesProp :: Module -> InstancesProp
+instancesProp modu = makeInstancesProp (instancesList modu)
+
+instancesList :: Module -> InstancesList
+instancesList (Module _ _ _ _ _ _ ds) = catMaybes $ map makeInstanceDecl ds
+
+makeInstanceDecl :: Decl -> Maybe InstanceDecl
+makeInstanceDecl d@(InstDecl _ _ name typs _) = 
+    Just $ InstanceDecl (extractQName name) (hsppList typs) (prettyPrint d)
+makeInstanceDecl _                          = Nothing
+
+
+-- compare instances on class name and text rep of type
+--
+type InstanceKey = (StrName,TextRep)  
+
+instanceKey :: InstanceDecl -> InstanceKey
+instanceKey (InstanceDecl s k _) = (s,k)
+
+diffInstancesProps :: InstancesProp -> InstancesProp -> [Edit InstanceDecl]
+diffInstancesProps = diffProperty cmp
+  where
+    cmp :: InstancesList -> InstancesList -> [Edit InstanceDecl]
+    cmp xs1 xs2 = difference (lift2a (==)) (/=) xs1 xs2
+        
+    lift2a :: (InstanceKey -> InstanceKey -> b) 
+           -> InstanceDecl -> InstanceDecl -> b
+    lift2a op s1 s2 = instanceKey s1 `op` instanceKey s2
+
+
+
+--------------------------------------------------------------------------------
+-- exported data types (regular and GADTS)
+
+type DataDeclsProp = Property DataDeclsList
+
+makeDataDeclsProp :: DataDeclsList -> DataDeclsProp
+makeDataDeclsProp = Property name descr 
+  where
+    name  = "Exported data declarations"
+    descr = "Data declarations exported from exposed modules."
+
+type DataDeclsList = [DatatypeDecl]
+
+dataDeclsProp :: Module -> DataDeclsProp
+dataDeclsProp modu = makeDataDeclsProp (dataDeclsList modu)
+
+dataDeclsList :: Module -> DataDeclsList
+dataDeclsList (Module _ _ _ _ mb_expo _ ds) = filterDatatypes mb_expo all_datas
+  where  
+    all_datas = catMaybes $ map makeDatatypeDecl ds
+
+makeDatatypeDecl :: Decl -> Maybe DatatypeDecl
+makeDatatypeDecl d@(DataDecl _ _ _ name _ _ _)    = 
+    Just $ DatatypeDecl (extractName name) (prettyPrint d)
+makeDatatypeDecl d@(GDataDecl _ _ _ name _ _ _ _) = 
+    Just $ DatatypeDecl (extractName name) (prettyPrint d)
+makeDatatypeDecl _                          = Nothing
+
+filterDatatypes :: Maybe [ExportSpec] -> [DatatypeDecl] -> [DatatypeDecl]
+filterDatatypes Nothing      xs = xs
+filterDatatypes (Just expos) xs = filter fn xs
+  where
+    fn (DatatypeDecl n _)    = n `elem` expo_vars
+    expo_vars                = catMaybes $ map mkExpoT expos
+
+    mkExpoT (EAbs n)         = Just $ extractQName n
+    mkExpoT (EThingAll n)    = Just $ extractQName n
+    mkExpoT (EThingWith n _) = Just $ extractQName n
+    mkExpoT _                = Nothing
+
+
+diffDataDeclsProps :: DataDeclsProp -> DataDeclsProp -> [Edit DatatypeDecl]
+diffDataDeclsProps = diffProperty cmp
+  where
+    cmp :: DataDeclsList -> DataDeclsList -> [Edit DatatypeDecl]
+    cmp xs1 xs2 = difference (lift2a (==)) (/=) xs1 xs2
+        
+    lift2a :: (StrName -> StrName -> b) -> DatatypeDecl -> DatatypeDecl -> b
+    lift2a op s1 s2 = datatypeDeclName s1 `op` datatypeDeclName s2
+
+
+--------------------------------------------------------------------------------
+-- exported type sigs
+
+type TypeSigsProp = Property TypeSigsList
+
+makeTypeSigsProp :: TypeSigsList -> TypeSigsProp
+makeTypeSigsProp = Property name descr 
+  where
+    name  = "Exported type signatures"
+    descr = unwords [ "Type signatures of functions, constants... exported"
+                    , "from exposed modules."]
+
+type TypeSigsList = [TypeSigDecl]
+
+typeSigsProp :: Module -> TypeSigsProp
+typeSigsProp modu = makeTypeSigsProp (typeSigsList modu)
+
+typeSigsList :: Module -> TypeSigsList
+typeSigsList (Module _ _ _ _ mb_expo _ ds) = filterTypeSigs mb_expo all_typesigs
+  where  
+    all_typesigs = concat $ map makeTypeSigDecl ds
+
+makeTypeSigDecl :: Decl -> [TypeSigDecl]
+makeTypeSigDecl (TypeSig _ ns t)    = map fn ns
+  where 
+    fn n = TypeSigDecl (extractName n) (prettyPrint t)
+makeTypeSigDecl _                    = []
+
+filterTypeSigs :: Maybe [ExportSpec] -> [TypeSigDecl] -> [TypeSigDecl]
+filterTypeSigs Nothing      xs = xs
+filterTypeSigs (Just expos) xs = filter fn xs
+  where
+    fn (TypeSigDecl n _)     = n `elem` expo_vars
+    expo_vars                = catMaybes $ map mkExpoT expos
+
+    mkExpoT (EVar n)         = Just $ extractQName n
+    mkExpoT _                = Nothing
+
+
+diffTypeSigsProps :: TypeSigsProp -> TypeSigsProp -> [Edit TypeSigDecl]
+diffTypeSigsProps = diffProperty cmp
+  where
+    cmp :: TypeSigsList -> TypeSigsList -> [Edit TypeSigDecl]
+    cmp xs1 xs2 = difference (lift2a (==)) (/=) xs1 xs2
+        
+    lift2a :: (StrName -> StrName -> b) -> TypeSigDecl -> TypeSigDecl -> b
+    lift2a op s1 s2 = typeSigDeclName s1 `op` typeSigDeclName s2
diff --git a/src/Precis/PPShowS.hs b/src/Precis/PPShowS.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/PPShowS.hs
@@ -0,0 +1,246 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.HString
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+-- Pretty print combinators for ShowS
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.PPShowS
+  (
+
+    toString
+  , putShowS
+  , putShowSLine
+
+  , punctuate
+  , encloseSep
+  , list 
+  , tupled
+  , semiBrace
+
+  , hcat 
+  , hsep
+  , vsep
+  , (<>)
+  , (<+>)
+
+  , sep
+  , line
+
+  , squotes
+  , dquotes
+  , braces
+  , parens
+  , angles
+  , brackets
+
+  , lparen
+  , rparen
+  , langle
+  , rangle
+  , lbrace
+  , rbrace
+  , lbracket
+  , rbracket
+
+  , sglquote
+  , dblquote
+  , semi
+  , colon
+  , comma
+  , space
+  , dot
+  , equal
+  , backslash
+  , newline
+  , bar
+
+  , empty
+  , text 
+  , char
+  , int
+
+  , repeatChar
+  , prefixLines
+  , nextLine
+
+  ) where
+
+infixr 5 `line`
+infixr 6 <>,<+>
+
+toString :: ShowS -> String
+toString = ($ [])
+
+putShowS :: ShowS -> IO ()
+putShowS = putStr . toString
+
+putShowSLine :: ShowS -> IO ()
+putShowSLine = putStrLn . toString
+
+
+punctuate :: ShowS -> [ShowS] -> [ShowS]
+punctuate _ []      = []
+punctuate _ [x]     = [x]
+punctuate s (x:xs)  = (x . s) : punctuate s xs
+
+encloseSep :: ShowS -> ShowS -> ShowS -> [ShowS] -> ShowS
+encloseSep l r _ []  = l . r
+encloseSep l r _ [x] = l . x . r
+encloseSep l r s xs  = l . hcat (punctuate s xs) . r
+
+list            :: [ShowS] -> ShowS
+list            = encloseSep lbracket rbracket comma
+
+tupled          :: [ShowS] -> ShowS
+tupled          = encloseSep lparen   rparen   comma
+
+semiBrace       :: [ShowS] -> ShowS
+semiBrace       = encloseSep lbrace   rbrace   semi
+
+hcat            :: [ShowS] -> ShowS
+hcat            = fold (.)
+
+hsep            :: [ShowS] -> ShowS
+hsep            = fold sep
+
+vsep            :: [ShowS] -> ShowS
+vsep            = fold line
+
+
+(<>)            :: ShowS -> ShowS -> ShowS
+(<>)            = (.)
+
+(<+>)           :: ShowS -> ShowS -> ShowS
+(<+>) f g       = f <> space <> g
+
+
+fold :: (ShowS -> ShowS -> ShowS) -> [ShowS] -> ShowS
+fold _ []      = id
+fold f xs      = foldr1 f xs
+
+sep             :: ShowS -> ShowS -> ShowS
+x `sep`  y      = x . space . y  
+
+line            :: ShowS -> ShowS -> ShowS
+x `line` y      = x . newline . y
+
+squotes         :: ShowS -> ShowS
+squotes         = enclose sglquote sglquote
+
+dquotes         :: ShowS -> ShowS
+dquotes         = enclose dblquote dblquote
+
+braces          :: ShowS -> ShowS
+braces          = enclose lbrace rbrace
+
+parens          :: ShowS -> ShowS
+parens          = enclose lparen rparen
+
+angles          :: ShowS -> ShowS
+angles          = enclose langle rangle
+
+brackets        :: ShowS -> ShowS
+brackets        = enclose lbracket rbracket
+
+enclose         :: ShowS -> ShowS -> ShowS -> ShowS
+enclose l r x   = l . x . r
+
+lparen          :: ShowS
+lparen          = showChar '('
+
+rparen          :: ShowS
+rparen          = showChar ')'
+
+langle          :: ShowS
+langle          = showChar '<'
+
+rangle          :: ShowS
+rangle          = showChar '>'
+
+lbrace          :: ShowS
+lbrace          = showChar '{'
+
+rbrace          :: ShowS
+rbrace          = showChar '}'
+
+lbracket        :: ShowS
+lbracket        = showChar '['
+
+rbracket        :: ShowS
+rbracket        = showChar ']'     
+
+
+sglquote        :: ShowS
+sglquote        = showChar '\''
+
+
+dblquote        :: ShowS
+dblquote        = showChar '"'
+
+semi            :: ShowS
+semi            = showChar ';'
+
+colon           :: ShowS
+colon           = showChar ':'
+
+comma           :: ShowS
+comma           = showChar ','
+
+space           :: ShowS
+space           = showChar ' '
+
+dot             :: ShowS
+dot             = showChar '.'
+
+equal           :: ShowS
+equal           = showChar '='
+
+backslash       :: ShowS
+backslash       = showChar '\\'
+
+newline         :: ShowS
+newline         = showChar '\n'
+
+bar             :: ShowS
+bar             = showChar '|'
+
+
+empty           :: ShowS
+empty           = id
+
+text            :: String -> ShowS
+text            = showString
+
+char            :: Char -> ShowS
+char            = showChar
+
+int             :: Int -> ShowS
+int             = shows
+
+
+repeatChar      :: Int -> Char -> ShowS
+repeatChar i    = showString . replicate i
+
+
+prefixLines     :: ShowS -> String -> ShowS
+prefixLines pre xs = vsep $ map ((pre <>) . text) $ lines xs
+
+-- | Note - this function evaluates the second arg and uses (++) 
+-- via 'showString'.
+--
+nextLine :: ShowS -> ShowS -> ShowS
+nextLine f g = step $ toString g 
+  where
+    step str | null str = f
+             | otherwise = f `line` (showString str)
diff --git a/src/Precis/PathUtils.hs b/src/Precis/PathUtils.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/PathUtils.hs
@@ -0,0 +1,92 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.PathUtils
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.PathUtils
+  (
+    exeModuleName
+  , resolveFiles
+  , removePrefix
+  , resolveToCabalFileLoc
+  ) where
+
+import Precis.Datatypes
+
+import Distribution.ModuleName
+
+import Data.List ( intersperse )
+import Data.Monoid
+import System.Directory
+import System.FilePath
+
+
+-- should have \".hs\" or \".lhs\" extension
+exeModuleName :: FilePath -> ModuleName
+exeModuleName = fromString . dropExtension
+
+resolveFiles :: FilePath 
+             -> [FilePath] 
+             -> [ModuleName] 
+             -> [String]
+             -> IO [SourceFile]
+resolveFiles path_root src_dirs mod_names exts = 
+    let cp_paths = map fn $ longCrossProduct src_dirs mod_names in
+    mapM resolve cp_paths 
+  where
+    fn (path,modu) = (mname modu, moduleLongPath path_root path modu)
+
+    resolve (mod_name,path) = do { ans <- findByExtension path exts
+                                 ; case ans of
+                                     Nothing ->         
+                                         return $ UnresolvedFile $ mod_name
+                                     Just path' -> 
+                                         return $ sourceFile mod_name path'
+                                 }
+
+
+findByExtension :: FilePath -> [String] -> IO (Maybe FilePath)
+findByExtension _    []     = return Nothing
+findByExtension path (e:es) = let full = addExtension path e in 
+    doesFileExist full >>= \ans -> if ans then return (Just full) 
+                                          else findByExtension path es
+
+
+moduleLongPath :: FilePath -> FilePath -> ModuleName -> FilePath
+moduleLongPath root src_dir mod_name = 
+    joinPath $ splitPath root ++ splitPath src_dir ++ components mod_name 
+
+
+longCrossProduct :: Monoid a => [a] -> [b] -> [(a,b)]
+longCrossProduct [] ys = map (\b -> (mempty,b)) ys
+longCrossProduct xs ys = [(a,b) | a <- xs , b <- ys ]
+
+
+mname :: ModuleName -> String 
+mname = concat . intersperse "." . components
+
+--------------------------------------------------------------------------------
+
+removePrefix :: FilePath -> FilePath -> FilePath
+removePrefix pre path = joinPath $ step (fn pre) (fn path) 
+  where
+    fn                          = splitPath . normalise
+    step (x:xs) (y:ys) | x == y = step xs ys 
+    step _      ys              = ys
+
+--------------------------------------------------------------------------------
+
+resolveToCabalFileLoc :: FilePath -> FilePath -> FilePath
+resolveToCabalFileLoc cabal_file src_file = 
+    (dropFileName cabal_file) `combine` src_file 
diff --git a/src/Precis/Properties.hs b/src/Precis/Properties.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/Properties.hs
@@ -0,0 +1,163 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.Properties
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+-- 
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.Properties
+  (
+  -- * Property type
+    Property(..)
+  
+  -- * Edit type
+  , Edit(..)
+
+  -- * Edit operations
+  , difference
+  , diffProperty
+  , addedRemoved
+  , summarizeAddedRemoved
+  , summarizeConflictRemoved
+  , summarizeAddedConflictRemoved
+
+  ) where
+
+import Precis.PPShowS
+import Precis.Utils
+
+import Data.List ( find )
+
+
+data Property n = Property 
+      { property_name         :: String
+      , property_description  :: String 
+      , property_value        :: n
+      }
+  deriving (Eq,Ord,Show)
+
+
+--------------------------------------------------------------------------------
+
+data Edit a = Added a | Conflict a a | Same a | Removed a
+  deriving (Eq,Show)
+
+
+
+difference :: (a -> a -> Bool) -> (a -> a -> Bool) -> [a] -> [a] -> [Edit a]
+difference matches conflict as bs = toListH $ checkShort bs (checkLong as id)
+  where
+    checkLong []     f = f
+    checkLong (x:xs) f = case find (matches x) bs of
+        Just b | conflict x b -> checkLong xs (f `snocH` Conflict x b) 
+               | otherwise    -> checkLong xs (f `snocH` Same x)
+        Nothing               -> checkLong xs (f `snocH` Added x)
+
+    checkShort []     f = f
+    checkShort (y:ys) f = case find (matches y) as of
+        Just _                -> checkShort ys f   -- already found
+        Nothing               -> checkShort ys (f `snocH` Removed y)
+
+
+
+diffProperty :: (n -> n -> b) -> Property n -> Property n -> b
+diffProperty cmp (Property _ _ a) (Property _ _ b) = cmp a b
+
+
+addedRemoved :: [Edit a] -> ([a],[a])
+addedRemoved = foldr fn ([],[])
+  where
+    fn (Added a)   (as,rs) = (a:as,rs)
+    fn (Removed r) (as,rs) = (as,r:rs)
+    fn _           acc     = acc
+
+addedConflictRemoved :: [Edit a] -> ([a],[(a,a)],[a])
+addedConflictRemoved = foldr fn ([],[],[])
+  where
+    fn (Added a)      (as,cs,rs) = (a:as,cs,rs)
+    fn (Conflict a b) (as,cs,rs) = (as,(a,b):cs,rs)
+    fn (Removed r)    (as,cs,rs) = (as,cs,r:rs)
+    fn _              acc        = acc
+
+
+conflictRemoved :: [Edit a] -> ([(a,a)],[a])
+conflictRemoved = foldr fn ([],[])
+  where
+    fn (Conflict a b)   (cs,rs) = ((a,b):cs,rs)
+    fn (Removed  r)     (cs,rs) = (cs,r:rs)
+    fn _                acc     = acc
+
+
+summarizeAddedRemoved :: String -> String -> (a -> String) -> [Edit a] -> ShowS
+summarizeAddedRemoved single plural sf xs = 
+           added_msg <> comma <+> removed_msg 
+    `nextLine` vsep (map (addedLine sf)   as)
+    `nextLine` vsep (map (removedLine sf) rs)
+  where
+    (as,rs)       = addedRemoved xs
+    added_msg     = addedMsg    single plural (length as)
+    removed_msg   = removedMsg  single plural (length rs)
+
+
+summarizeConflictRemoved :: String -> String -> (a -> String) -> [Edit a] -> ShowS
+summarizeConflictRemoved single plural sf xs = 
+           conflict_msg <> comma <+> removed_msg 
+    `nextLine` vsep (map (conflictLine sf) cs)
+    `nextLine` vsep (map (removedLine sf)  rs)
+  where
+    (cs,rs)       = conflictRemoved xs
+    conflict_msg  = conflictMsg single plural (length cs)
+    removed_msg   = removedMsg  single plural (length rs)
+
+
+summarizeAddedConflictRemoved :: String 
+                              -> String 
+                              -> (a -> String) -> [Edit a] -> ShowS
+summarizeAddedConflictRemoved single plural sf xs = 
+           added_msg <> comma <+> conflict_msg <> comma <+> removed_msg 
+    `nextLine` vsep (map (addedLine sf)    as)
+    `nextLine` vsep (map (conflictLine sf) cs)
+    `nextLine` vsep (map (removedLine sf)  rs)
+  where
+    (as,cs,rs)    = addedConflictRemoved xs
+    added_msg     = addedMsg    single plural (length as)
+    conflict_msg  = conflictMsg single plural (length cs)
+    removed_msg   = removedMsg  single plural (length rs)
+
+
+-- 
+
+msgCount :: String -> String -> Int -> ShowS
+msgCount single _      1 = int 1 <+> text single
+msgCount _      plural n = int n <+> text plural
+
+
+addedMsg :: String -> String -> Int -> ShowS
+addedMsg single plural i = msgCount single plural i <+> text "added (+)"
+
+conflictMsg :: String -> String -> Int -> ShowS
+conflictMsg single plural i = msgCount single plural i <+> text "conflict (*)"
+
+removedMsg :: String -> String -> Int -> ShowS
+removedMsg single plural i = msgCount single plural i <+> text "removed (-)"
+
+
+addedLine :: (a -> String) -> a -> ShowS
+addedLine f a = char '+' <+> (text $ f a)
+
+conflictLine :: (a -> String) -> (a,a) -> ShowS
+conflictLine f (a,b) = prefixLines (text "< ") (f a) `line`
+                       prefixLines (text "> ") (f b) <> newline
+
+removedLine :: (a -> String) -> a -> ShowS
+removedLine f a      = char '-' <+> (text $ f a)
diff --git a/src/Precis/Utils.hs b/src/Precis/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Precis/Utils.hs
@@ -0,0 +1,56 @@
+{-# OPTIONS -Wall #-}
+
+--------------------------------------------------------------------------------
+-- |
+-- Module      :  Precis.Utils
+-- Copyright   :  (c) Stephen Tetley 2010
+-- License     :  BSD3
+--
+-- Maintainer  :  Stephen Tetley <stephen.tetley@gmail.com>
+-- Stability   :  highly unstable
+-- Portability :  to be determined.
+--
+-- Utils
+--
+--------------------------------------------------------------------------------
+
+
+module Precis.Utils
+  (
+
+    H 
+  , snocH
+  , toListH
+ 
+  , onSuccess
+  , onSuccessM
+
+  ) where
+
+
+import Control.Monad
+
+
+
+-- Hughes lists
+
+type H a = [a] -> [a]
+
+snocH :: H a -> a -> H a
+snocH f a = f . (a:)
+
+toListH :: H a -> [a]
+toListH = ($ [])
+
+  
+onSuccess :: Monad m => Either a b -> (b -> m c) -> m (Either a c)
+onSuccess (Left a)  _  = return (Left a)
+onSuccess (Right b) mf = liftM Right $ mf b 
+
+  
+onSuccessM :: Monad m => m (Either a b) -> (b -> m c) -> m (Either a c)
+onSuccessM ma msk = ma >>= step 
+  where
+    step (Left a)  = return (Left a)
+    step (Right b) = liftM Right $ msk b 
+
