diff --git a/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,16 @@
+### 1.0.0.3 (2023-01-11)
+
+Packaging and documentation improvement
+
+### 1.0.0.2 (2022-03-15)
+
+- Add support for base 4.16 (ghc 9.2)
+- Add support for hashable 1.4
+
+### 1.0.0.1 (2021-06-02)
+
+Minor documentation changes
+
+### 1.0 (2021-02-09)
+
+Initial release
diff --git a/invert.cabal b/invert.cabal
--- a/invert.cabal
+++ b/invert.cabal
@@ -1,31 +1,15 @@
 cabal-version: 3.0
 
 name: invert
-version: 1.0.0.2
+version: 1.0.0.3
 synopsis: Automatically generate a function’s inverse
 category: Functions
 
 description:
     This library deals with computing a function’s inverse.
-    This is, of course, not possible in general, so the
-    applicability of this library comes with some caveats:
-
-      * The function’s domain must be enumerable, and
-        preferably rather small. We provide a few suggestions
-        and utilities for how to enumerate the domain.
-      * The function’s codomain must belong to the @Eq@ class.
-        An @Ord@ or @Hashable@ instance is also nice, to
-        accommodate a data structure for efficient lookups.
-      * The functions for inverting injections, surjections,
-        and bijections require some care to use correctly,
-        because the library does not verify these properties.
-
-    The main purpose of this library is to provide documentation
-    and convenience. It does not contain a great quantity of code,
-    so a user hesitant to incur a dependency on the package might
-    well choose only to read and borrow its techniques.
-
-build-type: Simple
+    The function’s domain must be enumerable, and preferably
+    rather small. The function’s codomain must belong to the
+    @Eq@ class (even better, @Ord@ or @Hashable@).
 
 author:     Chris Martin
 maintainer: Chris Martin, Julie Moronuki
@@ -36,33 +20,40 @@
 license: Apache-2.0
 license-file: license.txt
 
+extra-source-files: *.md
+
 common base
     default-language: Haskell2010
     ghc-options: -Wall
     build-depends:
-        base ^>= 4.12 || ^>= 4.13 || ^>= 4.14 || ^>= 4.15 || ^>= 4.16
-      , containers ^>= 0.6
-      , hashable ^>= 1.2.7 || ^>= 1.3 || ^>= 1.4
-      , unordered-containers ^>= 0.2.9
-      , generic-deriving ^>= 1.14
-      , vector ^>= 0.12
+      , base ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17
+      , containers ^>= 0.6.4
+      , hashable ^>= 1.3.5 || ^>= 1.4
+      , unordered-containers ^>= 0.2.17
+      , generic-deriving ^>= 1.14.1
+      , vector ^>= 0.12.3 || ^>= 0.13
 
 library
     import: base
-    exposed-modules: Invert, Invert.Reexport
-    other-modules: Map, Vector
+    exposed-modules:
+        Invert
+        Invert.Reexport
+    other-modules:
+        Map
+        Vector
     hs-source-dirs: src
     default-extensions:
         NoImplicitPrelude
-      , NamedFieldPuns
-      , ExistentialQuantification
+        NamedFieldPuns
+        ExistentialQuantification
 
 test-suite billing-codes-example
     import: base
     type: exitcode-stdio-1.0
     main-is: billing-codes.hs
     hs-source-dirs: examples
-    build-depends: invert
+    build-depends:
+      , invert
 
 benchmark invert-benchmark
     import: base
@@ -71,5 +62,6 @@
     hs-source-dirs: benchmarks
     main-is: bench.hs
     ghc-options: -O2
-    build-depends: invert
-    build-depends: criterion ^>= 1.5
+    build-depends:
+      , invert
+      , criterion ^>= 1.6
diff --git a/readme.md b/readme.md
new file mode 100644
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,16 @@
+This library deals with computing a function’s inverse. This is, of course, not
+possible in general, so the applicability of this library comes with some caveats:
+
+  * The function’s domain must be enumerable, and preferably rather small. We
+    provide a few suggestions and utilities for how to enumerate the domain.
+
+  * The function’s codomain must belong to the `Eq` class. An `Ord` or `Hashable`
+    instance is also nice, to accommodate a data structure for efficient lookups.
+
+  * The functions for inverting injections, surjections, and bijections require
+    some care to use correctly, because the library does not verify these
+    properties.
+
+The main purpose of this library is to provide documentation and convenience.
+It does not contain a great quantity of code, so a user hesitant to incur a
+dependency on the package might well choose only to read and borrow its techniques.
diff --git a/src/Invert.hs b/src/Invert.hs
--- a/src/Invert.hs
+++ b/src/Invert.hs
@@ -2,28 +2,22 @@
 
 module Invert
   (
-    -- * Overview
-    -- $overview
-
-    -- * 1. Varieties of function
-    function, bijection, injection, surjection,
+    {- * Overview -} {- $overview -}
 
-    -- * 2. Inversion strategies
-    linearSearchLazy, linearSearchStrict, binarySearch, hashTable,
+    {- * 1. Varieties of function -}
+            function, bijection, injection, surjection,
 
-    -- * 3. Domain enumeration
-    enumBounded, genum,
+    {- * 2. Inversion strategies -} linearSearchLazy,
+            linearSearchStrict, binarySearch, hashTable,
 
-    -- * The Strategy type
-    Strategy,
-    -- $strategyCreation
-    strategyAll, strategyOneAndAll,
+    {- * 3. Domain enumeration -} enumBounded, genum,
 
-    -- * Re-exports
-    -- $reexports
-    module Invert.Reexport
+    {- * The Strategy type -} Strategy, {- $strategyCreation -}
+            strategyAll, strategyOneAndAll,
 
-  ) where
+    {- * Re-exports -} {- $reexports -} module Invert.Reexport,
+  )
+  where
 
 import Invert.Reexport
 
@@ -32,20 +26,20 @@
 
 import qualified Vector
 
-import Data.Eq            ( Eq, (==) )
-import Data.Foldable      ( foldl' )
-import Data.Function      ( (.) )
-import Data.List.NonEmpty ( NonEmpty, nonEmpty )
-import Data.Maybe         ( Maybe (Just, Nothing), fromMaybe, listToMaybe )
-import Data.Ord           ( Ord )
-import Data.Tuple         ( uncurry )
-import Prelude            ( error )
-import Prelude            ( Enum, enumFromTo )
-import Prelude            ( Bounded, minBound, maxBound )
+import Data.Eq (Eq, (==))
+import Data.Foldable (foldl')
+import Data.Function ((.))
+import Data.List.NonEmpty (NonEmpty, nonEmpty)
+import Data.Maybe (Maybe (Just, Nothing), fromMaybe, listToMaybe)
+import Data.Ord (Ord)
+import Data.Tuple (uncurry)
+import Prelude (error)
+import Prelude (Enum, enumFromTo)
+import Prelude (Bounded, minBound, maxBound)
 
-import qualified Data.List         as List  ( lookup, map )
-import qualified Data.Maybe        as List  ( mapMaybe )
-import qualified Generics.Deriving as GEnum ( genum )
+import qualified Data.List as List (lookup, map)
+import qualified Data.Maybe as List (mapMaybe)
+import qualified Generics.Deriving as GEnum (genum)
 
 {- $overview
 
@@ -94,7 +88,7 @@
     of tuples, one for each value of the domain.
 
   * 'linearSearchLazy' precomputes nothing at all.
-    It is possible to use this stategy when the domain is infinite.
+    It is possible to use this strategy when the domain is infinite.
 
 Our other two strategies, 'binarySearch' and 'hashTable',
 work by building data structures that allow more efficient lookups.
@@ -102,7 +96,7 @@
   * 'binarySearch' precomputes a binary search tree;
     the codomain must belong to the 'Ord' class.
 
-  * 'hashTable' precomputers a hash table;
+  * 'hashTable' precomputes a hash table;
     the codomain must belong to the 'Hashable' class.
 
 The 'Hashable' class comes from "Data.Hashable" in the @hashable@ package.
@@ -124,9 +118,7 @@
 The 'Generic' class comes from "GHC.Generics", and the 'GEnum' class
 comes from "Generics.Deriving" in the @generic-deriving@ package.
 Both classes are re-exported by "Invert", which you may find convenient
-if your primary motivation for deriving 'GEnum' is to invert a function.
-
--}
+if your primary motivation for deriving 'GEnum' is to invert a function. -}
 
 function ::
     Strategy a b
@@ -177,16 +169,11 @@
 surjection (Strategy _ s) as f = finagle . s (inverseEntries as f)
   where finagle = fromMaybe (error "Not a surjection!") . nonEmpty
 
-{- |
-
-    An inversion strategy is an approach for producing
-    the inverse of an @(a -> b)@ function.
-
-    All strategies produce the same results, but they
-    have operational differences that affect performance.
-
--}
+{-| An inversion strategy is an approach for producing
+    the inverse of an @(a -> b)@ function
 
+All strategies produce the same results, but they
+have operational differences that affect performance. -}
 data Strategy a b =
   Strategy
     ([(b, a)] -> b -> Maybe a)
@@ -194,13 +181,11 @@
 
 {- $strategyCreation
 
-    === Defining your own strategies
-
-    If you want to design your own strategy instead
-    of using one provided by this module, use either
-    'strategyAll' or 'strategyOneAndAll'.
+=== Defining your own strategies
 
--}
+If you want to design your own strategy instead
+of using one provided by this module, use either
+'strategyAll' or 'strategyOneAndAll'. -}
 
 strategyAll ::
     ([(b, a)] -> b -> [a]) -- ^ Find all matches
@@ -224,29 +209,20 @@
     f Map{ Map.empty, Map.singleton, Map.union, Map.lookup } =
         lookup . foldl' union empty . List.map (uncurry singleton)
 
-{- |
-
-    A function inversion strategy that precomputes nothing at all.
-    It is possible to use this stategy when the domain is infinite.
-
--}
+{-| A function inversion strategy that precomputes nothing at all
 
+It is possible to use this strategy when the domain is infinite. -}
 linearSearchLazy :: Eq b => Strategy a b
 linearSearchLazy = Strategy one all
   where
     one bas b = List.lookup b bas
     all bas b = List.mapMaybe (sndIfFstEq b) bas
 
-{- |
-
-    A function inversation strategy that works by precomputing a
-    strict sequence of tuples, one for each value of the domain.
-
-    For larger functions, it may be preferable to use 'binarySearch' or
-    'hashTable' instead to get a more efficient inverse.
-
--}
+{-| A function inversion strategy that works by precomputing a
+    strict sequence of tuples, one for each value of the domain
 
+For larger functions, it may be preferable to use 'binarySearch' or
+'hashTable' instead to get a more efficient inverse. -}
 linearSearchStrict :: Eq b => Strategy a b
 linearSearchStrict = strategyAll f
   where
@@ -257,59 +233,53 @@
 sndIfFstEq :: Eq b => b -> (b, a) -> Maybe a
 sndIfFstEq x (b, a) = if b == x then Just a else Nothing
 
-{- |
-
-    A function inversion strategy that works by precomputing
-    a binary search tree. The data structure imposes the
-    requirement that the codomain belongs to the 'Ord' class.
-
--}
+{-| A function inversion strategy that works by precomputing
+    a binary search tree
 
+The data structure imposes the requirement that the codomain
+belongs to the 'Ord' class. -}
 binarySearch :: Ord b => Strategy a b
 binarySearch = mapStrategy Map.ordSingleMap Map.ordMultiMap
 
-{- |
-
-    A function inversion strategy that works by precomputing
-    a hash table. The data structure imposes the requirement
-    that the codomain belongs to the 'Hashable' class.
-
--}
+{-| A function inversion strategy that works by precomputing
+    a hash table
 
+The data structure imposes the requirement that the codomain
+belongs to the 'Hashable' class. -}
 hashTable :: (Eq b, Hashable b) => Strategy a b
 hashTable = mapStrategy Map.hashSingleMap Map.hashMultiMap
 
--- |
--- 'enumBounded' can be a convenient way to enumerate
--- the domain for a function that you want to invert.
--- It uses two stock-derivable classes, 'Enum' and 'Bounded'.
---
--- To derive the required typeclass instances, add the
--- following deriving clause to the type’s definition:
---
---   > deriving (Enum, Bounded)
---
+{-| A convenient way to enumerate the domain for a function that you
+want to invert, using the stock-derivable classes 'Enum' and 'Bounded'
 
+To derive the required typeclass instances, add the following deriving clause to
+the type’s definition:
+
+@
+deriving (Enum, Bounded)
+@ -}
 enumBounded :: (Enum a, Bounded a) => [a]
 enumBounded = enumFromTo minBound maxBound
 
--- |
--- 'genum' uses GHC generics; it requires deriving 'Generic'
--- and 'GEnum'. The 'Generic' class comes from "GHC.Generics",
--- and the 'GEnum' class comes from "Generics.Deriving" in the
--- @generic-deriving@ package.
---
--- To derive the required typeclass instances, enable the
--- following language extensions:
---
---   > {-# language DeriveGeneric, DeriveAnyClass, DerivingStrategies #-}
---
--- Then add the following deriving clauses to the type’s definition:
---
---   > deriving stock Generic
---   > deriving anyclass GEnum
---
+{-| Use GHC generics to enumerate a function's domain
 
+This requires deriving 'Generic' and 'GEnum'. The 'Generic' class comes
+from "GHC.Generics", and the 'GEnum' class comes from "Generics.Deriving"
+in the @generic-deriving@ package.
+
+To derive the required typeclass instances, enable the following
+language extensions:
+
+@
+\{\-# language DeriveGeneric, DeriveAnyClass, DerivingStrategies #\-\}
+@
+
+Then add the following deriving clauses to the type’s definition:
+
+@
+deriving stock Generic
+deriving anyclass GEnum
+@ -}
 genum :: GEnum a => [a]
 genum = GEnum.genum
 
@@ -322,6 +292,5 @@
 List of re-exports:
 
   - __'Hashable'__ (for the 'hashTable' inversion strategy)
-  - __'Generic'__ and __'GEnum'__ (for the 'genum' domain enumeration approach)
-
--}
+  - __'Generic'__ and __'GEnum'__ (for the 'genum' domain
+    enumeration approach) -}
